【DDIA阅读】2.数据模型与查询语言

大多数应用程序都是一层层的叠加数据模型,每一层设计的关键是如何用于下一层数据模型的表示

例如:

  1. 程序需要表示用户信息,交易订单等数据结构
  2. 这些应用的数据结构可以使用json,xml等通用数据格式
  3. 数据库工程师决定用什么方式表示上述json,xml等数据
  4. 硬件工程师用电流或光脉冲来表示上一层的字节流

关系模型与文档模型

最著名的是SQL,数据组织为关系,被称为表,每一个关系都是一个无序元组,成为一行

关系数据库的给核心在于商业数据处理,例如事务处理(订票,购物交易)和批处理(打印流水,工资单)

NOSQL的诞生

NOSQL的意思是不仅仅是SQL

NOSQL有如下的优点:

  • 扩展性更好,支持大数据集和超高吞吐量
  • 普遍都是免费且开源的而不是商业数据库产品
  • 关系模型不能很好的支持一些特定的查询操作
  • 是一种更具同态和表达力的数据模型

现在关系模型已经可以和非关系数据存储混合使用

对象---关系不匹配

当对象和关系之间存在一对多的关系时,有如下的方案:

  • 在传统SQL中可以将user放在一张表,职位经历,教育经历等单独存在其他表,通过外键引用,如下图
image.png
  • 新的SQL标准增加了对结构化数据类型和XML数据的支持,可以将多值数据存在单行内
  • 将工作经历,教育经历等合并成一个大文档,当作一个文本字段列存储,缺点是通常不能查询该列中的值,因为合并成了一个大文档没法查询。市场上有一些面向文档的数据库,例如MongoDB,RethinkDB,CouchDB等,存储为一个文档的好处就是只需要查询一次,不需要连接查询多次

下面的例子将简历表示为一个文档

{
  "user_id": 251,
  "first_name": "Bill",
  "last_name": "Gates",
  "summary": "Co-chair of the Bill & Melinda Gates... Active blogger.",
  "region_id": "us:91",
  "industry_id": 131,
  "photo_url": "/p/7/000/253/05b/308dd6e.jpg",
  "positions": [
    {
      "job_title": "Co-chair",
      "organization": "Bill & Melinda Gates Foundation"
    },
    {
      "job_title": "Co-founder, Chairman",
      "organization": "Microsoft"
    }
  ],
  "education": [
    {
      "school_name": "Harvard University",
      "start": 1973,
      "end": 1975
    },
    {
      "school_name": "Lakeside School, Seattle",
      "start": null,
      "end": null
    }
  ],
  "contact_info": {
    "blog": "http://thegatesnotes.com",
    "twitter": "http://twitter.com/BillGates"
  }
}

多对一和多对多的关系

多对一的例子有很多人生活在一个城市,在一个工厂上班

在存储一些地区或是工厂时可以存储一个id,而不是直接存储名字字符串。使用id的好处是,因为id对于用户没有意义,只对数据库有意义,所以不需要直接改变,即使对应的内容变化了,不需要逐条记录的改变,只需要改变id对应的映射内容

多对多指的是一个求职者可以有很多推荐人,一个推荐人可以推荐多个求职者

image.png

文档数据库到底合理吗

用json来表示数据的文档数据库和最初的层次结构相似,层次结构将数据表示为一个有层级的树,支持一对多关系,但是支持多对多困难,并且不支持连接,因为所有的东西都放一起了,所以程序员需要多次查询,手动解析数据间的关系

为了解决这些问题出现了关系模型和网络模型,网络模型最终被淘汰

网络模型是层次模型的推广,层次模型一个节点只能有一个父节点,而网络模型可以有多个,网络模型用指针来表示不同记录间的关系而不是用外键。访问记录需要从根记录开始,沿着指针逐步访问,就像在一个n维空间进行遍历,查询和更新的性能很差

关系模型使用外键来充当访问路径,每次选择不同的外键就是在选择不同的路径,区别在于底层的细节都有查询优化器帮你做了,不需要程序员手动构建访问路径

关系数据库和文档数据的比较

这里主要关注数据模型的差异

文档数据的优点主要是由于局部性,可以查一次就拿到所有的数据,对于某一些应用更接近于应用定义的数据结构

关系数据库优点是连接操作,对多对一和多对多的支持更好

那种模式的应用代码更简单?

如果一次就要使用整个数据树,那么文档模式更简单,如果使用多对多关系,那么使用关系模式更好。对于高度关联的数据,也是关系模式,但是最自然的是使用图模型

文档模型的模式灵活性

文档数据库被称为无模式或者读时模式,读时模式是指应用程序在写入数据库时不做任何的检查,用户可以写入任何样子的数据,例如你可以在json或者xml中包含了不同的字段

关系数据库被称为写时模式,写时模式是指应用程序再写入数据库时会做检查,例如SQL会强制要求你按照定义的表的规范写入

如果一个数据对象集合中的每一个项都有着不同的结构,或是它的结构会动态变化,那么这种时候固定的模式反而是不好的,无模式的文档显得更加的自然

数据查询语言

SQL是一种声明式的语言,只需要指定数据模式,结构需要满足什么条件等,不需要提供具体的实现,查询优化器会决定如何实现,此外它对外隐藏了具体的实现细节,并且不依赖于顺序,适合并行执行

另一类的查询语言叫做命令式,命令式的查询需要指明计算机需要如何操作,例如以下的查询:

function getSharks(){
    var sharks = []
    for(var i = 0; i < animals.len; i++){
        if animals[i].family == "Sharks"{
            sharks.push(animals[i])
        }
    }
    return sharks;
}

所以对于直观的感受,声明式和命令式的区别就是:声明式需要说清楚我想干什么,但是具体怎么做不需要你自己当心,而命令式不需要你直接说你想干什么,你只需要告诉计算机怎么做就可以了

还有一个例子可以非常直观的感受声明式和命令式的差别,假设有个海洋动物的网站,用户在查看鲨鱼的界面,想把用户当前浏览的地方高亮显式

<ul>
    <li class="selected">
        <p>Sharks</p>
        <ul>
            <li>Great White Shark</li>
            <li>Tiger Shark</li>
            <li>Hammerhead Shark</li>
        </ul>
    </li>
    <li><p>Whales</p>
        <ul>
            <li>Blue Whale</li>
            <li>Humpback Whale</li>
            <li>Fin Whale</li>
        </ul>
    </li>
</ul>

如果是声明式,也就是我们熟悉的CSS,只需要说清楚想把selected的li标签下的p变成蓝色背景,具体实现交给浏览器,代码就很简单

li.selected > p {
  background-color: blue;
}

但是如果是命令式,那就麻烦了,例如想要通过js的DOM来实现:

var liElements = document.getElementsByTagName("li");
for (var i = 0; i < liElements.length; i++) {
    if (liElements[i].className === "selected") {
        var children = liElements[i].childNodes;
        for (var j = 0; j < children.length; j++) {
            var child = children[j];
            if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "P") {
                child.setAttribute("style", "background-color: blue");
            }
        }
    }
}

图状数据模型

如果数据之间的关系大多都是多对多关系,那么使用图状数据模型会更加自然

一个图有两个元素:顶点

图状数据模型的优势是提供了单个数据存储区中保存完全不同类型对象的一致性方式

image.png

属性图模型

在一个属性图中,每个定点包括:

  • 唯一标识符
  • 出边集合
  • 入边集合
  • 属性集合

每个边包括:

  • 唯一标识符
  • 开始顶点
  • 结束顶点
  • 描述两个顶点间关系类型的标签
  • 属性集合

可以用两张表,一张存储点,一张存储 边。例如,Postgresql对图的实现如下:

CREATE TABLE vertices (
  vertex_id  INTEGER PRIMARY KEY,
  properties JSON
);

CREATE TABLE edges (
  edge_id     INTEGER PRIMARY KEY,
  tail_vertex INTEGER REFERENCES vertices (vertex_id),
  head_vertex INTEGER REFERENCES vertices (vertex_id),
  label       TEXT,
  properties  JSON
);

CREATE INDEX edges_tails ON edges (tail_vertex);
CREATE INDEX edges_heads ON edges (head_vertex);

三元存储模型

在三元存储模型中,信息以三元组(主体,谓语,客体)组成

三元组的主体相当于顶点,客体是以下两种情况之一:

  1. 原始数据类型的值,这样的情况下三元组的谓语和客体对应着主题的一个属性kv对,例如(lucy,age,33)
  2. 图中的另一个顶点,此时谓语是图中的边,例如(lucy, marriedTo, alain)

一个例子如下:

@prefix : <urn:example:>.
_:lucy     a       :Person.
_:lucy     :name   "Lucy".
_:lucy     :bornIn _:idaho.
_:idaho    a       :Location.
_:idaho    :name   "Idaho".
_:idaho    :type   "state".
_:idaho    :within _:usa.
_:usa      a       :Location
_:usa      :name   "United States"
_:usa      :type   "country".
_:usa      :within _:namerica.
_:namerica a       :Location
_:namerica :name   "North America"
_:namerica :type   :"continent"

因为_:lucy的主题写了很多次很麻烦,可以把它们写在一起:

@prefix : <urn:example:>.
_:lucy      a :Person;   :name "Lucy";          :bornIn _:idaho.
_:idaho     a :Location; :name "Idaho";         :type "state";   :within _:usa
_:usa       a :Loaction; :name "United States"; :type "country"; :within _:namerica.
_:namerica  a :Location; :name "North America"; :type "continent".

最后有一个问题:图数据模型和网络模型有区别吗

其实是有的区别如下:

  • 网络模型是有模式限制的,例如在一条记录上,第一个节点是Person类型,第二个节点一定是Location类型,而图模型可以是任何的类型
  • 网络模型获取一个记录的唯一方法是遍历,而图模型可以根据唯一id直接找到这个顶点

总结一下,第二章介绍了不同的数据模型已经对应的数据查询语言

最初的数据被表示为层次模型,但是不利于多对多,于是出现了关系模型,又因为一些应用不适合关系模型,出现了NoSQL,NoSQL主要分为两种:文档数据库主要用于数据之间的联系很少,图数据库则恰恰相反。事实上,一种模型可以用另一种模型实现,例如用两张表实现图数据模型。文档数据模型和图数据模型的共同点是都不会对数据施加某种模式,具体对数据的解释留给了应用程序,增加了灵活性,这其实是一种隐式的模式(读时处理),另外一种SQL则是显式的模式(写时强制)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容