海量数据存储系列读后感
读了淘宝的海量数据存储系列, 感觉豁然开朗, 见地提升了很多. 现在简单用自己的话总结一下里边的"精髓".
SQL与关系代数(第1-2章)
第一 二章内容比较简单. 第一章给了我们一个模型. 他是一个三层结构. 模型如下:
- 用户API
- 关系代数和事务引擎
- k-v 存储.
用户API就是我们使用SQL, 结构化查询语言. 这个对少有的数据库知识的人来说都不陌生, 也不用介绍. 传统的数据库数据是按行存储, 索引(二级索引, 复合索引)辅助以提供查询性能(以空间换取时间). 多条件查询就是关系运算.
比如这样子一个用表,有四个字段: pk(id), user_id(用户id),name姓名, sex(性别) . 如果我们要查询id=0 的用户信息, 最笨的方法是遍历所有的表, 逐个记录进行id的比对,返回符合条件的记录. 还有一种是建立一个索引表, 里边有一个id字段还有id对应的记录的物理地址, 我们只需要扫描这边索引表, 之后根据id对应的物理地址返回查询记录信息(这叫反查)就可以返回id=0的记录数. 同理 如果我要查询 性别男并且用户名称为xxx的人, 一种做法我对性别sex和用户name分别做索引, 之后取交集(这是一种关系代数运算). 还有一种 我对用户名称name+性别 sex 做一个复合索引, 直接返回满足条件的记录. “或“的情况就是各种条件取并集. 所以简单的可以把查询的各种条件归结为关系代数运算. 深层次想想索引表就类似于一种k-v存储. 采用的是B tree(多路搜索树)树形结构,以支持快速范围查找.
单机事务篇(第3-4章)
事务是数据库的最重要的一个属性.它的本质是什么呢?事务,本质来说就是一组由一个人(或机器)发起的连续的逻辑操作,共同的完成一件事情,在 完成整个事情之前,其所有的改动,都不应该对其他人可见和影响。而在事务结束之后,其一切 的改动,都必须全部立刻对其他的人(或机器)可见。
人们为了描述事务, 提出了四个词汇 ACID, 下面简单看看什么是ACID:
- 原子性(Atomicity):也就是说,一组操作,要不就都成功,要不就都失败。不存在中间状态。
- 一致性(Consistency):一致性,也就是说,这个事务在提交或回滚的时候,对其他人(或机器) 来说,数据的状态是同一的,不会出现中间的状态。最理想的状态下,就是说,数据提交后,所有的更改立刻同时生效,可惜,在计算机领域,这个做不到。因为 cpu 运算,磁盘写入,内存写入,都是要时间的,内部一定是个顺序化的过程,所以不可能做到绝对的立刻同时生效。所以一致性一般来说指代的是逻辑上的同时生效,比如,我要改 A,B 两行数据,那么,最简单的一致性保证就是,对 A,B 加锁,改 A,B,然后对 A,B 解锁。这样下一个人读到的一定是 A,B 的最新值啦。
- 隔离性(Isolation):隔离性,是依托锁来进行设计的。我们所知道的锁,主要有以下几种,1.读写锁,2. 排他锁. 隔离性的四种级别其实就和这两种锁的实现有关,之所以要定义四个级别,其实原因也是因为,锁的范围越大,并行效率越低。而范围越小,那么并行的效率就越高。
- 读未提交: 其实就是什么锁也没有,所以数据的中间状态,是可能被其他人读到的.
- 读已提交:就是读写锁实现,读锁在查询之后会被释放掉,所以这样其他人可能会更改那些被释放了读锁的数据,这样当前事务再去读取的时候,就可能读取到被别人修改过的数据了,所以一 个人在事务中读取到的某个数据,可能下次读取就变成别的数据啦。这就是不可重复读的意思.
- 可重复读:也是个读写锁实现,读锁会阻塞其他人(或机器)的写,于是,只要是事务中读取到 得数据,都被加了锁,其他人没办法改他们,于是就实现了可重复读咯.
- 最后是序列化,就是所有都顺序,一个大锁全部锁住
- 持久性(Durability):持久性就是,事务执行后,数据不能丢. 为了一致性, 就会对数据做多个副本, 并且副本要尽可能的分布在不同的机房,以避免天灾人祸等不可预测的因素.
要保证一大批数据在不同的时间被查询, 被修改,直至事务提交用户才感知数据变化又要保证性能是个很复杂的事情. 实现主要有后面几种方法:
- 1 排他锁: 排他锁性能最差, 几乎不可用.
- 2. 读写锁: 而读写因为读可以并发,所以效率稍高,但写和读不能同时进行
- 3. Copy on write(MVCC): 则读取和写入之间可以互相不影响,所以效率更高
- 4. 队列. 内存时效果很好.省去中断上下文切换的时间. 数据库层面应用较少.
- 5. 内存事务. 目前还在研究阶段,具备很大潜力的东西.
读写锁:
也是隔离性中―读已提交,可重复读 两种实现中最重要的底层实现方式.简单来说,就是如果一个人在事务中,那么他所有写过的数据,所有读过的数据,都给他来个锁, 让其他小样儿都只能等在外面,直到数据库能确定所有更改已经全部完成了,没有剩下什么半拉子状态的时候,就解开所有的锁,让其他人可以读取和写入.
MVCC:
MVCC对读写锁对读写锁进行了一个改进,写不阻塞读,读不阻塞写. 如果一个人在事务中,会先申请一个事务 ID,这个 ID 是自增的,每个事务都有他自己的唯一的 I D,那么他写过的数据,都会被转变为一次带有当前事务 ID 的新数据,在读取的时候,则只会读取小于等于自己事务 ID 的数据。这样实现的东东,语义上来说,与可重复读就一样了。而如果读小于等于全局 ID 的数据,那么这样的实现,就是读已提交了. MVCC 只实现了四个级别中的第二级和第三级,但这已经足够了. 有了这个东西,我们的一致性也就很容易保证了,因为一个事物和他对应的版本号对应,又有更改后的数据和更改前的数据,如果要提交,那么就只需要很简单的让更改后的数据生效可见即可, 这样我们可以将大量的更新中要做的事情,都在事务过程中进行,这样,比原有的基于读写锁的必须在 commit 时候一起做掉来说,commit 这个操作就轻量化了很多,于是,就可以支持更多的人(或机器)持有事务状态了。
原子性和持久性:
原子性么,一般来说就是要么都成功,也就是新版本数据都让他生效,要么就都失败,也就是让和自己事务 ID 对应的所有修改都无效即可。也很好就解决掉了。持久性。这个就是后面我们要在写入模型里面介绍的东西了,基本上来说就 是写磁盘策略的事情.