内存数据库解析与主流产品对比(三)( 二 )


对于每个Transaction , 只需要比较该事务从开始到Commit之间 , 是否存在其他Transaction对满足搜索条件的数据集进行过增/删/改 , 从而判断是否存在幻影现象 , 如果存在 , 就直接终止事务 。
4. 多版本并发控制之HANA和HStore/VoltDB
HANA并行控制方式比较简单 , 采用悲观的多版本控制 , 由行级锁保护数据结构 , 每行由时间戳决定每个版本的可见范围 。 每个Transaction在更新或删除时都需要申请写锁 , 而且要做死锁检测 。
HStore/VoltDB是一个Partition系统 , 锁的粒度很粗 , 每个Partition对应一把锁 , 因此Transaction在某节点上执行时 , 需要拿到该节点所有资源 。 一旦一个事务可能涉及到两个Partition , 就需要把两个Partition的锁都拿到 。 所以Partition系统的优点是单Partition处理速度非常快 , 缺点是多Partition效率很低 。 同时 , 系统对于负载的偏斜非常敏感 , 如果有热点数据 , 那么热点数据就构成系统瓶颈 。
5. 多版本并发控制之负载预知
假设一个工作负载中 , 事务需要读和写的数据集可以提前获得 , 就可以在执行前确定所有事务的执行顺序 。 Calvin就是基于这样的假设设计的VLL (Very Lightweight Locking)超轻量级锁数据库原型系统 。 通用场景的工作负载是无法提前知道读写集合的 , 但在存储过程业务的应用中 , 可以提前确定读写集合 , 对于这些场景就可以考虑类似Calvin的系统 。
— 数据库管理系统中的持久化技术—
对于内存数据库而言 , 和基于磁盘的数据库相同也需要日志和Checkpoint 。 Checkpoint的目的是恢复可以从最近的检查点开始 , 而不需要回放所有数据 。 因为Checkpoint涉及写入磁盘的操作 , 所以影响性能 , 因此要尽量加快相关的处理 。
一个不同是内存数据库的日志和Checkpoint可以不包含索引 , 在恢复时通过基础数据重新构造索引 。 内存数据库中的索引在恢复时重新构造 , 构造完成后也放在内存中而不用落盘 , 内存索引数据丢失了再重构即可 。 另外一个不同是内存数据库Checkpoint的数据量更大 。 面向磁盘的数据库在Checkpoint时 , 只需要把内存中所有Dirty Page写到磁盘上即可 , 但是内存数据库Checkpoint要把所有数据全部写到磁盘 , 数据量无论多大都要全量写一遍 , 所以内存数据库Checkpoint时写入磁盘的数据远大于基于磁盘的数据库 。
Hekaton Checkpoint对于持久化的性能优化 , 第一要保证写日志时的高吞吐量和低延迟 , 第二要考虑恢复时如何快速重构整个数据库 。 Hekaton的记录和索引存放在内存 , 所有操作写日志到磁盘 。 日志只记录数据的更新 , 而不记录索引的更新 。 进行Checkpoint时 , Hekaton会从日志中恢复 , 并根据主键范围并行处理 。 如下图 , 分三个主键范围:100~199、200~299、300~399 , 绿色代表数据 , 红色代表删除的记录(单独保存被删除的文件) 。 在恢复时 , Hekaton用并行算法在内存中重构索引和数据 , 过程中根据删除记录过滤数据文件 , 去除被删除的数据 , 然后从Checkpoint点开始 , 根据日志回放数据 。
内存数据库解析与主流产品对比(三)文章插图
其他系统的Checkpoints1. 采用Logic Logging的系统如H-Store/VoltDB , 即不记录具体的数据改动 , 而是记录执行过的操作、指令 。 它的优势是记录的日志信息比较少 。 写日志时 , HStore/VoltDB采用COW(Copy-on-Write)模式 , 即正常状态是单版本 , 但在写日志时会另外“复制”一个版本 , 待写完再合并版本 。
2. 另一种是定期把Snapshot写入磁盘(不包括索引) , 比如Hyper就是基于操作系统Folk功能来提供Snapshot 。
— 数据库管理系统中的查询处理—
传统的查询处理采用火山模型 , 查询树上的每个节点是一个通用的Operator , 优势在于Operator可以任意组合 。 但Operator拿到的记录只是一个字节数组 , 还需要调用另一个方法来解析属性以及属性类型 。 如果这种设计放到内存数据库中 , 属性以及类型的解析都是在Runtime而非编译时进行的 , 会对性能产生影响 。
另外对于get-next , 如果有百万个数据就要调用百万次 , 同时get-next通常实现是一个虚函数 , 通过指针调用 , 相比直接通过内存地址调用 , 这些都会影响性能 。 此外 , 这样的函数代码在内存中的分布是非连续的 , 要不断跳转 。 综上 , 传统DBMS的查询处理方式在内存数据库当中并不适用 , 尤其体现在在底层执行时 。
内存数据库解析与主流产品对比(三)文章插图