it_worker365

   ::  ::  ::  ::  :: 管理

异步,最终一致性,幂等操作

关系型数据库隔离了数据的存储路径,让用户只关心查询的逻辑,为了实现事物和强一致性通过各种锁牺牲了性能
互联网在线处理需求排列
数据的扩展性 > 请求的响应时间 > down机时间 > 成本 > 快速自动恢复 > 数据的读取一致性 > 开发相关
 
多机事物  多机join 分布式索引
 
预写日志WAL,队列记录了每次的写操作(用户的一次写操作可能对应计算机内的多步操作),利用操作系统的院子操作fsync()将一小段数据写入磁盘,保证数据不丢失
 
触发器都是同步的,只有写入数据操作和触发器操作都执行完了才算结束
 
关系表叫T,有三个行组成 pk,cash,col2
映射的key是pk的值,value则是cash+col2的组合:
那么,select * from T where pk= 100020这个查询就可以被转译成一个非常简单的针对映射的操作了,map.get(100020)
sql解析器 => 执行优化器 => 锁  => 映射[读取主数据] =>触发器[触发读取事件] =>锁[释放读锁]
 
select * from T where cash= 100
建立一个新的映射关系idx_cash,针对原有T表中部分数据的重排,key是cash,value则是pk。主索引或一级索引是PK为key的数据,非PK为key的数据成为二级索引或辅助索引。当执行查询时,先查辅助索引idx_cash,以logN的复杂度找到一批pk数据,然后再去住索引中按照pk去找到记录。

sql解析器 => 执行优化器 => 锁[申请读锁(或使用MVCC)] => 映射[读取二级索引] => 映射[读取主数据] =>触发器[触发读取事件] =>锁[释放读锁]

 
insert into T (pk,cash,col2) values (100,10,20)

sql解析器 => 执行优化器 => 锁[申请写锁,同时锁住主数据和辅助索引数据] => 映射[读取主索引,判断该值是否存在] =>预写式日志[写入数据日志]=> 映射[写入数据,如果不存在] =>触发器[触发写入事件] => 映射[根据触发器,更新二级索引] => 触发器[触发二级索引写入事件] =>预写式日志[标记该条记录全部写入完成]=>锁[释放写锁]
 

begin transaction ;

预写式日志[声明一个事务的唯一标记]

select cash from T where pk = 1;

sql解析器 => 执行优化器 => 锁[申请读锁] => 映射[读取主数据] =>触发器[触发读取事件]

update T set cash = cach-100 where pk = 1;

sql解析器 => 执行优化器 => 锁[读锁升级为写锁] => 映射[读取主数据pk=1]  => 预写式日志[写入数据日志,添加事务的唯一标记] => 映射[写入数据] =>触发器[触发写入事件] => 映射[根据触发器,更新二级索引] => 触发器[触发二级索引写入事件]

update T set cach = cash+100 where pk =2;

sql解析器 => 执行优化器 => 锁[读锁升级为写锁] => 映射[读取主数据pk=2]  => 预写式日志[写入数据日志,添加事务的唯一标记] => 映射[写入数据] =>触发器[触发写入事件] => 映射[根据触发器,更新二级索引] => 触发器[触发二级索引写入事件] 

commit;

预写式日志[标明该事务提交]

DB磁盘读写优化:一次性读取或写入固定大小的一块数据,并尽可能的减少随机寻道这个操作的次数
 
B树原位写入:查找到当前数据对应块的位置(找到某个块【数据块包含了一组个数有限的有序数据】,检查主键约束,是否需要分裂),然后将新数据写入到刚才找到的数据块,然后再查找到块所对应的磁盘物理位置,将数据写入。假定内存只能容纳一个B树块大小的数据,操作需要两次随机寻道(一次查找,一次原位写)
EG:   现有块 0,1,2|3,4,5|6,7,9 , 写入8 
先找到块6,7,9 ==> 放不下,分裂为两个块 ==> 写出后变为 0,1,2|3,4,5|6,7,空|8,9,空
 
内存满了之后如何将数据写回磁盘
1. 原位写入(Innodb),不浪费空间,保证查询O(log2n)
2. 队尾写入,不做原位替换,只将新写入的数据追加到整个数据的尾部,加快写入速度,缺点是老值还在原数据块中,占用了额外的空间。树1:0,1,2|3,4,5|6,7,9; 树2:5',8,9'
读取最新值: 
A. 每次将内存中的树和硬盘中的树做合并后写到新位置,并更新父节点的指针--磁盘增删次数多了导致磁盘空洞,随机跳跃存在,对范围查询不友好,读取的随机IO增加
B. 内存中存在一个或者多个有序树结构,读取根据时间顺序倒着去多个树种一次查找,结果第一个找到的就是最新值。
 
空间浪费:
后台开一个线程,利用异步归并排序的方式将多个小的有序树进行合并后追加。因为是异步并且写出来的数据也是有序的,所以也尽可能降低磁盘寻到次数
树1:0,1,2|3,4,5|6,7,9; 树2:5',8,9' ;  树3:0,1,2|3,4,5'|6,7,8|9',此时树3已经包含了树1.2,删除他俩即可。
 
LSM Tree(log structured merge tree),针对传统b树在磁盘写入性能上的优化。放弃部分读能力换取写入能力的最大化。假定内存足够大,不需要每次更新都写入磁盘,可以先将最新的数据驻留在磁盘中,等积累到一定程度,再用归并方式将内存数据合并追加到磁盘队尾
SSTable/Merge-dump模型的LSMTree: 避免写入太多,内存不够,所以定期将内存数据刷写到磁盘尾部并清空内存,通过良好的缓存和compaction机制以及适当的bloom-filter可以降低对读效率的影响
 
posted on 2017-05-05 17:09  it_worker365  阅读(161)  评论(0编辑  收藏  举报