1.1. 数据库与其他东西相比,还有一个很重要的区别就在于,它们需要通过某种机制来确保数据一致,对于运行在多个节点上的数据库来说,这尤其重要
1.1.1. 一致性模型(consistency model)
1.2. 立即一致性
1.2.1. 立即一致性(immediate consistency)也叫强一致性(strong consistency),这种一致性模型能够确保所有的用户都会在同一时刻看到同样的数据,无论他们身处何方,用何种方式来查看数据,都可以保证这一点
1.2.2. 如果数据库采用立即一致性模型,那么出现的bug与需要排解的问题,就会比采用最终一致性模型的数据库少一些
1.2.3. 这种模型通常要比后者好,但是它会让数据库的性能受限,对于分布在多个节点上的数据库来说尤其如此
1.3. 最终一致性
1.3.1. 最终一致性(eventual consistency)是,如果某实体于更新之后不再发生变化,那么对该实体的各种读取操作,最终都会看到更新之后的那个值
1.3.2. DNS系统就是一种采用最终一致性模型的典型系统,它可能需要花几分钟乃至几个小时,才能把某条已经修改的DNS记录传播到世界上的所有DNS服务器上
1.3.2.1. 对于尚未收到更新的那些DNS服务器来说,查询操作返回的还是旧记录
1.3.2.2. 所有的服务器最终都会收到更新,到那时,无论查询的是哪一台服务器,得到的都是更新之后的结果
1.4. 混合一致性
1.4.1. 混合一致性(hybrid consistency)模型通常用在一些NoSQL数据库里,这种数据库支持以最终一致性的方式写入数据,同时又支持在读取数据时通过API来指定你是按照立即一致性来读取,还是按照最终一致性来读取
1.4.2. DynamoDB数据库就允许用户在读取数据时,采用强一致性(也就是立即一致性)的方式读取,这样数据库就总是会从leader(也就是最早发生写入的那个地方)开始读取,所以即便新值还没有传播到所有的地方,用户也总是能够看到这个值,而不会读取到尚未更新的旧值
1.4.3. DynamoDB、MongoDB、Couchbase以及其他几种数据库都支持混合一致性模型
1.4.4. 在备份这样的数据库时,可以指定自己需要按照立即一致性的方式读取最新的数据
1.5. 你必须根据该模型采取相应的办法,以保证你正在备份的(或者你以后恢复出来的)数据是一致的
1.6. 参照完整性(referential integrity,也叫引用完整性)问题
1.7. 数据库所支持的一致性模型会影响该数据库面对各种故障的能力
1.7.1. 有些数据库能够应对某个存储设施发生故障的情况
1.7.2. 有些数据库能够应对某个计算节点发生故障的情况
1.7.3. 有一些数据库则能够同时应对某个存储设施与某个计算节点发生故障的情况
1.8. 某个数据库究竟能够应对什么样的故障,还取决于它是否会把数据复制到(或者说分享到)其他存储设施或计算机上
1.9. 数据库应对某种故障的具体方式,则通常与它所采用的一致性模型有关
1.10. 位于本组织数据中心里的传统数据库
1.10.1. 如果数据库采用的是立即一致性模型,而且只关心自己能不能应对节点故障(而不关心是否能够应对存储故障)
1.10.1.1. 通常的设计方案就是采用两个高度可用的(highly available,或者说,具备高可用性的)主机,让它们共享同一个存储阵列
1.10.1.2. 能够面对其中一个主机(也就是其中一个节点)出现故障的情况,但无法面对整个存储阵列发生故障的情况
1.10.2. 如果数据库采用的是立即一致性模型,但你想要的是一个全冗余系统(fully redundant system),那么可以采用shared-nothing(无分享的)架构
1.10.2.1. 这种架构不会让数据库节点共用任何东西(这也包括不让它们共用存储设施)
1.10.2.2. 这种集群一般只有两个节点,因此这实际上意味着把数据复制给另外的那个节点
1.10.2.3. 还可以做出配置,让某个数据库事务必须等到这两个节点全都更新完毕之后,才会接到ACK信号,以表示写入的数据已更新到了所有的节点上
1.10.2.3.1. 会影响性能,但是可以尽量确保数据完整(或者说,尽量提高数据完整性水平)
1.10.2.4. 方案能够面对其中一个节点或其中一个存储设施发生故障的情况,因为无论何时出现故障,集群中的其他节点都有一份完整的数据库可以使用
1.10.2.5. 最大缺点在于,它的扩展能力有限
1.10.3. 如果数据库采用的是最终一致性模型或混合一致性模型
1.10.3.1. 数据库的数据表通常会分布到几十个乃至成百上千个节点中,它会采用与对象存储类似的方式,将数据复制到这些节点
1.10.3.2. 如果某个节点上的数据发生变化,那么该节点会立刻记录这次更新,并根据设计者的要求,把这个新的数据复制到一定数量的replica(副本节点)上
1.10.3.3. 一般情况下,每条记录会复制到三个replica上
1.10.3.3.1. replica不仅能够充当冗余机制,而且可以帮助我们顺利执行备份与恢复流程
1.10.3.3.2. 在备份之前可以先把复制机制暂停,将内存中的数据全都写入磁盘,并从每个节点里拿一个replica出来,然后针对拿出来这个的replica做备份
1.10.3.3.2.1. 备份工作不会影响主数据库的性能
1.10.3.3.2.2. 备份下来的这个镜像,其内容是一致的,不会出现那种因为同时备份多个数据状态不一致的节点而产生的参照完整性问题
1.11. PaaS数据库与serverless数据库
1.11.1. PaaS与serverless数据库经受故障的能力,取决于你在购买这种数据库时所选择的服务水平
1.11.1.1. serverless数据库通常具备相当高的可用性,能够经受各种故障
1.11.1.2. PaaS数据库是否拥有这么强的故障经受能力,则要看你想达到什么样的冗余水平
1.11.2. 无论是PaaS数据库,还是serverless数据库,它用来克服故障的这套技术都集成在该产品之中,这个数据库产品的用户是看不到这套技术的
1.11.2.1. 用户只需要享受该技术所带来的好处,而无须关注具体的实现方式
1.11.3. 其重点也仅在于应对硬件方面的故障
1.11.3.1. 无论执行的是什么样的操作,它都会把该操作的效果尽快传播到系统中的所有节点
1.11.3.2. 数据库也必须像其他东西一样得到备份,以防受人为原因影响而丢失数据
2. 传统数据库的术语2.1. 实例
2.1.1. 实例(instance)是运行在一个或多个计算机上的一组进程,凡是属于该实例的数据库都可以通过这组进程连接到共享内存并通信
2.1.2. 同一个实例里一般会有多个数据库
2.1.3. 同一个数据库也可以分布在多个实例上
2.1.3.1. 这些实例可能位于同一台计算机里
2.1.3.2. 可能分别处在某集群内的各台计算机之中
2.1.3.3. 实例通常会跨越多个服务器与节点
2.2. 数据库
2.2.1. 数据库(database)是由数据库对象(database object)所构成的集合
2.3. 表
2.3.1. 表(table)是数据库里的一组相互关联的信息
2.3.2. 正式名字是relation(关系)
2.3.3. 在给表中添加数据之前,必须先定义好schema(纲要/模式),而且表中的这些信息通常做了安排,使得各表之间不会毫无必要地出现重复数据
2.4. 索引
2.4.1. 索引(index)是一种有特殊用途的对象,专门用来查找表格中的数据
2.4.2. 能够更快地访问到数据,或是用一种与表格本身有所不同的方式来查看数据
2.4.3. 只针对表中的一部分内容(而不是整张表格)制作索引,这样的索引叫作稀疏索引(sparse index)
2.4.4. 通常并不需要把索引一并恢复出来,而是可以根据已经恢复的表格内容来重新建立索引
2.5. 行
2.5.1. 行(row)是由相互关联的属性所构成的集合
2.5.2. NoSQL数据库一般不使用“行”这个概念
2.5.2.1. MongoDB、DynamoDB与Neo4j都没有所谓的行
2.5.2.2. 在DynamoDB里,这叫作一个条目(item)
2.6. 属性
2.6.1. 对于表中的数据来说,最基本的元素就是属性(attribute)
2.6.2. 是单个的值
2.7. 数据文件
2.7.1. 数据文件(data file)是存放数据的地方
2.7.2. 可能是一个(尚且没有文件系统的)“生”设备
2.7.2.1. raw device,也就是所谓“生”文件,raw file
2.7.2.2. Linux里的/dev/sd1
2.7.3. 可能是一个(已经处于某个文件系统中的)常规文件
2.7.3.1. 所谓的“熟”文件,cooked file
2.7.3.2. /oracle/data/dbs01.dbf
2.7.3.3. c:\database\somefile.dbf
2.7.4. 有可能位于本地文件系统里面
2.7.5. 有可能位于某个NAS filer的网络驱动器中
2.7.6. 某些数据库产品要求你必须使用“生”分区存放数据
2.7.7. 其他一些产品则仅是建议你这样做,而不提出强制要求
2.7.8. 实质区别仅在于一开始应该如何创建此文件,以及稍后应该如何备份文件
2.7.8.1. 在数据库里并没有什么不同
2.8. 表空间
2.8.1. 表空间(tablespace)是由一个或多个数据文件所构成的集合,数据表会添加到这个空间里
2.8.2. 主要的表空间(main tablespace)
2.9. 分区
2.9.1. 数据库技术的一项重大进展就是能够把一张数据表分布到多个资源上
2.9.1.1. 能够给某张数据表分区(partition)
2.9.1.2. 把每个区分别存放在不同的资源中
2.9.2. 给数据表分区,把它存放在多个表空间中
2.9.3. 数据表可以横向分区,也可以纵向分区
2.9.3.1. 如果沿着垂直方向分区,那么可以把其中某些属性放在一个表空间中,并将另外一些属性放在另一个表空间中
2.9.3.2. 沿着水平方向分区,也就是按行来划分,把某些行放在某个分区里,把另一些行放在另一个分区里
2.9.4. 分片比分区更进一步,它会将数据表中的每块数据(也就是每一“片”数据)放在不同的节点上
2.9.4.1. 对于大规模的数据库来说,这意味着该数据库会运行在好几十或好几百个节点上
2.9.4.2. serverless数据库几乎都会对数据做分片,并将其划分到几十至几百个节点上
2.9.4.2.1. 在使用serverless数据库时可能看不到这些分片,或者根本就不用自己去考虑分片问题
2.10. ⑩主文件
2.10.1. 许多传统数据库与PaaS数据库都会用某种方式来记录某个已经安装好的数据库环境里的全部元素
2.10.2. 如果你运行的是serverless数据库,那么或许无法观察到这份记录
2.10.3. 一些数据库可能根本就不采用集中管控式的架构,因而也就不需要保留这样一份记录
2.10.4. 如果数据库采用集中管控式的架构,那么它会把所有的存储元素都记录下来,以便管理这些元素,这样的记录称为主文件(master file)
2.10.4.1. 主文件可能是一个JSON文件
2.10.4.2. 可能是一个文本文件
2.10.4.3. 甚至本身也是一个数据库
2.10.5. 里面记录着数据库系统的各种信息以及该系统的状态
2.10.6. 如果数据库产品允许你在同一个环境里配置多个数据库系统,那么主文件会把每个数据库系统的信息全都记录下来
2.10.7. Oracle数据库将这个用来记录信息的主文件称为控制文件(control file)
2.10.8. SQL Server会用主数据库(Master Database)来记录
2.11. ⑾事务
2.11.1. 任何一种发生在数据库里,且要修改其中一项或多项属性的活动,都可以称为事务(transaction)
2.11.2. 简单事务(simple transaction)
2.11.2.1. 简单事务只需用一行语句即可实现
2.11.3. 复杂事务(complex transaction,又叫作复合事务)
2.11.3.1. 复杂事务可能比较长,需要用多条语句来实现,而且你必须把这些语句明确地放在一条起始语句与一条结束语句之间
2.11.4. 如果在执行事务的过程中发生了问题(例如程序崩溃),那么可以撤销整个事务,让数据库恢复到执行该事务之前的状态,以确保其数据是一致的
2.12. ⑿事务日志
2.12.1. transaction log
2.12.2. 所有的关系型数据库管理系统都有这样的日志,然而NoSQL数据库则未必包含此机制
2.12.3. 在从备份里恢复数据库的时候,这样的日志也很有帮助
2.12.3.1. 如果系统崩溃,导致我们必须根据最近制作的备份把数据库恢复出来,那么在恢复出来之后,必须设法将发生在备份点以后的事务重做一遍,否则那些事务就会丢失
2.12.3.2. 事务日志正好可以帮助我们实现这样的重做,因为它记录了用户在数据库上执行的每项事务,以及这些事务所修改的记录
2.12.3.3. 如果数据库系统因为崩溃而必须重新录入某些事务,那么它就可以利用事务日志里的这些信息来录入那些事务,从而修复数据库
2.12.3.4. 如果其中某个数据文件已经损坏,而且无法通过事务日志来修复,那么你就必须自己从备份里恢复那个数据文件了
2.12.4. 把数据文件恢复到当初制作备份时的状态之后,主数据库在查看该数据文件时就会意识到:这个数据文件的状态处在早于当前时间点的某个时间点上,因此,它可能是从备份里恢复出来的(而不是本来就在那里)
2.12.5. 究竟是先重做还是先回滚,取决于具体的数据库产品,但无论如何,目标都是一致的,都是要令尚未提交的事务回滚,并根据事务日志把需要重做的事务重做一遍,使得系统在崩溃或重启之后能够恢复到适当的状态