Concurrency Control for Adaptive Indexing
自适应索引的目标在于能够隐藏或最小化索引创建的代价。它的一个副作用就是在查询过程中,从自读变成了更新事务——可能引起锁的争用。
本文研究的是自适应索引只读查询的并发控制。
自适应索引是查的越多越优化,先前的查询以Database cracking的方式进入了查询中。代价就是读查询变成了写查询,查询在执行扫描或者索引查找的时候会调用操作来递增地优化数据库的物理结构。
自适应索引在查询中的并发控制需要协调索引的更新优化比更新传统的索引简单(然而传统索引在查找时并不需要更新)。图1展示了自适应索引和传统索引的对比。只读查询引起的更新只影响索引的结构不影响索引的内容,因此用latch而不是lock。并且这种查询带来的更新是可选的,可以跳过的——这就放宽了比传统索引更新的限制。
另外随着查询,索引的优化,锁的粒度会越来越小。
三点贡献:
自适应索引在并发查询中也维持着自适应的属性(体现在锁的粒度)
自适应索引随着查询的增加,并发执行时的冲突会自适应的减少
自适应索引可以探索并发查询的调度来增加并行性
前期工作
database cracking 略
Adaptive Merging:database cracking类似于快速排序,而Adaptive merging类似于归并排序。它会事先分成等大的部分,在各个区内先排序,然后产生结果到一个final partition,查询最后的结果都会进到final partition中,final partition就是优化好的部分。final partition是每次都要访问的,要么结果在里面,要么结果需要移入里面。图3
Database Cracking的初始化代价很小,但是收敛慢(需要较多次查询来形成full index);adaptive merging初始化代价较大,但是收敛较快。如何理解
Hybrid Adaptive Indexing:结合以上两个方法的优点,又避免了两个的缺点,能够快速收敛,又不用初始分区的完全排序。如图4
方法
图5是索引的状态,1是不存在索引,2是索引在目录中但还没有创建完毕,3是传统索引的操作(在未完成的部分索引上,实际上就是数据的插入),4是自适应索引的优化操作,5是完全建立好的索引
自适应索引优化也就是状态4——所有的entries都有,但不在自己的位置上。
两个基本因素减轻自适应索引的并发控制的开销(也就是虽然是逻辑上是只读的查询,但事实上会修改索引数据结构):
只读查询修改的是索引的数据结构而不是索引的内容(latch 和 lock)。用户数据和系统状态的隔离,系统事务和用户事务的隔离。当用户事务回滚时,已经完成的优化是不需要回滚的。
索引结构的自适应性也要适应当前的工作负载,其锁的粒度也会随着调整。越优化,锁的粒度越小。减少latch争用的可能性。
3.1Locks vs. Latches 略
3.2层次锁和增量锁
层次锁——减少锁的数量
增量锁——动态改变锁的粒度
Crack创建的分区有利于增量锁
3.2 并发控制
并发控制的解决有以下集中方法:
Latching:快速重组期间要持有latch。需要先获得page latch再去获取page或者key value上的lock
冲突检测:因为自适应索引是可选的,有冲突可以放弃优化
提前终止:索引优化可以随时停止
MVCC
B树上的自适应索引
这是用adaptive merging自适应的建立一个完整的B树的方法,partitioned B-tree。
面向列的自适应索引
面向列的存储与访问:同一个表中所有元素都是对其的,表中第i个元组的各个元素都在其各自列的第i个位置。面向列的自适应索引利用了列的两个特性:1.定长稠密数据很好优化 2. 对于在多个列查询的情况,每个列的查询只占整个查询的一部分,这就意味着它完成后就可以释放latch了,而不用整个时间都持有latch。
并发控制
Column latches:考虑在一个列上的单个select,select开始执行,(首先latch全局数据结构什么意思看这个列的cracker index有没有创建,如果没有初始化一个原始的cracker index列)latch这个AVL(AVL是database cracking默认的索引)和cracker array。一旦获得latch,释放全局数据结构的latch,然后select和对cracker array的优化开始在上面进行排他访问。select(包含cracker index优化和AVL更新)完成后,索引的latch释放。
(全是排它锁)
读写Latch:同一个列,一个查询一个聚合,重组可能会使聚合出现错误。但是多个聚合操作又可以在同一个列并行执行。每个select操作都需要写latch,聚合(和其他不发生cracking的操作)需要读latch。
Pieces-wise Latches:事实上cracking只发生在包含这个范围查询[a,b]边界a和b的两个piece上,也就是这用给这两个pieces和AVL上Latch就好(图9)。
图8中间,首先初始化,Q1给全部加写锁,在70Cracking成两个pieces,然后之后的就可以按照两个边界并行执行了。
Pieces-wise Latch的优化:边界[a,b]在不同pieces时,可以并行执行,一个冲突另一个可以执行。查询Q1会对等待的查询产生影响。如图10,Q1是100,执行后原先的piece被分成了两个,对于Q2和Q3来说其对应的pieces都改变了(但可以并行执行了)。对于每个查询都是通过AVL树来获得其所在piece的。
另一个优化是可以通过调度来增加并行性,如Q1-Q5分别是等待在piece[0,100]上的查询,并且key分别是20,30,40,50,60,如果按照这个顺序来执行的话,只能串行执行,但如果执行Q3-40那么剩下的就可以并行执行了。
持续减少冲突:越查询越优化 ——》piece越小 ——》优化的成本越低——》一个查询持有的Latch时间越短——》并发度越高
实验分析
基本性能
对于扫描,完全无法利用之前查询的知识;对于排序,如果一个查询是离群值what?或者工作负载只有几个查询,不能分摊掉前期投入的巨额成本。
并发控制
在完全索引中 不需要考虑并发问题,而在自适应索引中,因为把只读转化为了写,因此为了性能必须要控制并发控制的成本。
单个客户端作为基本(串行执行),看多个客户端与其的对比情况。图12显示并发控制成本很小。
未来的工作
并发控制的算法
主动算法:一旦索引达到当前工作负载的最佳状态,查询将停止进行优化操作和随着伴随的并发问题。——发生冲突机会减少,代价是活动步骤中产生潜在的高成本。
懒惰算法:如一次只有一个查询对给定的数据部分产生副作用,减少争用以实现更高的并发——降低优化速度。
动态算法:当前自适应索引优化操作都是独立的对一个查询做出反应,为一个查询决定如何为当前的工作负载进行优化。因此下一步考虑如何使用一组查询来指导索引优化。如在给定一个piece等待的多个查询可能有更高的策略来进行优化。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。