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

锁的算法,隔离级别的问题

锁的算法

InnoDB存储引擎有3中行锁的算法设计,分别是:

  1. Record Lock:单个行记录上的锁。
  2. Gap Lock:间隙锁,锁定一个范围,但不包含记录本身。
  3. Next-Key Lock:Gap Lock+Record Lock,锁定一个范围,并且锁定记录本身。

Record Lock总是会去锁住索引记录。如果InnoDB存储引擎表建立的时候没有设置任何一个索引,这时InnoDB存储引擎会使用隐式的主键来进行锁定。

Next-Key Lock是结合了Gap Lock和Record Lock的一种锁定算法,在Next-Key Lock算法下,InnoDB对于行的查询都是采用这种锁定算法。对于不同SQL查询语句,可能设置共享的(Share)Next-Key Lock和排他的(exlusive)Next-Key Lock。

可以通过一个例子来演示Next-Key Lock的锁定算法,建立一张表t,插入值为1、2、3、4、7、8的6条记录。

create table t(a int,primary key(a))ENGINE=InnoDB;

begin;

insert into t select 1;

insert into t select 2;

insert into t select 3;

insert into t select 4;

insert into t select 7;

insert into t select 8;

commit;

select * from t;

接着开启两个会话,会话A在一个事务中执行select * from t where a<6 lock in share mode,会话B中,插入小于6或者等于6的记录,如下所示:

在这种情况下,不论插入的记录是5还是6,都会被锁定。因为在Next-Key Lock算法下,锁定的是(-∞,6)这个数值区间的所有数值。但是插入9这个数值是可以的,因为该记录不在锁定的范围内,而对于单个值的索引查询,不需要用到Gap Lock,只要加一个Record Lock即可,因此InnoDB存储引擎会自己选一个最小的算法模型。同样,对于上面的表t,进行如下操作:

这时插入记录5或6都是可行的了。需要注意的是,上面演示的两个例子都是在InnoDB的默认配置下,即事务的隔离级别为REPEATABLE READ的模式下。因为在REPEATABLE READ模式下,Next-Key Lock算法是默认的行记录锁定算法。

隔离级别的问题

通过锁可以实现事务的隔离性的要求,使得事务可以并发地工作。锁提高了并发,但是却会带来问题。不过,好在因为事务隔离性的要求,锁只会带来3种问题。如果可以防止这3种情况的发生,那将不会产生并发异常。

丢失更新

丢失更新(lost update)是一个经典的数据库问题。实际上,所有多用户计算机系统环境下有可能产生这个问题。简单说来,出现下面的情况时,就会发生丢失更新:

(1)事务T1查询一行数据,放入本地内存,并显示给一个终端用户User1。

(2)事务T2也查询该行数据,并将取得的数据显示给终端用户User2。

(3)User1修改这行记录,更新数据库并提交。

(4)User2修改这行记录,更新数据库并提交。

显然,这个过程中用户User1的修改更新操作“丢失”了。这可能会发生一个恐怖的结果。设想银行丢失了更新操作:一个用户账户中有10 000元人民币,他用两个网上银行的客户端转账,第一次转9 000人民币,因为网络和数据的关系,这时需要等待。但是如果这时用户可以操作另一个网上银行客户端,转账1元。如果最终两笔操作都成功了,用户的账号余款是9 999人民币,第一转的9 000人民币并没有得到更新。也许有人会说,不对,我的网银是绑定USB Key的,不会发生这种情况——通过USB Key登录也许可以解决这个问题,但是更重要的是,要在数据库层解决这个问题,以避免任何可能发生丢失更新的情况。

要避免丢失更新发生,其实需要让这种情况下的事务变成串行操作,而不是并发的操作。即在上述四种的第(1)种情况下,对用户读取的记录加上一个排他锁,同样,发生第(2)种情况下的操作时,用户也需要加一个排他锁。这种情况下,第(2)步就必须等待第(1)、(3)步完成,最后完成第(4)步,如以下所示:

我发现,程序员可能在了解如何使用SELECT、INSERT、UPDATE、DELETE语句后就开始编写应用程序。因此,丢失更新是程序员最容易犯的错误,也是最不易发现的一个错误,特别是由于这种现象只是随机的、零星的出现,但其可能造成的后果却十分严重。

脏读

理解脏读之前,需要理解脏数据的概念。脏数据和脏页有所不同。脏页指的是在缓冲池中已经被修改的页,但是还没有刷新到磁盘,即数据库实例内存中的页和磁盘的页中的数据是不一致的,当然在刷新到磁盘之前,日志都已经被写入了重做日志文件中。而所谓脏数据,是指在缓冲池中被修改的数据,并且还没有被提交(commit)。

对于脏页的读取,是非常正常的。脏页是因为数据库实例内存和磁盘的异步同步造成的,这并不影响数据的一致性。并且因为是异步的,因此可以带来性能的提高。而脏数据却不同,脏数据是指未提交的数据。如果读到了脏数据,即一个事务可以读到另外一个事务中未提交的数据,则显然违反了数据库的隔离性。

脏读指的就是在不同的事务下,可以读到另外事务未提交的数据,简单来说,就是可以读到脏数据。比如下面的例子所示:

事务的隔离级别进行了更换,由默认的REPEATABLE READ换成了READ UNCOMMITTED,因此在会话A中事务并没有提交的前提下,会话B中两次SELECT操作取得了不同的结果,并且这两个记录是在会话A中并未提交的数据,即产生了脏读,违反了事务的隔离性。

脏读现象在生产环境中并不常发生。从上面的例子中就可以发现,脏读发生的条件是需要事务的隔离级别为READ UNCOMMITTED,而目前绝大部分的数据库都至少设置成READ COMMITTED。InnoDB存储引擎默认的事务隔离级别为READ REPEATABLE,Microsoft SQL Server数据库为READ COMMITTED,Oracle数据库同样也是READ COMMITTED。

不可重复读

不可重复读是指在一个事务内多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务的两次读数据之间,由于第二个事务的修改,第一个事务两次读到的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为不可重复读。

不可重复读和脏读的区别是:脏读是读到未提交的数据;而不可重复读读到的确实是已经提交的数据,但是其违反了数据库事务一致性的要求。

可以通过下面一个例子来观察不可重复读的情况:

会话A中开始一个事务,第一次读取到的记录是1;另一个会话B中开始了另一个事务,插入一条2的记录。在没有提交之前,会话A中的事务再次读取时,读到的记录还是1,没有发生脏读的现象。但会话2中的事务提交后,在对会话A中的事务进行读取时,这时读到的是1和2两条记录。这个例子的前提是,在事务开始前,会话A和会话B的事务隔离级别都调整为了READ COMMITTED。

一般来说,不可重复读的问题是可以接受的,因为其读到的是已经提交的数据,本身并不会带来很大的问题。因此,很多数据库厂商(如Oracle、Microsoft SQL Server)将其数据库事务的默认隔离级别设置为READ COMMITTED,在这种隔离级别下允许不可重复读的现象。

InnoDB存储引擎中,通过使用Next-Key Lock算法来避免不可重复读的问题。在MySQL官方文档中,将不可重复读定义为Phantom Problem,即幻象问题。在Next-Key Lock算法下,对于索引的扫描,不仅仅是锁住扫描到的索引,而且还锁住这些索引覆盖的范围(gap)。因此对于这个范围内的插入都是不允许的。这样就避免了另外的事务在这个范围内插入数据导致的不可重复读的问题。因此,InnoDB存储引擎的默认事务隔离级别是READ REPEATABLE,采用Next-Key Lock算法,就避免了不可重复读的现象。

相关文章:

好程序员分享24个canvas基础知识小结

好程序员分享24个canvas基础知识小结,非常全面详尽,推荐给大家。 现把canvas的知识点总结如下,以便随时查阅。 1、填充矩形 fillRect(x,y,width,height); 2、绘制矩形边框 strokeRect(x,y,width,height); 3、擦除矩形 clearRect(x,y,width,he…

【leetcode】二叉树与经典问题

文章目录笔记leetcode [114. 二叉树展开为链表](https://leetcode-cn.com/problems/flatten-binary-tree-to-linked-list/)解法一: 后序遍历、递归leetcode [226. 翻转二叉树](https://leetcode-cn.com/problems/invert-binary-tree/)思路与算法复杂度分析leetcode [剑指 Offer…

PHP PSR-1 基本代码规范(中文版)

基本代码规范 本篇规范制定了代码基本元素的相关标准,以确保共享的PHP代码间具有较高程度的技术互通性。 关键词 “必须”("MUST")、“一定不可/一定不能”("MUST NOT")、“需要”("REQUIRED")、“将会”("SHALL")、“不会…

最近邻插值实现:图像任意尺寸变换

#<opencv2/imgproc/imgproc.hpp> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> using namespace cv; using namespace std; // 实现最近邻插值图像缩放 cv::Mat nNeighbourInterpolatio…

软件测试-培训的套路-log3

最新的套路&#xff01;我是没了解过--下图中描述-log3 Dotest-董浩 但是我知道不管什么没有白吃的午餐而且还会给钱…如果真的有&#xff0c;请醒醒&#xff01; 当然话又回来&#xff0c;套路不套路&#xff0c;关键看你是否需要&#xff1b;你如果需要我觉得是帮你…不需要&…

ALSA声卡驱动中的DAPM详解之四:在驱动程序中初始化并注册widget和route

前几篇文章我们从dapm的数据结构入手&#xff0c;了解了代表音频控件的widget&#xff0c;代表连接路径的route以及用于连接两个widget的path。之前都是一些概念的讲解以及对数据结构中各个字段的说明&#xff0c;从本章开始&#xff0c;我们要从代码入手&#xff0c;分析dapm的…

双线性插值实现

#include <opencv2/imgproc/imgproc.hpp> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> // 实现双线性插值图像缩放 cv::Mat BilinearInterpolation(cv::Mat srcImage) {CV_Assert(srcI…

【C++】C++ 强制转换运算符

C 运算符 强制转换运算符是一种特殊的运算符&#xff0c;它把一种数据类型转换为另一种数据类型。强制转换运算符是一元运算符&#xff0c;它的优先级与其他一元运算符相同。 大多数的 C 编译器都支持大部分通用的强制转换运算符&#xff1a; (type) expression 其中&…

WPS 2019 更新版(8392)发布,搭配优麒麟 19.04 运行更奇妙!

WPS 2019 支持全新的外观界面、目录更新、方框打勾、智能填充、内置浏览器、窗口拆组、个人中心等功能。特别是全新的新建页面&#xff0c;让你可以整合最近打开的文档、本地模版、公文模版、在线模板等。 随着优麒麟即将发布的新版 19.04 的到来&#xff0c;金山办公软件也带来…

图像金字塔操作,上采样、下采样、缩放

#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include <iostream> // 图像金子塔采样操作 void Pyramid(cv::Mat srcImage) {// 根据图像源尺寸判断是否需要缩放if(srcImage.rows > 400 && srcImage…

【C++】【OpenCv】图片加噪声处理,计时,及键盘事件响应捕捉

图像噪声添加 cv::Mat addGuassianNoise(cv::Mat& src, double a, double b) {cv::Mat temp src.clone();cv::Mat dst(src.size(), src.type()); ​// Construct a matrix containing Gaussian noisecv::Mat noise(temp.size(), temp.type());cv::RNG rng(time(NULL));//…

透视学理论(十四)

2019独角兽企业重金招聘Python工程师标准>>> 已知左右距点分别位于透视画面两侧&#xff0c;因此我们可以左距点来得出透视画面的纵深长度。 距点&#xff0c;指的是与透视画面成45的水平线的消失点。我们把画面边缘按1米的宽度&#xff08;A点&#xff09;&#xf…

适用于Mac上的SQL Server

适用于Mac上的SQL Server&#xff1f; 众所周知&#xff0c;很多人都在电脑上安装了SQL Server软件&#xff0c;普通用户直接去官网下载安装即可&#xff0c;Mac用户则该如何在Mac电脑上安装SQL Server呢&#xff1f;想要一款适用于Mac上的SQL Server&#xff1f;点击进入&…

【MATLAB】————拷贝指定文件路径下的有序文件(选择后),可处理固定规律的文件图片数据或者文件

总体上来说这种数据有2中处理思路。第一种如下所示&#xff0c;从一组数据中挑选出符合要求的数据&#xff1b; 第二中就是数据中不需要的数据删除&#xff0c;选择处理不需要的数据&#xff0c;留下的补集就是需要的数库。一般情况下需要看问题是否明确&#xff0c;需求明确的…

mouseover与mouseenter,mouseout与mouseleave的区别

mouseover与mouseenter 不论鼠标指针穿过被选元素或其子元素&#xff0c;都会触发 mouseover 事件。只有在鼠标指针穿过被选元素时&#xff0c;才会触发 mouseenter 事件。 mouseout与mouseleave不论鼠标指针离开被选元素还是任何子元素&#xff0c;都会触发 mouseout 事件。只…

图像掩码操作的两种实现

#include <opencv2/imgproc/imgproc.hpp> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> #include <stdio.h> using namespace cv; using namespace std; // 基于像素邻域掩码操作…

控制反转 IOC

2019独角兽企业重金招聘Python工程师标准>>> 控制反转&#xff08;Inversion of Control&#xff0c;缩写为IoC&#xff09;面向对象设计原则&#xff0c;降低代码耦合度 依赖注入&#xff08;Dependency Injection&#xff0c;简称DI&#xff09; 依赖查找&#xf…

【C++】explicit关键字

explicit的优点是可以避免不合时宜的类型变换&#xff0c;缺点无。所以google约定所有单参数的构造函数都必须是显式的** explicit关键字只需用于类内的单参数构造函数前面。由于无参数的构造函数和多参数的构造函数总是显式调用&#xff0c;这种情况在构造函数前加explicit无…

mongodb3 分片集群平滑迁移

分片集群平滑迁移实验(成功)过程概述&#xff1a;为每个分片添加多个从节点&#xff0c;然后自动同步。同步完后&#xff0c;切换主节点到新服务器节点。导出原来的config 数据库&#xff0c;并导入到新服务器的config数据库停掉整个集群&#xff0c;可以使用kill 命令停掉新服…

图像添加椒盐噪声

#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <cstdlib> // 图像添加椒盐噪声 cv::Mat addSaltNoise(const cv::Mat srcImage, int n) {cv::Mat resultIamge srcImage.clone() ;for(int k0; k<n; k){// 随机取值行…

我用python10年后,我发现学python必看这三本书!

非常喜欢python 我非常喜欢python&#xff0c;在前面5年里&#xff0c;它一直是我热衷使用并不断研究的语言&#xff0c;迄今为止&#xff0c;python都非常友好并且易于学习&#xff01; 它几乎可以做任何事&#xff0c;从简单的脚本创建、web&#xff0c;到数据可视化以及AI人…

【OpenCV】内核的形状函数使用记录

opencv getStructuringElement函数 为形态操作返回指定大小和形状的结构化元素。 该函数构造并返回结构化元素&#xff0c;这些元素可以进一步传递给侵蚀、扩张或morphologyEx。但是您也可以自己构造一个任意的二进制掩码&#xff0c;并将它用作结构化元素。 getStructuringE…

boxFilter 滤波器实现

cv::Ptr<cv::FilterEngine> cv::createBoxFilter( int srcType, int dstType, Size ksize,Point anchor, bool normalize, int borderType ) {// 基础参数设置 图像深度int sdepth CV_MAT_DEPTH(srcType);int cn CV_MAT_CN(srcType), sumType CV_64F;if( sdepth < …

git代理设置

2019独角兽企业重金招聘Python工程师标准>>> git config --global http.proxy http://127.0.0.1:1080 git config --global https.proxy https://127.0.0.1:1080 git config --global http.sslVerify false删除 git config --global --unset http.proxy git config …

获得PMP证书的这一年

很荣幸&#xff0c;通过2018年12月的PMP考试&#xff0c;这不仅是一张证书的收获&#xff0c;更体现了我的成长&#xff0c;明确了以后的道路。在考证的过程中&#xff0c;我收获了很多&#xff0c;不仅是工作技能方面&#xff0c;还包括思想的升华。  首先&#xff0c;重拾了…

【OpenCV】图片操作小结:RAW图转image以及image连续保存

opencv将RAW图转image uint32_t ReadRawImage(cv::Mat& image,const std::string& path,int width,int height,int cv_image_type) {cv::Mat input_image(height, width, cv_image_type);std::ifstream in(path, std::ios::in | std::ios::binary);if (!in) {std::cou…

Windows server2008服务器设置多用户登录

添加用户 右击我的电脑-->管理-->本地用户和组-->新用户 启用远程服务并添加远程用户 启用 右键我的电脑--->属性--->远程设置--->勾上允许远程连接到此电脑 添加用户 点击选择用户--->添加--->高级--->立即查找 防火墙允许远程连接设置 控制面板(c…

Linux6版本系统搭建Open***远程访问

前言&#xff1a;open***是一个***工具,用于创建虚拟专用网络(Virtual Private Network)加密通道的免费开源软件,提供证书验证功能,也支持用户名密码认证登录方式,当然也支持两者合一,为服务器登录和连接提供更加安全的方式,可以在不同网络访问场所之间搭建类似于局域网的专用网…

【Smart_Point】unique_ptr与shared_ptr使用实例

shared_ptr使用实例 文章目录shared_ptr使用实例unique_ptr使用实例cv::fitLine中斜率为正无穷的情况&#xff0c;需要特殊考虑std::string path "D:\\code\\test_base_opencv\\example\\depth_98.raw";std::string save_path "D:\\code\\test_base_opencv\\e…

关于kNN、kMeans、Apriori算法小结

趁着准备即将到来的笔试&#xff0c;也为了回顾一下这一星期来所学的三个机器学习算法&#xff0c;觉得还是重新理一下思路&#xff0c;好理解一下这几个算法。 复制代码 kNN算法 即k-近邻算法&#xff0c;属监督学习。 概述 优点&#xff1a;精度高&#xff0c;对异常值不敏感…