当前位置: 首页 > 编程日记 > 正文

Rocksdb 事务(一): 隔离性的实现

文章目录

      • 前言
      • 1. 隔离性
      • 2. Rocksdb实现的隔离级别
        • 2.1 常见的四种隔离级别
        • 2.2 Rocksdb 支持的隔离级别及基本实现
          • 2.2.1 ReadComitted 隔离级别的测试
          • 2.2.2 ReadCommitted的实现
          • 2.2.3 RepeatableRead的实现
          • 2.2.4 事务并发处理
      • 3. 一些总结

前言

Rocksdb 作为单机存储引擎,已经非常成熟得应用在了许多分布式存储(CEPH, TiKV),以及十分通用的数据库之上(mysql, mongodb, Drango等),所以Rocksdb本身需要能够实现ACID属性,尤其是其中的不同的隔离级别才能够作为一个公共的存储组件。本节,结合rocksdb6.4.6代码以及官网wiki来梳理一下rocksdb的事务管理以及隔离性的实现。


1. 隔离性

ACID中的隔离性意味着 同时执行的事务之间是互不影响的。这个时候,在一些同时执行事务的场景下,就需要有针对事务的隔离级别,来满足客户端针对存储系统的要求。
在这里插入图片描述
图1.1 两个客户之间的竞争状态同时递增计数器

如上图1.1,user1和user2对数据库的访问

  • user1先从数据库中get,得到了42。完成get事务之后拿着get的结果+1,将43set到数据库中
  • user1下发set的同时user2从数据库中get,同样得到了42,也进行42+1 的操作
  • 两者的事务都是各自隔离的,且是串行执行互不影响(user2的get并无法同时访问user1 set的结果),保证了结果对用户的正确性

在这里插入图片描述
图1.2 违反了隔离性:一个事务读取了另一个事务执行的结果

如上图中,user2将user1的insert过程中的 hello 作为了自己的输入,即一个事务能够读取另一个事务未被执行状态。这个过程被称作脏读

2. Rocksdb实现的隔离级别

2.1 常见的四种隔离级别

  • ReadUncommited 读取未提交内容,所有事务都可以看到其他未提交事务的执行结果,存在脏读
  • ReadCommited 读取已提交内容,事务只能看到其他已提交事务的更新内容,多次读的时候可能读到其他事务更新的内容
  • RepeatableRead 可重复读,确保事务读取数据时,多次操作会看到同样的数据行(innodb引擎使用快照隔离来实现)。
  • Serializability 可串行化,强制事务之间的执行是有序的,不会互相冲突。

2.2 Rocksdb 支持的隔离级别及基本实现

2.2.1 ReadComitted 隔离级别的测试

Rocksdb支持ReadCommited的隔离级别,它能够提供两个保障

  • 从数据库读时,只能看到已提交的数据(没有脏读(dirty reads):不同事务之间能够读到对方未提交的内容
  • 写入数据库时,只会覆盖已经写入的数据(没有脏写(dirty writes):不同事务之间的写在提交之前能够相互覆盖

先看一下简单的测试代码:

  //支持事务的方式打开rocksdbStatus s = TransactionDB::Open(options, txn_db_options, kDBPath, &txn_db);// 开启事务操作,定义当前事务为t1Transaction* txn = txn_db->BeginTransaction(write_options);assert(txn);// 先下发一个t1的读操作s = txn->Get(read_options, "abc", &value);assert(s.IsNotFound());// 再下发一个t1的写操作(注意此时是在同一个事务t1内部,现在只是不同的操作)s = txn->Put("abc", "def");assert(s.ok());// 在当前事务外部下发一个t2读操作,确认是否存在脏读(txn_db->Get是一个不同于当前事务的独立事务,t2)s = txn_db->Get(read_options, "abc", &value);std::cout << "t2 Get result " << s.ToString() << std::endl;// 在当前事务外部下发一个t3写操作,这里更新的是不同的key,如果更新相同的key。则t1事务commit的时候会报错//s = txn_db->Put(write_options, "xyz", "zzz");s = txn_db->Put(write_options, "abc", "zzz");std::cout << "t3 Put result " << s.ToString() << std::endl;// 提交t1事务s = txn->Commit();assert(s.ok());//提交之后再get一次s = txn_db->Get(read_options, "abc", &value);std::cout << "t4 Get result after commit: " << value << std::endl;delete txn;

输出如下:

# 两个事务Get时不可见对方未提交内容,不存在脏读
t2 Get result NotFound: 
# 在提交之后能够发现Set的结果也并未生效,不存在脏写,切Put相同的key发现加锁超时
t3 Put result Operation timed out: Timeout waiting to lock key
# t4在t1提交之后get t1的结果的时候能够看到t1的结果生效
t4 Get result after commit def

通过这个简单的测试代码以及对应的输出结果,我们能够看出当前Rocksdb已经能够支持ReadCommited的隔离级别,不存在脏读,同时脏写实现看起来像是通过加锁来避免的。

2.2.2 ReadCommitted的实现

简单描述一下该隔离特性,Rocksdb的一个事务操作是通过Rocksdb内部WriteBatch实现的,针对不同事务Rocksdb会为其分配对应的WriteBatch,由WriteBatch来处理具体的写入。同时针对同一个事务的读操作,会优先从当前事务的WriteBatch中读,来保证能够读到当前写操作之前未提交的更新。提交的时候则依次写入WAL和memtable之中,保证ACID的原子性和一致性。

大体的流程如下2.1图
在这里插入图片描述
图2.1 通过WriteBatch实现 ReadCommitted

以上过程结合我们的测试代码,可以有两种方式来进行

  • 显式得通过事务的方式写入,提交
    Transaction* txn = txn_db->BeginTransaction(write_options);
    txn->Get(read_option,"abc",&value);
    txn->Put("abc","value1");
    txn->commit();
    
  • 直接通过TransactionDB生成一个auto transaction,transactionDB会将这个单独的操作封装成事务,并自动commit。
    txn_db->Get(read_options, "abc", &value);
    txn_db->Put(write_options, "abc", "zzz");
    

一种transactionDB这里没有锁的冲突检查,而我们使用transaction的方式进行Put,实验代码中也能看到有锁的超时检查.

2.2.3 RepeatableRead的实现

可重复读是指Rocksdb重复多次读取数据的时候,能够访问到预期的数值,而不会被其他事务的更新操作影响。
这里的可重复读其实在SQL指定标准之前是用快照隔离来描述的,通用的关系型数据库都使用MVCC机制来进行多版本管理,多版本的访问也就是通过快照来进行的。

Rocksdb这里的实现是通过为每一个写入的key-value请求添加一个LSN(Log Sequence Number),最初是0,每次写入+1,达到全局递增的目的。同时当实现快照隔离时,通过Snapshot设置其与一个lsn绑定,则该snapshot能够访问到小于等于当前lsn的k-v数据,而大于该lsn的key-value是不可见的。

相关代码在snapshot_impl.h之中

class SnapshotImpl : public Snapshot {public://lsn numberSequenceNumber number_;  ......SnapshotImpl* prev_;SnapshotImpl* next_;SnapshotList* list_;                 // 链表头指针int64_t unix_time_; //时间戳// 用于写冲突的检查bool is_write_conflict_boundary_;
};

snapshot可以有多个,它的创建和删除是通过操作一个全局的双向链表来进行,天然得根据创建的时间来进行排序SetSnapShot()函数创建一个快照。
快照隔离的测试代码如下:

  // 通过设置set_snapshot=true,来在BeginTransaction的时候就设置一个快照value = "def";txn_options.set_snapshot = true;txn = txn_db->BeginTransaction(write_options, txn_options);//读取一个快照const Snapshot* snapshot = txn->GetSnapshot();// 重新生成一个写入事务db->Put(write_options, "abc", "xyz");// 通过读取的snapshot,来访问指定的keyread_options.snapshot = snapshot;// 通过GetForUpdate来进行读操作,这个函数锁定多个事务操作,即也会让之前的Put加入到WriteBatch中。s = txn->GetForUpdate(read_options, "abc", &value);assert(value == "def");// 提交事务s = txn->Commit();// 新生成的事务可能与读操作冲突,不过这里用了GetForUpdate就不会产生冲突了assert(s.IsBusy());delete txn;// 释放snapshotread_options.snapshot = nullptr;snapshot = nullptr;

其中用到了GetForUpdate函数,区别于Get接口,GetForUpdate对读记录加独占写锁,保证后续对该记录的写操作是排他的。保证了多个事务的操作都能够被GetForUpdate锁定,而不是一个GetForUpdate成功,其他的失败。

2.2.4 事务并发处理

通过对以上事务的隔离性的分析,能够总结出以下几种事务并发时Rocksdb的处理方式。

  1. 如果事务都是读操作,不论操作之间是否有交集,都不会触发锁定
  2. 如果事务冲包含读、写操作
    • 所有的读事务都不会触发锁定,读的结果与snapshot请求相关
    • 写事务之间不存在交集,则不会锁定
    • 写事务之间存在交集,如果此时设置了snapshot,则会串行提交;如果没有设置snapshot,则只执行第一个写操作,其他的操作都会失败。

3. 一些总结

本文通过探索Rocksdb的事务机制 以及描述了事务的基本实现,读提交以及可重复读的特性基本能够让其作为单机存储引擎底座,来适配分布式存储中的ACID特性。
同时还有一些更加细粒度的实现需要探索:

  • 像针对写事务的交集如何进行冲突检测以及如何通过锁机制解决冲突。
  • 默认使用的悲观锁以及可以显式调用的乐观锁 在隔离性的几个级别中是如何生效的。
  • 还有2PC(Two-Pharse-Commit)的实现机制,以及2PC上层的应用场景

不得不说一个公共的存储底座实现是真的不容易,后续将尝试手写一些隔离级别,来加深对分布式锁的理解。

相关文章:

linux镜像修改密码,OpenStack 镜像修改密码方案

现在各大linux厂商&#xff0c;其实已经有专门给openStack提供的镜像&#xff0c;不过国内的朋友&#xff0c;不太习惯老外做镜像的方式&#xff0c;经常问密码是多少。本博客提供几种修改密码方案&#xff0c;仅供参考。前言对OpenStack云主机来说&#xff0c;有以下几种方式登…

专题8-Linux系统调用

1.从用户空间到内核空间的时候需要进行系统调用 2.系统调用通过swi指令实现&#xff0c;这个时候会有一个参数&#xff0c;存放到r7寄存器&#xff0c;用来指定要调用的内核态函数。 3.一般内核态的实际函数可以再任意的.c文件实现&#xff0c;但是必须在call.S里面加上相应的函…

企业架构研究总结(39)——TOGAF架构能力框架之架构委员会和架构合规性

3. 架构委员会 正如前面所说&#xff0c;一个用来对架构治理策略的实现进行监督的跨组织的架构委员会是架构治理策略成功的主要要素之一。架构委员会应该能够代表所有主要干系人的需求&#xff0c;并且通常还需要对整个架构的审查及维护活动负有高级行政职责。通常来讲&#xf…

linux c编程之fcntl

fcntl可实现对指定文件描述符的各种操作&#xff0c;其函数原型如下&#xff1a; int fcntl(int fd, int cmd, ... /* arg */ ); 其中&#xff0c;操作类型由cmd决定。cmd可取如下值&#xff1a; F_DUPFD&#xff1a;复制文件描述符F_DUPFD_CLOEXEC&#xff1a;复制文件描述符&…

Rocksdb iterator和snapshot 接口

Rocksdb提供迭代器来来访问整个db中的数据&#xff0c;就像STL中的迭代器功能一样&#xff0c;用来访问容器中的具体的数据。 访问形式以及访问接口有如下几种&#xff1a; 遍历所有的key-value//打开db&#xff0c;并初始化一个迭代器指针 rocksdb::Iterator* it db->Ne…

原生js自动完成 [转]

本来想用jquery的autocomplete的,可是需求有些变化,打算改源码,进了源码发现,改起来要的时间太长了,毕竟不是自己写的,改起来慢,在网上开始大肆搜罗资料,终于找到了类似的 本文转自http://www.cnblogs.com/jaiho/archive/2011/02/28/js_autocomplete.html 完成有以下功能&#…

linux 内存管理slab源码,Linux内核源代码情景分析-内存管理之slab-回收

图 1我们看到空闲slab块占用的若干页面&#xff0c;不会自己释放&#xff1b;我们是通过kmem_cache_reap和kmem_cache_shrink来回收的。他们的区别是&#xff1a;1、我们先看kmem_cache_shrink&#xff0c;代码如下&#xff1a;int kmem_cache_shrink(kmem_cache_t *cachep){if…

Vlookup的兄弟lookup讲解

Vlookup是查找函数&#xff0c;lookup也是&#xff0c;但它主要是充当模糊查找。最常见的例子就是算个税等级和成绩区间。我们创建源数据如图结果要求的是300&#xff0c;500&#xff0c;50对应的积分情况。因为数据量大&#xff0c;这里只取小部分。运用lookup函数&#xff0c…

6.python探测Web服务质量方法之pycurl模块

才开始学习的时候有点忽略了这个模块&#xff0c;觉得既然Python3提供了requests库&#xff0c;为什么多此一举学习这个模块。后来才发现pycurl在探测Web服务器的时候的强大。 pycurl是一个用c语言写的libcurl Python实现&#xff0c;支持的操作协议有FTP&#xff0c;HTTP&…

Rocksdb DeleteRange实现原理

文章目录1. 基本介绍2. 两种接口使用及简单性能对比3. DeleteRange 的基本实现3.1 写流程的实现3.2 读流程的实现 -- skyline算法以下涉及到的代码都是基于rocksdb 6.4.0版本进行描述的 1. 基本介绍 DeleteRange接口的设计是为了代替传统的删除一个区间[start,end) 内的key-va…

题目1460:Oil Deposit

题目描述&#xff1a;The GeoSurvComp geologic survey company is responsible for detecting underground oil deposits. GeoSurvComp works with one large rectangular region of land at a time, and creates a grid that divides the land into numerous square plots. I…

linux做预警机制,预警通告:Linux内核中TCP SACK机制远程DoS

漏洞描述2019年6月18日&#xff0c;RedHat官网发布报告&#xff1a;安全研究人员在Linux内核处理TCPSACK数据包模块中发现了三个漏洞&#xff0c;CVE编号为CVE-2019-11477、CVE-2019-11478和CVE-2019-11479&#xff0c;其中CVE-2019-11477漏洞能够降低系统运行效率&#xff0c;…

C# 使用xsd文件验证XML 格式是否正确

//创建xmlDocument XmlDocument doc new XmlDocument(); //创建声明段 如<?xml version"1.0" encoding"utf-8" ?> doc.AppendChild(doc.CreateXmlDeclaration("1.0", "utf-8", null)); //创建一个根节点 KYTResults Xm…

[蓝桥杯]PREV-23.历届试题_数字游戏

问题描述栋栋正在和同学们玩一个数字游戏。游戏的规则是这样的&#xff1a;栋栋和同学们一共n个人围坐在一圈。栋栋首先说出数字1。接下来&#xff0c;坐在栋栋左手边的同学要说下一个数字2。再下面的一个同学要从上一个同学说的数字往下数两个数说出来&#xff0c;也就是说4。…

Mac 上使用 Clion 阅读C++源码的一些操作

一直在尝试一些写代码方便&#xff0c;阅读代码也很方便的工具&#xff0c;因为使用的是Mac&#xff0c;所以阅读源码上面sourceInsight就没办法用了。 从vscode – sublime – clion 想要可配置性强一点&#xff0c;软件轻一点&#xff0c;也能提供足够的便捷操作&#xff0c…

c语言 字母 八进制表示'/1011',C语言C语言第一课:C语言概述为什么学习C语言怎样学习C语言.DOC...

[摘要]C语言 第一课&#xff1a; C语言概述 为什么学习C语言 怎样学习C语言 参考资料 ----------------------------------------------------------- 入门经典《C语言程序设计》 谭浩强 清华 《汇编语言》 王爽 《The C programming language》 机械工业 《C Primer Plus》 60…

Discuz! X2.5 添加自定义数据调用模块(简单方法)

转&#xff1a;http://521-wf.com/archives/46.html Discuz! X2.5 添加自定义数据调用模块&#xff08;简单方法&#xff09; Discuz!X系列的diy功能还是相当不错的&#xff0c;在对其进行二次开发的过程中&#xff0c;或许需要加入新的数据调用模块&#xff0c;这样可以使你开…

Cassandra数据模型设计最佳实践

2019独角兽企业重金招聘Python工程师标准>>> 本文是Cassandra数据模型设计第一篇&#xff08;全两篇&#xff09;&#xff0c;该系列文章包含了eBay使用Cassandra数据模型设计的一些实践。其中一些最佳实践我们是通过社区学到的&#xff0c;有些对我们来说也是新知识…

矩阵相关概念的物理意义

参考链接&#xff1a; 矩阵乘法的本质是什么&#xff1f; 条件数 病态矩阵与条件数&#xff08;&& 与特征值和SVD的关系&#xff09;矩阵的物理意义&#xff1a;https://blog.csdn.net/NightkidLi_911/article/details/38178533https://blog.csdn.net/NightkidLi_911/a…

Linux 下 进程运行时内部函数耗时的统计 工具:pstack,strace,perf trace,systemtap

简单记录一些 在linux下 统计进程内部函数运行耗时的统计工具&#xff0c;主要是用作性能瓶颈分析。当然以下工具除了pstack功能单一之外&#xff0c;其他的工具都非常强大&#xff0c;这里仅仅整理特定场景的特定用法&#xff0c;用作协同分析。 以下工具需要追踪具体的进程&…

c语言作业扩展名通常为什么,C语言的源程序通常的扩展名是( )

C语言的源程序通常的扩展名是( )更多相关问题【C20】A&#xff0e;asB&#xff0e;afterC&#xff0e;untilD&#xff0e;whenAlthough I spoke to her about the matter several times, she took little ______ of what I s“以质取胜”战略包括三个方面内容&#xff0c;分别是…

VS中C#读取app.config数据库配置字符串的三种方法(转)

关于VS2008或VS2005中数据库配置字符串的三种取法 VS2008建立Form程序时,如果添加数据源会在配置文件 app.config中自动写入连接字符串,这个字符串将会在你利用DataSet,SqlDataAparter,SqlConnection等控件时如影随行地提示你让去选择,或者是新建字符串。如果要用代码的方式取得…

获取线程中抛出的异常信息

1 ScheduledExecutorService service Executors.newScheduledThreadPool(10);2 // 从现在开始delay毫秒之后&#xff0c;每隔一天执行一次&#xff0c;转换为毫秒3 // service.scheduleAtFixedRate(this, delay, period, TimeUnit.MILLISECONDS);4 …

浅谈批处理获取管理员运行权限的几种方法

很多用了Win10版本系统的人都会发现&#xff0c;Windows对程序的运行权限是控制得更加严格了&#xff0c;即使你将UAC控制放至最低&#xff0c;如果没有特别赋予外来程序管理员运行权限的话&#xff0c;很多程序都会运行出错&#xff0c;包括很多用于系统维护的批处理程序由于运…

使用 sched_setaffinity 将线程绑到CPU核上运行

linux 提供CPU调度函数&#xff0c;可以将CPU某一个核和指定的线程绑定到一块运行。 这样能够充分利用CPU&#xff0c;且减少了不同CPU核之间的切换&#xff0c;尤其是在IO密集型压力之下能够提供较为友好的性能。 通过sched_setaffinity 设置 CPU 亲和力的掩码&#xff0c;从…

Objective C内存管理之理解autorelease------面试题

Objective C内存管理之理解autorelease Autorelease实际上只是把对release的调用延迟了&#xff0c;对于每一个Autorelease&#xff0c;系统只是把该Object放入了当前的Autorelease pool中&#xff0c;当该pool被释放时&#xff0c;该pool中的所有Object会被调用Release。 &…

c语言子程序return,c语言return返回到哪

c语言return返回到哪c语言return&#xff0c;返回给了上一级&#xff0c;比如一个递归程序&#xff0c;从第三层返回到第二层&#xff1b;又比如一个普通的子程序&#xff0c;那就返回到主程序中去。主程序中return返回给了操作系统。比如下面一个c程序int sum(int a, int b) {…

有关 schema

2019独角兽企业重金招聘Python工程师标准>>> 主要分析2点 &#xff1a;schema含义 以及 多schema下的XA处理 A schema is a collection of database objects (used by a user.). Schema objects are the logical structures that directly refer to the database’…

关于查询ios的app更新的历史版本记录

https://www.qimai.cn 推荐七麦数据 可以查询app的各种版本更新内容 由于历史久远忘记了自己app第一次上架的时间 通过这个可以查询 转载于:https://www.cnblogs.com/ccw-congcong/p/10593917.html

关于 Rocksdb 性能分析 需要知道的一些“小技巧“ -- perf_context的“内功” ,systemtap、perf、 ftrace的颜值

文章目录内部工具包含头文件接口使用核心指标Perf ContextIOStats Context外部工具Systemtap 工具Perf工具Ftrace 工具2020.8.20 23:23 &#xff0c;又到了夜深人静学习时&#xff0c;不断得思考总结总会让繁忙一天的大脑得到舒缓。作为单机存储引擎&#xff0c;Rocksdb总会被嵌…