Relations
下哉:https://www.sisuoit.com/2811.html
假定我们正在运营一个轿车修补的事务. 除了其他一些必要的工作, 我们还需求追寻一辆车的服务前史, 即在该辆车上全部的修整记载. 那么我们或许会创建包括以下一些列的 ServiceHistory 表:
VIN | Make | Model | Year | Color | Service Performed | Mechanic | Price | Date
这样, 每次当车辆修补往后, 我们就在表中添加新的一行, 并写入该次服务我们做了一些什么工作, 是哪位修补工, 花费多少和服务时间等.
但是等一下, 我们都知道,关于同一辆车而言,全部车辆本身信息有关的列是不变的。 也就是说,假定把我的 Black 2014 Lexus RX 350 修整 10 次的话, 那么即便 Make, Model, Year 和 Color 这些信息并不会改动,每一次依然重复记载了这些信息. 与无效的重复记载比较, 一个更合理的做法是对此类信息只存储一次, 并在有需求的时分进行查询。
那么该怎么做呢? 我们能够创建第二张表: Vehicle , 它有如下一些列:
VIN | Make | Model | Year | Color
这样一来, 关于 ServiceHistory 表, 我们能够精简为如下一些列:
VIN | Service Performed | Mechanic | Price | Date
你或许会问,为什么 VIN 会在两张表中一起呈现? 因为我们需求有一个办法来招认在 ServiceHistory 表的 这 辆车指的就是 Vehicle 表中的 那 辆车, 也就是需求招认两张表中的两条记载所标明的是同一辆车。 这样的话,我们仅需求为每辆车的本身信息存储一次即可. 每次当车辆过来修补的时分, 我们就在 ServiceHistory 表中创建新的一行, 而不用在 Vehicle 表中添加新的记载。 毕竟, 它们指的是同一辆车。
我们能够通过 SQL 查询句子来展开 Vehicle 与 ServiceHistory 两张表中包括的隐式联络:
SELECT Vehicle.Model, Vehicle.Year FROM Vehicle, ServiceHistory WHERE Vehicle.VIN = ServiceHistory.VIN AND ServiceHistory.Price > 75.00;
该查询旨在查找修补费用大于 $75.00 的全部车辆的 Model 和 Year. 注意到我们是通过匹配 Vehicle 与 ServiceHistory 表中的 VIN 值来挑选满足条件的记载. 回来的将是两张表中符合条件的一些记载, 而 "Vehicle.Model" 与 "Vehicle.Year" , 标明我们只想要 Vehicle 表中的这两列.
假定我们的数据库没有 索引 (indexes) (正确的应该是 indices), 上面的查询就需求履行 表扫描 (table scan) 来定位匹配查询要求的行。 table scan 是依照次第对表中的每一行进行顺次检查, 而这一般会十分的慢。 实际上, table scan 实际上是全部查询中最慢的。
能够通过对列加索引来避免扫描表。 我们能够把索引看做一种数据结构, 它能够通过预排序让我们在被索引的列上快速地找到一个指定的值 (或指定范围内的一些值). 也就是说, 假定我们在 Price 列上有一个索引, 那么就不需求一行一行地对整个表进行扫描来判别其价格是否大于 75.00, 而是只需求运用包括在索引中的信息 “跳” 到第一个价格高于 75.00 的那一行, 并回来随后的每一行(因为索引是有序的, 因此这些行的价格至少是 75.00)。
当应对许多的数据时, 索引是提高查询速度不可或缺的一个东西。当然, 跟全部的工作相同,有得必有失, 运用索引会导致一些额定的耗费: 索引的数据结构会耗费内存,而这些内存本可用于数据库中存储数据。这就需求我们权衡其好坏,寻求一个折中的办法, 但是为常常查询的列加索引是 十分 常见的做法。
The Clear Box
得益于数据库能够检查一张表的 schema (描绘了每列包括了什么类型的数据), 像索引这样的高级特性才能够实现, 并且能够根据数据做出一个合理的抉择计划。 也就是说, 关于一个数据库而言, 一张表其实是一个 “黑盒” (或者说透明的盒子) 的反义词?
当我们谈到 NoSQL 数据库的时分要牢牢记住这一点。 当触及 query 不同类型数据库引擎的才能时, 这也是其间十分重要的一部分。
Schemas
我们现已知道, 一张表的 schema , 描绘了列的姓名及其所包括数据的类型。它还包括了其他一些信息, 比如哪些列能够为空, 哪些列不允许有重复值, 以及其他对表中列的全部约束信息。 在任意时间一张表只能有一个 schema, 并且 表中的全部行有必要遵从 schema 的规则 。
这是一个十分重要的约束条件。 假定你有一张数据库的表, 里边有数以百万计的顾客信息。 你的出售团队想要添加额定的一些信息 (比如, 用户的年岁), 以期提高他们邮件营销算法的准确度。 这就需求来 alter (更改) 现有的表 -- 添加新的一列。 我们还需求挑选是否表中的每一行都要求该列有必要有一个值。 一般情况下, 让一个列有值是十分有道理的, 但是这么做的话或许会需求一些我们无法轻易取得的信息(比如数据库中每个用户的年岁)。因此在这个层面上,也需求有些权衡之策。
此外,对一个大型数据库做一些改动一般并不是一件小事。为了以防呈现错误,有一个回滚计划十分重要。但即就是如此,一旦当 schema 做出改动后,我们也并不总是能够撤消这些变化。 schema 的保护或许是 DBA 作业中最困难的部分之一。
Key/Value Stores
在 “NoSQL” 这个词存在前, 像 memcached 这样的 键/值 数据存储 (Key/Value Data Stores) 无须 table schema 也可供给数据存储的功用。 实际上, 在 K/V 存储时, 根本没有 "表 (table)" 的概念。 只需 键 (keys) 与 值 (values) . 假定键值存储听起来比较了解的话, 那或许是因为这个概念的构建准则与 Python 的 dict 与 set 相一致: 运用 hash table (哈希表) 来供给根据键的快速数据查询。 一个根据 Python 的最原始的 NoSQL 数据库, 简略来说就是一个大的字典 (dictionary) .
为了了解它的作业原理,亲自动手写一个吧! 首要来看一下一些简略的规划主意:
- 一个 Python 的 dict 作为首要的数据存储
- 仅支撑 string 类型作为键 (key)
- 支撑存储 integer, string 和 list
- 一个运用 ASCLL string 的简略 TCP/IP 服务器用来传递消息
- 一些像 INCREMENT, DELETE , APPEND 和 STATS 这样的高级指令 (command)
有一个根据 ASCII 的 TCP/IP 接口的数据存储有一个长处, 那就是我们运用简略的 telnet 程序即可与服务器进行交互, 并不需求特别的客户端 (尽管这是一个十分好的练习并且只需求 15 行代码即可完结)。