脚本宝典收集整理的这篇文章主要介绍了《数据密集型应用系统设计》读书笔记,脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
个人读书笔记,有些地方用词不够严谨(欢迎评论指正),见谅。书籍链接
笔记是个人理解,与书籍可能有偏差,建议看书。
什么样的数据适合图数据库?
社交关系?网页数据?地图数据?
MySQL有没有事务重试?
更新数据时,是按行更新还是按页更新?
更新数据时,是采用copy on wrITe还是直接修改?
允许部分服务可不用(不影响整体系统),最后同一个服务有多个提供者(集群)
相对应的就是关系型数据库以及NoSQL(文档型MongoDB,图数据库等)
拿简历举例:
简历包含有工作经历、教育经历
如果场景都是要加载全部数据,那么适合使用NoSQL
灵活性
mysql5.7开始增加对json的支持,mysql8.0进一步优化的json支持
和文档型数据库一样,不会在保存时强制格式。
查询时无法确定级联层数???
哈希索引
优点:速度快
缺点:全部放入内存。无序,所以对顺序范围等查询不友好
SSTable和LSM树
因数据结构以及顺序写,保证的写入的性能
Memtable:数据首先放在内存中,按key排序
Immutable MemTable:MemTable达到一定大小后,会转化成Immutable MemTable。写操作由新的MemTable处理,Immutable转成SSTable
SSTable:已持久化。不同SSTable可能存在相同的key,以最新的为准。SSTable达到一定层数,会触发合并,此时冗余的旧key会被去除
B树
mysql使用的B+树是B树的增强版
比较B树和LSM树
B树写入慢,读取快。LSM树写入快,读取慢。
LSM写入同等数据需要磁盘IO比B树少
LSM压缩比B树好,因为B树的空字段、页结构等,会存在少量不被使用的空间。
LSM需考虑压缩和写入的比
数仓的事实表基本都是大宽表,字段较多(100列)
文本格式:JSON、XML、CSV
json不支持二进制字符串以及对数字类型不够友好,比如无法准确表达int和float,比如20不知道是int还是float
数据流:
数据库中的数据流
服务中的数据流:REST和RPC
消息传递中的数据库(消息队列)
注意向前和向后兼容:
对于RESTful API,常用的方法是在URL或HTTP Accept头中使用版本号
保证最终一致性。一是性能方面的考虑,二是数据离用户越近,延迟越小(CDN?)
读己之写
用户不想延迟看到自己提交的数据(写入主库,但是读取从库时还没更新)
解决方案:
单调读
确保每个用户总是从同一个副本读取,否则会出现两次查询,副本a有数据,副本b没有
一致前缀读
如果一系列写入按某个顺序发生,那么任何人读取这些写入时,也会看见它们以同样的顺序出现。
比如顺序为ab,但是读取到ba,常见于分区、分片
解决方案:保证因果相关的写入都写入相同的分区(分区划分策略?)
复制延迟的解决方案
事务???
场景
处理写入冲突
多主复制拓扑
MySQL仅支持环形拓扑,即只接收一个节点的写入和自己的写入。也类似与星型拓扑。
当节点故障时写入数据库
同时写入多节点,过半成功则成功。同时读取多节点,取最新版本的数据。
读修复和返熵
读写的法定人数
n个副本(不是节点),写入w个节点,读取r个节点,则 w + r > n
这样才能保证每次读取至少有一个节点是最新的
节点可能大于副本数,有可能两个节点组成一个副本(分区)
法定人数一致性的局限性
宽松的法定人数与提示移交
运维多个数据中心
法定人数配置只限定在本地数据中心,并异步将数据同步到其余数据中心。
最后写入胜利(LWW)
保留最后版本数据,其他版本
捕获”此前发生“关系
就是每个连接(客户端)记录并维持自己的版本号,只修改自己版本的东西
在MongoDB,ElasticseArch和Solr Cloud中被称为分片(shard),但是分区(partitioning)** 是最约定俗成的叫法。
一个大型数据库可以拆分成多个分区,分区也可以复制(主从、无主等)
基于文档的次级索引进行分区
每个分区维护自己的索引,也叫本地索引。即分区0,分区1都可能存在color=red的索引数据
基于关键词的次级索引进行分区
也叫全局索引。一定范围的索引数据汇聚到同一个分区,比如首字母从a
到r
的颜色在分区0中,s
到z
的在分区1。就是分区1的索引可能被存到分区1中。
分区同时存数据以及索引数据。
缺点:写入速度较慢且较为复杂
就是分区的增减,节点故障等导致的数据、请求、负载转移的过程,叫再平衡。
固定数量的分区
初始创建好固定数量的分区,增加节点时,帮其他节点接管一部分分区。比如初始4个节点,20个分区,每个节点5个分区。当增加一个节点时,每个节点那管理4个分区。
缺点:初始很难定好总分区数。分区数量不正确会影响性能。
Riak、ES、Couchbase、Voldemort使用了此方法。
动态分区
当分区增长到超过配置的大小时,会被分成两个分区。与之相反,分区缩小到某个阈值以下,则合并。
按节点比例分区
每个节点具有固定数量的分区,新节点加入集群时,所以选择固定数量的现有分区拆分,一人一半。
查询要转发到哪个节点,问题概括为服务发现
三种方案:
原子性(atomicity)
一致性(consistency)
原子性,隔离性和持久性是数据库的属性,而一致性(在ACID意义上)是应用程序的属性。
隔离性(isolation)
同时执行的事务是相互隔离的。
可串行化级别:事务串行执行,上一个事务没结束,下一个事务不会开始执行。
持久性(durability)
处理错误和中止
发生错误时,事务可以中止并安全地重试。
事务重试仅在临时性错误(例如,由于死锁,异常情况,临时性网络中断和故障切换)后,永久性错误(违反约束等)重试是无意义的。
即非串行化的级别
读已提交
解决脏读和脏写。就是只有数据在commit的一刻才全部生效。
快照隔离和可重复读
防止丢失更新
丢失更新:事务a、b同时修改同一条数据,后提交的事务会覆盖前事务修改的数据。
自动检测丢失的更新:oracle的可串行化隔离级别,可自动检测到丢失更新,并终止惹麻烦的事务。(mysql没有)
解决方案:
写入偏差与幻读
写偏差:两个事务正在更新两个不同的对象。丢失更新是两个事务更新同一个对象
举例:医院规定最少有一人值班,医生a、b同时请假,事务A、B各自修改了a、b的值班状态。最后0人值班。
解决方案:
为什么数据库默认都不使用串行化?
真的串行执行
在单个线程上按顺序依次只执行一个事务,正是可串行化的定义。
Redis实现的就是串行执行事务(旧版redis是单线程)
两阶段锁定
两阶段锁定(2PL,two-phase locking),不是2PC
mysql的默认隔离级别为可重复读(mvcc是实现此隔离级别的技术),在此级别下,如果两个事务同时尝试写入同一个对象,则锁可确保第二个写入必须等到第一个写入完成事务(中止或提交),然后才能继续(防止脏写)。
事务A修改了id=3的数据,不会阻塞事务B的读,但是B读的是B开启事务时的旧数据。
在2PL中,写入不仅会阻塞其他写入,也会阻塞读,反之亦然。快照隔离使得读不阻塞写,写也不阻塞读。
事务A修改了id=3的数据,事务B会被阻塞,直到事务A中止、提交或者回滚。
实现两阶段锁
2PL用于MySQL(InnoDB)的可串行化、SQL Server的可串行化、DB2的可重复读隔离级别。
2PL的性能
因为2PL会导致阻塞,在事务中越早获取独占锁,就阻塞越久。且容易导致死锁。
谓词锁
2PL无法解决幻读问题,因此需要引入谓词锁。
在2PL级别下的幻读问题如下:
// 1.事务A读取
select name from student where id=2;// 结果 name='jack'
// 2.事务B修改
update student set name='rose' where id=2;
// 3.事务A第二次读取,id=2的数据,会被阻塞
// 4.事务B提交,释放锁
// 5.事务A第二次读取的结果返回 name='rose',此时出现了幻读
谓词锁即共享读,排斥写
例如:
// 1.事务A读取(创建谓词锁)
select name from student where id=2;// 结果 name='jack'
// 2.事务B读取
select name from student where id=2;// 结果 name='jack'
// 3.事务B修改
update student set name='rose' where id=2;// 此时会被阻塞直到事务A提交
关键思想是,谓词锁甚至适用于数据库中尚不存在,但将来可能会添加的对象(幻象)。如果两阶段锁定包含谓词锁,则数据库将阻止所有形式的写入偏差和其他竞争条件,因此其隔离实现了可串行化。
索引范围锁
谓词锁性能不佳:如果活跃事务持有很多锁,检查匹配的锁会非常耗时。因此引入索引范围锁(index-range locking,也称为next-key locking)
通过使谓词匹配到一个更大的集合来简化谓词锁是安全的。
例如:如果有在12:00 ~ 13:00预定123号的房间,则锁定123号房间的所有时间段或者锁定12:00 ~ 13:00的所有房间。
这是匹配开销和阻塞开销的平衡。
可串行化快照隔离
可串行化快照隔离(SSI, serializable snapshot isolation)
和mvcc类似,但是在事务提交时(写入操作),会判断事务期间操作的数据时候已被其他操作修改,如被修改,则事务提交失败
// 1.事务A查询
select name from student where id=2;// name='jack'
// 2.事务B修改
update student set name='rose' where id=2;
// 3.事务A再次查询
select name from student where id=2;// name='jack'
// 4.事务A修改
update student set name='张三' where id=2;
// 5.事务B提交
// 6.事务A提交,报错。因为事务B修改了数据。
Q:为什么不在第4步的时候就报错,而是等待提交时才检测?
A:如果事务A是只读事务,即没有第4步的update,则没有写入偏差的风险。
Q:如果事务B的提交放到第三步之前呢,事务A的再次查询会异常吗???
A:我猜测会,否则就不是串行化了。实际结果如何,求大神解答
可串行化快照隔离的性能
与2PL相比,写不阻塞读,且读取运行在一致性快照上。
与串行执行相比,事务能在保证可串行化隔离的同时读写多个分区的数据,很好利用了多核cpu的性能。
以上是脚本宝典为你收集整理的《数据密集型应用系统设计》读书笔记全部内容,希望文章能够帮你解决《数据密集型应用系统设计》读书笔记所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。