badgerdb里面的事务

事务的ACID

  • A 原子性(Atomicity)
    多步骤操作,只能是两种状态,要么所有的步骤都成功执行,要么所有的步骤都不执行,举例说明就是小明向小红转账30元的场景,拆分成两个步骤,步骤1:小明减30元。步骤2:小红加30元。步骤1和2必须同时执行成功或失败,不能只执行其中的一个步骤。

  • C 一致性(Consistency)
    其实和原子性一样

  • I 隔离性(Isolation)
    多个事务执行时,不能受并发的事务的影响,后面会详细的说隔离级别

  • D 持久性(Durability)
    事务一旦提交落盘后,数据不会因为程序异常或断电丢失数据

隔离性

  1. 读未提交(Read uncommitted)
  2. 读已提交(Read committed)
  3. 可重复读(Repeatable read)
  4. 序列化(Serializable )
    从上到下,四个级别的隔离性依次变强,性能依次变差

在这里插入图片描述
读未提交 :对应脏读,在本事务的线段内,会读到其他线段的中间状态。
读已提交:对应不可重复读,比上个好一些。该级别下不能读到其他事务的未提交状态。但如上图,如果事务 t2 在执行时,多次读某个记录 x 的状态,在事务 t1 未启动前,发现 x = 2,在事务 t1 提交后,发现 x = 3,这便出现了不一致。
可重复读:如上图,事务 t2 在整个执行期间,多次读取数据库 x 的状态,无论他事务(如 t1)是否改变 x 状态并提交,事务 t2 都不会感觉到。但是会存在幻读的风险。怎么理解呢?最关键的原因在于写并发。因为读不到,不代表其他事务的影响不存在。比如事务 t2 开始时,通过查询发现 id = “qtmuniao” 的记录为空,于是创建了 id=“qtmuniao” 的记录,然而在提交时,发现报错说该 id 已经存在。这可能是因为有一个开始的比较晚的事务 t2,也创建了一个 id=“qtmuniao” 的记录,但是先提交了。于是用户就郁闷了,明明你说没有,但我写又报错,难道我出现幻觉了?这就太扯淡了,但是此级别就只能做到这样了。反而,因为兼顾了性能和隔离性,他是大多数据库的默认级别。
序列化:最简单的实现办法就是一把锁来串行化所有的事务boltdb就是这么做的。badgerdb在此基础上如果能提高并发,做很多优化。

badger 的序列化SSI

badgerdb 的事务主要依靠多个tnx结构体和全局的一个oracle结构体来维护

type Txn struct {readTs   uint64commitTs uint64
}type oracle struct {nextTxnTs   uint64
}

每一个txn都有readTs和commitTs ,其中全局的o.nextTxnTs只有获得提交时间戳的时候才加1,如果多个事务并发,任何一个事务都还没有提交的时候,这些事务获得的readTs 是一样的

	var readTs uint64o.Lock()readTs = o.nextTxnTs - 1//txn 的readTso.readMark.Begin(readTs)o.Unlock()ts = o.nextTxnTso.nextTxnTs++//事务获得了提交时间后,再把nextTxnTs+1o.txnMark.Begin(ts)

创建一个事务的时候,要进行授时txn.readTs = db.orc.readTs(),这个时间是一个递增的序列,接下来主要来分析一下db.orc.readTs()这个函数,获得readTs后会等待readTs这个时间戳提交的事务彻底写入LSM tree后才返回,保证了不会脏读,不会读到其他未提交的事务,和不可重复读

func (o *oracle) readTs() uint64 {if o.isManaged {panic("ReadTs should not be retrieved for managed DB")}var readTs uint64o.Lock()readTs = o.nextTxnTs - 1o.readMark.Begin(readTs)o.Unlock()// Wait for all txns which have no conflicts, have been assigned a commit// timestamp and are going through the write to value log and LSM tree// process. Not waiting here could mean that some txns which have been// committed would not be read.y.Check(o.txnMark.WaitForMark(context.Background(), readTs))return readTs
}

badgerdb 解决幻读

在上文描述的可重复读,出现的幻读,badgerdb解决幻读和不可重复读的方法就是事务t2放弃提交,给用户层返回ErrConflict错误,让用户层稍后再试。

先找到代码中报ErrConflict的地方,是获取CommitTs的时候报的错误

func (txn *Txn) commitAndSend() (func() error, error) {orc := txn.db.orc// Ensure that the order in which we get the commit timestamp is the same as// the order in which we push these updates to the write channel. So, we// acquire a writeChLock before getting a commit timestamp, and only release// it after pushing the entries to it.orc.writeChLock.Lock()defer orc.writeChLock.Unlock()commitTs, conflict := orc.newCommitTs(txn)if conflict {return nil, ErrConflict}
}

进去看orc.newCommitTs(txn)

func (o *oracle) newCommitTs(txn *Txn) (uint64, bool) {o.Lock()defer o.Unlock()if o.hasConflict(txn) {return 0, true}
}

再看o.hasConflict(txn);
txn.reads 是被txn.addReadKey进行修改的;
committedTxn.conflictKeys 是txn.modify() 修改的,txn.modify()是txn.Set或txn.Detele调用的;
总结下来就是:当前事务如果读过的key,在当前事务的readTs后有在其他的事务对这些读到过的key做过修改,那么本次事务就是有冲突的

// hasConflict must be called while having a lock.
func (o *oracle) hasConflict(txn *Txn) bool {if len(txn.reads) == 0 {return false}for _, committedTxn := range o.committedTxns {// If the committedTxn.ts is less than txn.readTs that implies that the// committedTxn finished before the current transaction started.// We don't need to check for conflict in that case.// This change assumes linearizability. Lack of linearizability could// cause the read ts of a new txn to be lower than the commit ts of// a txn before it (@mrjn).if committedTxn.ts <= txn.readTs {continue}for _, ro := range txn.reads {if _, has := committedTxn.conflictKeys[ro]; has {return true}}}return false
}

https://zhuanlan.zhihu.com/p/395229054

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/46724.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Windows系统编程】06.HotFixHook与进程通信(详解HotFixHook)

上一讲讲到的InlineHook&#xff0c;每次Hook的时候&#xff0c;都要读写两次内存&#xff08;先Hook&#xff0c;再还原&#xff09;这种Hook方式&#xff0c;性能比较低&#xff0c;今天我们讲的这种Hook方式&#xff0c;可以说是InlineHook的升级版本 HotFix&#xff08;热…

公众号11周年,终于向公域流量打开了大门

是的&#xff0c;在这篇文章要发布之前&#xff0c;看了下日期&#xff0c;才惊觉明天就是公众号11周年了。 时间真的过得飞快&#xff0c;总觉得10周年刚过不久。 已经11年的公众号&#xff0c;或许少了很多关注&#xff0c;或许很多目光也被视频号夺去了。 但让人欣喜的是…

前馈神经网络dropout实例

直接看代码。 &#xff08;一&#xff09;手动实现 import torch import torch.nn as nn import numpy as np import torchvision import torchvision.transforms as transforms import matplotlib.pyplot as plt#下载MNIST手写数据集 mnist_train torchvision.datasets.MN…

基于开源模型搭建实时人脸识别系统(四):人脸质量

续人脸识别实战之基于开源模型搭建实时人脸识别系统&#xff08;三&#xff09;&#xff1a;人脸关键点、对齐模型概览与模型选型_CodingInCV的博客-CSDN博客 不论对于静态的人脸识别还是动态的人脸识别&#xff0c;我们都会面临一个问题&#xff0c;就是输入的人脸图像的质量可…

使用 OpenTelemetry 构建可观测性 04 - 收集器

在之前的博文中&#xff0c;我们讨论了如何使用 SDK 和链路追踪生产者来导出进程中的遥测数据。尽管有多种类型的导出器可供选择&#xff0c;但其中一个常见的目标是将数据导出到 OpenTelemetry Collector。本篇文章将深入探讨收集器以及如何使用它。 选 OTel Collector 还是…

Eduma主题 - 线上教育WordPress主题/网站

Eduma主题 – 线上教育WordPress主题是为教育网站、LMS、培训中心、课程中心、学院、大学、学校、幼儿园而制作的。基于我们使用以前的主题eLearning WP构建WordPress LMS的经验&#xff0c;Education WP是下一代&#xff0c;也是围绕WordPress最好的教育主题之一&#xff0c;它…

一个炫酷的头像悬停效果 2

基于上次翻译的 &#x1f525;&#x1f525;一个炫酷的头像悬停效果 收获了不少同学的喜欢&#xff0c;原作者近期进行了优化升级。本文将升级后的核心实现过程进行梳理讲解&#xff0c;如果没看过第一期的推荐先看看第一期的实现过程。升级后的效果如下图所示。 gif动画效果如…

Baklib是比语雀、Notion、石墨文档更好用的在线知识库管理工具

在当今信息爆炸的时代&#xff0c;如何高效地管理和利用知识成为了每个人都面临的问题。在线知识库管理工具应运而生&#xff0c;帮助用户整理、存储和共享知识。在这篇文章中&#xff0c;我将介绍一个更好用的在线知识库管理工具——Baklib&#xff0c;并探讨它相对于其他知识…

Spring系列篇--关于IOC【控制反转】的详解

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于Spring的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.什么是Spring 二.Spring的特点 三.什…

【O2O领域】Axure外卖订餐骑手端APP原型图,外卖众包配送原型设计图

作品概况 页面数量&#xff1a;共 110 页 兼容软件&#xff1a;Axure RP 9/10&#xff0c;不支持低版本 应用领域&#xff1a;外卖配送、生鲜配送 作品申明&#xff1a;页面内容仅用于功能演示&#xff0c;无实际功能 作品特色 本品为外卖订餐骑手端APP原型设计图&#x…

Datawhale Django 后端开发入门 Task05 DefaultRouter、自定义函数

一、DefaultRouter是Django REST framework中提供的一个路由器类&#xff0c;用于自动生成URL路由。路由器是将URL与视图函数或视图集关联起来的一种机制。Django REST framework的路由器通过简单的配置可以自动生成标准的URL路由&#xff0c;从而减少了手动编写URL路由的工作量…

Redis Lua脚本执行原理和语法示例

Redis Lua脚本语法示例 文章目录 Redis Lua脚本语法示例0. 前言参考资料 1. Redis 执行Lua脚本原理1.1. 对Redis源码中嵌入Lua解释器的简要解析&#xff1a;1.2. Redis Lua 脚本缓存机制 2. Redis Lua脚本示例1.1. 场景示例1. 请求限流2. 原子性地从一个list移动元素到另一个li…

基于郊狼算法优化的BP神经网络(预测应用) - 附代码

基于郊狼算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于郊狼算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.郊狼优化BP神经网络2.1 BP神经网络参数设置2.2 郊狼算法应用 4.测试结果&#xff1a;5.Matlab代码 摘要…

【深入解析:数据结构栈的魅力与应用】

本章重点 栈的概念及结构 栈的实现方式 数组实现栈接口 栈面试题目 概念选择题 一、栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底。栈中的数…

指针(一)【C语言进阶版】

大家好&#xff0c;我是深鱼~ 【前言】&#xff1a; 指针的主题&#xff0c;在初阶指针章节已经接触过了&#xff0c;我们知道了指针的概念&#xff1a; 1.指针就是个变量&#xff0c;用来存放地址&#xff0c;地址的唯一标识一块内存空间&#xff08;指针变量&#xff09;&a…

【云原生|Docker系列第3篇】Docker镜像的入门实践

欢迎来到Docker入门系列的第三篇博客&#xff01;在前两篇博客中&#xff0c;我们已经了解了什么是Docker以及如何安装和配置它。本篇博客将重点介绍Docker镜像的概念&#xff0c;以及它们之间的关系。我们还将学习如何拉取、创建、管理和分享Docker镜像&#xff0c;这是使用Do…

jenkins同一jar包部署到多台服务器

文章目录 安装插件配置ssh服务构建完成后执行 没有部署过可以跟这个下面的步骤先部署一遍&#xff0c;我这篇主要讲jenkins同一jar包部署到多台服务器 【Jenkins】部署Springboot项目https://blog.csdn.net/qq_39017153/article/details/131901613 安装插件 Publish Over SSH 这…

stm32g070的PD0/PD2 PA8和PB15

目前在用STM32G070做项目&#xff0c;其中PD2TIMER3去模拟PWM&#xff0c;PD0用作按键检测&#xff0c;测试发现PD0低电平检测没有问题&#xff0c;高电平检测不到&#xff0c;电路图如下图所示&#xff1a; 用万用表测试电平&#xff0c;高电平1.0V左右&#xff0c;首先怀疑硬…

rust踩雷笔记(4)——刷点Vec相关的题(持续更新)

俗话说&#xff0c;孰能生巧&#xff0c;今天是第六天接触Rust&#xff0c;感觉基础语法和特性没什么问题了&#xff08;当然如果你整天都学这个可能2天半就够了&#xff09;&#xff0c;但是想达到熟练使用&#xff0c;还需要刷点题。算法我相信能来看rust博客的人都是大牛&am…

【项目实践】基于LSTM的一维数据扩展与预测

基于LSTM的一维数据拟合扩展 一、引(fei)言(hua) 我在做Sri Lanka生态系统服务价值计算时&#xff0c;中间遇到了一点小问题。从世界粮农组织(FAO)上获得Sri Lanka主要农作物产量和价格数据时&#xff0c;其中的主要作物Sorghum仅有2001-2006年的数据&#xff0c;而Millet只有…