本文源自:https://kernelmaker.github.io/Rocksdb_transaction_1

之前对Rocksdb都是关于它主体实现的介绍,最近在看他的事务实现,还没全看完,打算边看边记录,用几篇博客详细写一下Rocksdb Transaction相关的原理及实现。这一篇先不展开细节实现,简单介绍一下它的Pessimistic Transaction。

1. Pessimistic vs Optimistic

Rocksdb的事务实现包括Pessimistic(悲观)和Optimistic(乐观)。

Pessimistic transaction会在进行的过程中,当需要对某个key做Write操作时,首先对其加行锁,确保这个key在Commit或Abort之前不会被其他并行事务修改,然后在最终Commit或Abort后对所有行锁进行释放。当并发事务较高,并且事务间执行Write操作的key重叠度较高时,此时就需要采用Pessimistic transaction通过行锁来实现事务间的操作隔离。当然他的代价就是额外的锁操作。

Optimistic transaction则相反,Commit之前的所有Write操作都不需要加锁,冲突检测会在Commit时来执行,没有冲突则Commit成功,有冲突则Commit失败。当并发事务之间Write操作的key重叠度较低时,如果采用Pessimistic transaction反而会因为引入额外的锁操作反而降低性能,这里更适合使用Optimistic。Rocksdb默认是Pessimistic。

2. ACID

数据库事务需要满足ACID(Atomic、Consistency、Isolation、Durability)。这些正常应该是MyRocks或者InnoDB关心并实现的特性,而Rocksdb身为MyRocks的基础引擎,没有要求说一定得满足ACID,不过这里我们尝试强行套一下概念,来看看Rocksdb满足了哪些?

  1. Atomic:原子性通过Rocksdb已有的WriteBatch就可以实现,事务在Commit之前所有的Write操作都只是写到WriteBatch当中,在最终Commit时只需要如下一个Write(…)即可:
Status WriteCommittedTxn::CommitWithoutPrepareInternal() {Status s = db_->Write(write_options_, GetWriteBatch()->GetWriteBatch());return s;
}
  1. Consistency:一致性似乎不需要Rocksdb做什么,应该是MyRocks需要解决的事情;
  2. Isolation:隔离性通过行锁、Snapshot配合来实现,一会下面单独说;
  3. Durability:持久性,这个比较尴尬,我翻看代码,并没有看到上面Commit时Write调用传入的WriteOptions::sync被打开,也就是WAL走的是默认的刷盘策略(前面的博客有介绍),在机器掉电时会有丢失的可能。这就需要使用者在BeginTransaction时,将传入的WriteOptions中的sync手动设置为true;

可以看到Rocksdb的Transaction已经基本实现了AID;

3. 怎么用

Pessimistic transaction怎么用,和普通的事物一样,一开始需要TransactionBegin,最后需要Commit或者RollBack。下面看几个例子:

  1. 打开DB:
Options options;
TransactionDBOptions txn_db_options;
options.create_if_missing = true;
TransactionDB* txn_db;Status s = TransactionDB::Open(options, txn_db_options, kDBPath, &txn_db);
assert(s.ok());
  1. 开启事务:
Transaction* txn = txn_db->BeginTransaction(write_options);
assert(txn);
  1. 读写:
// 在事务中读取一个key
s = txn->Get(read_options, "abc", &value);
assert(s.IsNotFound());// 在事务中写一个key
s = txn->Put("abc", "def");
assert(s.ok());// 在事务外读取一个key
s = txn_db->Get(read_options, "abc", &value);// 在事务外写一个key
// 这里并不会有影响,因为写的不是"abc"
// 如果是"abc"的话
// 则Put会一直卡住直到等待事务Commit或者超时(本例中会超时)
s = txn_db->Put(write_options, "xyz", "zzz");
  1. 提交:
s = txn->Commit();
assert(s.ok());
  1. 析构事务:
delete txn;
  1. 最后析构DB:
delete txn_db;

4. Isolation

一般数据库事务隔离级别从弱到强分为:

  1. Read Uncommited:最弱的隔离级别,对读操作不加锁,对写加锁,会造成脏读
  2. Read Commited:读的时候加读锁,读完立刻放锁,写的时候加写锁,事务结束后放锁。这样的隔离级别会有不可重复读的问题
  3. Repeatable Reads:读的时候加读锁,事务结束后放锁,写的时候加写锁,事务结束后放锁。因为读锁在加锁后直到事务结束后才释放,所以其他事务无法修改对应key,这样就可以支持重复读,但还会存在幻读(Phantom Reads)问题
  4. Serializable:这事最严格的隔离界别,读的时候对加读锁,事务结束后放锁,写的时候对表加写锁,事务结束后放锁,由于都是对表加锁,所以没有幻读问题

知道了上述隔离级别的基本区别后,我们来看看Rocksdb的Pessimistic transaction在这方面是怎么做的。

  1. PessimisticTransaction的Put 会在一开始对key加写锁,直到事务结束后才释放
  2. PessimisticTransaction的Get 则不会对key加锁,它能读到什么数据完全取决于Options传入的Snapshot

开始看这里代码的时候,心里OS:“什么鬼?Get连锁都没有…”,后来发现还有一个GetForUpdate

  1. PessimisticTransaction的GetForUpdate 则会对key加读锁,直到事务结束后才释放。这个才是在事务中应该使用的读取操作,配合Put的写锁来实现一定程度上的隔离性。同样它能读到什么值也要取决于Options传入的Snapshot

有了Put和GetForUpdate的加锁,我们对比上面列出的隔离界别实现原理,可以发现:

Rocksdb Pessimistic Transaction的隔离界别是Repeatable Reads(当然也不可能是Serializable,一是因为Rocksdb暴露的接口不会造成幻读,所以不需要一把大锁来强行Serialize,二是Rocksdb也没有表的概念)

我们发现,Rocksdb对于加锁的时机是在需要读写某个key之前才加锁,这样的方式可以满足绝大多数的事务隔离场景,当然在有些极端场景下,用户希望在事务一开始就对所有接下来需要读写key加锁,直到事务结束后再释放。这个该如何实现呢?

要实现这个,有一个需要解决的问题:

无法在事务一开始就知道接下来要读写哪些key,所以无法提前对它们加锁

Rocksdb通过SetSnapshot来间接解决,BeginTransaction后,用户可以立刻调用SetSnapshot,这样该事务会记录当前DB的最新Sequence,然后再接下来事务的每一次Put操作时,会检查DB中该key的最新Sequence是否大于事务之前记录的Sequence,如果大于,则证明事务外部有对该key做了写操作,那么事务对应的Put则会返回失败。

可以看到,在这里Rocksdb采用相对乐观的处理方式,通过对Snapshot来取代提前对所有需要的key加锁,不过这样的处理方式可能会造成事务最终失败(事务外部修改某个key后会导致接下来事务内部修改失败)。

前面总说事务外部读写,这里除了其他事务可能读写外,也有可能是用户直接使用DB接口来读写,也就是:TransactionDB::Put和TransactionDB::Get,具体实现中也是将它们当成一个独立的事务(只有一条操作Put或Get)来处理。

5. 其他

看代码中发现一些有趣的功能:

  1. WriteBatchWithIndex:事务的Get和GetForUpdate操作可以读取到本事务中之前写过但还没有写入到DB中(未Commit)的最新值,具体实现就是通过WriteBatchWithIndex,它其实就是在WriteBatch基础之上加一个关于index的skiplist,这样就Get时可以在读Memtable之前先读在WriteBatch中查查看有没有最新写入。这个不止可以在Transaction中用,Rocksdb把它暴露了出来,如果在使用Rocksdb的时候有中途在WriteBatch里查找key的需求,就可以使用。
  2. SetSavePoint & RollbackToSavePoint:在事务的进行中,可以随时调用SetSavePoint来打记录点,然后在接下来可以随时回滚到上次打的记录点上。具体实现就是在SetSavePoint是记录一下事务的状态信息(如snapshot、num_puts、num_deletes、num_merges)以及对WriteBatch设置记录点(Record个数,rep当前size),然后在Rollback时恢复他们即可。
  3. 所有的锁操作默认不会无限卡死等待,会有超时时间,超时后将错误返回给用户来处理,这样更为合理。另外如果需要的话,可以开启事务Options的detect_deadlock_来强制在加锁前进行死锁检测

6. 总结

PessimisticTransaction就是一个事务的标准实现,之后再细扣下实现细节,以后提到事务的时候终于不再只是理论上的谈谈了,有了实践体会会更深。

日常PS:DB全局递增Sequence之前觉得不起眼,谁都能想到的东西,现在来看感觉真是一个NB的设计,大大简化了Snapshot和Transaction的实现。

【Rocksdb实现分析及优化】事务之Pessimistic ①相关推荐

  1. mysql性能优化-慢查询分析、优化索引和配置

    目录 一.优化概述 二.查询与索引优化分析 1性能瓶颈定位 Show命令 慢查询日志 explain分析查询 profiling分析查询 2索引及查询优化 三.配置优化 1)      max_con ...

  2. mysql io 100_MySQL服务器 IO 100%的分析与优化方案

    压力测试过程中,如果因为资源使用瓶颈等问题引发最直接性能问题是业务交易响应时间偏大,TPS逐渐降低等.而问题定位分析通常情况下,最优先排查的是监控服务器资源利用率,例如先用TOP 或者nmon等查看C ...

  3. mysql io_MySQL服务器 IO 100%的分析与优化方案

    前言 压力测试过程中,如果因为资源使用瓶颈等问题引发最直接性能问题是业务交易响应时间偏大,TPS逐渐降低等.而问题定位分析通常情况下,最优先排查的是监控服务器资源利用率,例如先用TOP 或者nmon等 ...

  4. 让oracle跑得更快——oracle 10g性能分析与优化思路,[让Oracle跑得更快.Oracle.10g性能分析与优化思路]概要1.doc...

    [让Oracle跑得更快.Oracle.10g性能分析与优化思路]概要1 在线事务(OLTP) 在线分析(OLAP) 在Oracle数据库中,凡是分配了存储空间的,都称为段,所有段并不一定指的是表,也 ...

  5. CPython解释器性能分析与优化

    原文来自微信公众号"编程语言Lab":CPython 解释器性能分析与优化 搜索关注 "编程语言Lab"公众号(HW-PLLab)获取更多技术内容! 欢迎加入 ...

  6. 性能分析--视图优化

    性能分析/性能优化-视图优化 优化概述 流畅的操作体验 卡顿 稳定性 内存泄漏,崩溃 省电省流量 代码质量,逻辑 安装包小 安装包过大 UI优化 View层级相同的情况下,尽量使用LinearLayo ...

  7. 基于linux服务器的性能分析与优化

    基于linux服务器的性能分析与优化 方面:硬件系统软件网络 现象:系统不稳定相应速度慢 web无法打开打开速度慢 方案:硬件故障更换硬件或升级硬件 系统问题修改系统参数和配置 软件问题修改和升级软件 ...

  8. RK2908开机时间分析及优化

    1    RK2908开机时间分析和优化文档背景概述      目前由于RK2908开机时间比较长(平均35s)可能对一体机项目的整体体验造成影响,所以需要对RK2908开机整个流程进行分析,是否有优 ...

  9. 三个步骤分析网站优化程度

    三个步骤分析网站优化程度 分析是做好seo优化必备工作之一!只有事前做好了充足的分析工作才能让网站的后期优化工作畅通无阻!那么如何细致的分析网站呢?!下面福州seo维思和大家分享三个步骤分析网站优化程 ...

  10. 排序算法 | 快速排序,算法的图解、实现、复杂度和稳定性分析与优化

    今天讲解一下快速排序算法的原理以及实现.复杂度和稳定性分析与优化 目录 1 快速排序的原理 2 快速排序代码实现 3 复杂度和稳定性分析.优化 4 习题练习 1 快速排序的原理 快速排序是所有内部排序 ...

最新文章

  1. nuxt 如何引入js_nuxtjs如何在单独的js文件中引入store和router
  2. 怎么解释三线圈直流电机工作原理更好?
  3. 女性养生需知的16条健康戒律
  4. date样式找不到_涡轮+国VI排放,顶配售价不到12万,家用轿车看它准没错
  5. Vue.js – 基于 MVVM 实现交互式的 Web 界面
  6. L2-020. 功夫传人
  7. 高等数学上-赵立军-北京大学出版社-题解-练习5.3
  8. (jdbc和cmd)sqlite数据迁入mysql(导入导出)
  9. linux 离线安装node.js,Linux上离线安装node.js、Newman、newman-reporter-html
  10. Qt QSettings读写ini时 General 读不出来值
  11. 如何准备数学建模,那些数学建模中我们踩过的坑(万字自述国赛美赛经历)
  12. H3C-H3CNE 华三网络工程师从入门到精通 自学视频课程[肖哥]-肖宗鹏-专题视频课程...
  13. 计算机无法安装网卡驱动,网卡驱动安装不了,详细教您解决网卡驱动安装不了...
  14. 华为 网络 链路捆绑
  15. aisell_EasyPOI 数据导入导出
  16. WPF DataGrid MVVM 绑定 SelectedCells
  17. 商务网站建设与维护【11】
  18. NUC安装CentOS-8.2
  19. TEX:文档的布局与组织
  20. 网站设计开发的步骤和方法!

热门文章

  1. Springboot 整合百度地图 API
  2. [转载]推荐两篇文章
  3. 帝国php改密码后登录不进去,帝国cms后台不能登录的解决方法
  4. 俄罗斯互联网的BAT
  5. 从零开始用人工智能预测股票(二、数据加工)
  6. 非对称TSP问题(asymmetric travelling salesman problem)与对称TSP问题的转换
  7. 软件测试人员是选择大公司好,还是选择小公司更好
  8. 使用 Typora 画图
  9. 安卓 ANR 原因,解决方法
  10. 如何root安卓手机_超级神器——安卓端的手机虚拟机,手机中的手机(支持root,xp框架)...