CEPH VERSION: Quincy 17.2.6
在PG层即将调用ObjectStore层的时候,会组装ObjectStore层的事务,然后通过
ObjectStore::queue_transactions或ObjectStore::queue_transaction提交,区别是多个事务或单个事务提交。
ObjectStore层事务结构如下,这是个不针对具体存储方案的一般性描述
ObjectStore层事务是抽象的,需要具体的ObjectStore实现来进行转换,才能真正与底层的Store组件匹配,最终完成IO落盘。所以,自然的,BlueStore会有自己的事务结构。
BlueStore::queue_transactions开始的时候,就会将一次提交的ObjectStore::Transaction转换为一个BlueStore::TransContext,注意是一次提交的多个上层的事务被转换为单个BlueStore事务。
转换发生在_txc_state_proc之前过程中,具体后文再分析,此处关注数据结构。转换后的BlueStore事务结构如下:
从上图可以看出来,本质上,事务由两个核心部分组成:
- 一个rocksdb::WriteBatch bat,所有元数据变更都被encode到了这个WriteBatch之中,最后被提交至RocksDB
- 若干个aio_t,包含所有数据变更,最后被提交至BlockDevice
至此不免会有些疑惑,BlueStore事务要更新RocksDB和BlockDevice两个地方,如何保证事务的原子性呢?
BlueStore的写策略综合运用了COW和RMW策略。新写直接分配空间写入即可;块大小对齐的覆盖写采用COW策略;小于块大小的覆盖写采用RMW策略。
新写和对齐覆盖写,都可以先分配空间,而后写数据到新位置,最后更新元数据到rocksDB,只要多个kv的rocksDB更新能够保证原子性,那么整个事务就具有原子性。
RMW则复杂一些,它本身是非原子操作,那么如何保证这个过程的原子更新呢?BlueStore的方案是,识别到这些RMW操作,将它们组装成bluestore_deferred_transaction_t,简称deferred_txn,而后将deferred_txn序列化成kv,合并放到rocksdb::WriteBatch中,一并提交给RocksDB,只要提交到了RocksDB,则有了持久化保证,正常情况下可以延迟做它的写盘操作,异常重启的情况下,可以通过回放来完成这些延迟写盘操作,一切就有保障。
所以,经过上述过程,BlueStore事务的原子性保证,就集中到了rocksdb::WriteBatch的原子性保证,而WriteBatch本就是一次RocksDB::Put()的原子操作集合,要么完全成功要么完全失败,并且提供read-committed事务隔离级别,所以事情就完美解决了。
下一篇接着分析BlueStore txc状态机,揭示事务的生命周期