关键词:
上一篇咱们简单了解了事务,这一篇我们再深入一下吧
Ⅰ、事务隔离级别
事务一共有四种隔离级别
简称 | 全称 | - |
---|---|---|
ru | read-uncommited | 未提交读 |
rc | read-commited | 提交读 |
rr | repeatable-read | 可重复读 |
srz | serializable | 可串行 |
从真正意义上来看只有srz达到真正隔离性的要求
oracel、sqlserver默认rc,mysql默认rr(99.99%达到隔离性要求)
事务隔离级别越低,事务请求的锁越少或者保持锁的时间就越短
1.1 四种隔离级别解读
事务隔离级别解决了三个问题,脏读,不可重复读,幻读
①read-uncommitted
可以读到其他线程未提交的数据,没人用,这就是脏读
②read-committed
解决了脏读,不会读到其他线程未提交的数据
但是a线程事务开始没提交,b线程读不到对应的数据,a线程事务提交后,b读到了
这就是不可重复读,两次读到不一样,破坏了隔离性,一个线程的事务所做的修改被另一个线程可见了
③repeatable-read
解决不可重复读,一边提交了,另一边还是看不到,两次读结果一样
重复读还可以解决一个问题,幻读(读到之前不存在的记录,讲锁的时候再演示)
例外情况:
for update锁定读就可以读到第一个线程中事务提交的数据,可以说是幻读,也可以说是一个不可重复读,这就是99.999%,哈哈
这里也是一种不符合隔离性的,读到了之前不存在的记录,只要是支持mvcc就很难做到完全隔离性
④serializable
两阶段加锁可串行化保证隔离性:加锁阶段,只加锁不放锁,解锁阶段,只放锁,不解锁
对于所有的写,每行上面都有锁,会有大量的锁竞争和超时
这样就失去了MVCC特性(非锁定一致性读)
所有操作都要加共享锁,lock in share mode ,执行selct * from xxx;
其实重写为select * from xxx lock in share mode; 对这条记录加共享锁,
强制事务排序,使得不会互相冲突,这样就不存在并发问题,都是串行了,读写相互阻塞
1.2 好奇心,我们对比下rr和sr
RR隔离级别
sesion1:
mysql> set tx_isolation="REPEATABLE-READ";
Query OK, 0 rows affected (0.00 sec)
mysql> create table t_lock(a int, b int, primary key(a));
Query OK, 0 rows affected (0.11 sec)
mysql> insert into t_lock values(1,1);
Query OK, 1 row affected (0.00 sec)
mysql> select * from t_lock;
+---+------+
| a | b |
+---+------+
| 1 | 1 |
+---+------+
1 row in set (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.02 sec)
mysql> select b from t_lock where a=1;
+------+
| b |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
session2:
mysql> set tx_isolation="REPEATABLE-READ";
Query OK, 0 rows affected (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> update t_lock set b=2 where a = 1;
Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0
mysql> commit;
Query OK, 0 rows affected (0.03 sec)
session1:
mysql> select b from t_lock where a=1; -- 再执行一次,得到的结果是1
+------+
| b |
+------+
| 1 |
+------+
1 row in set (0.00 sec)
mysql> select b from t_lock where a=1 for update; -- for update的去读,得到的结果是2
+------+
| b |
+------+
| 2 |
+------+ 1 row in set (0.00 sec)
session1中, RR隔离级别下,前两次读都是读取的快照,最后一次读取的当前更新的值
SR隔离级别
session1:
mysql> select * from t_lock;
+---+------+
| a | b |
+---+------+
| 1 | 1 |
+---+------+
1 row in set (0.00 sec)
mysql> set tx_isolation=‘SERIALIZABLE‘;
Query OK, 0 rows affected (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select b from t_lock where a=1;
+------+
| b |
+------+
| 1 |
+------+ 1
row in set (0.00 sec)
session2:
mysql> show engine innodb status\G
-- ------------省略部分输出------------
2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 3, OS thread handle 0x7f946bc94700, query id 30 localhost root cleaning up
TABLE LOCK table `burn_test`.`t_lock` trx id 5390 lock mode IS
RECORD LOCKS space id 15 page no 3 n bits 72 index `PRIMARY` of table `burn_test`.`t_lock` trx id 5390 lock mode S locks rec but not gap -- 有S锁
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 4; hex 80000001; asc ;;
1: len 6; hex 00000000150c; asc ;;
2: len 7; hex 8c000000340110; asc 4 ;;
3: len 4; hex 80000001; asc ;;
mysql> set tx_isolation=‘SERIALIZABLE‘;
Query OK, 0 rows affected (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> update t_lock set b=2 where a=1; -- 在SR的隔离级别下,直接阻塞,因为a=1上有一个S锁
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
SERIALIZABLE的隔离级别一般来说是不会去用
1.3 事务隔离级别的选择
show variables like ‘tx_isolation‘;
set global 后只对之后创建的session生效,之前的session没用
如何查看当前各个线程的事务隔离级别,sys库里面的session表看不了啊?
应该看performance_schema中user_variables_by_thread表
select * from user_variables_by_thread where variable_name = ‘tx_isolation‘;
找到thread_id
select processlist_id from threads where thread_id in(xxx);
再对应到processlist id
重点:
我们生产中一定要用rc,不要用默认的rr,一般场景rc性能会好很多,rc的锁好理解
为了同步数据一致性binlog_format用row,mixed会遇到各种bug导致主从不一致,5.7官方默认用row了,没有协商的余地,组复制也必须用row(目前MySQL若启用rc,不管binlog_format怎么设置都会强行转为row)
transaction_isolation = read-committed binlog_format = row 上面两个一定要配到my.cnf中,没有任何理由
有一种情况rr比rc好,条件是,应用偏读,一个事务里面有10个select,后面会提到,read_view的原因
再谈mysql锁机制及原理—锁的诠释(代码片段)
加锁是实现数据库并发控制的一个非常重要的技术。当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁。加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此... 查看详情
再谈mysql锁机制及原理—锁的诠释(代码片段)
加锁是实现数据库并发控制的一个非常重要的技术。当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁。加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此... 查看详情
再谈mysql锁机制及原理—锁的诠释(代码片段)
加锁是实现数据库并发控制的一个非常重要的技术。当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁。加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此... 查看详情
戏说领域驱动设计(廿六)——再谈事务
有关事务的内容,在前面我们已经不只谈过一次,没办法,这是一个绕不开的话题。你敢说你在开发中不用到它?最起码聚合进行序列化的时候得启动一个本地事务吧。当然了,如果你用的是NoSQL,则另当别论,咱也就别抬那个... 查看详情
再谈jdk的动态代理invocationhandler(代码片段)
前言为什么要再谈,因为动态代理是aop编程的核心。后面分析springaop的源代码的最重要的理论基础。再谈动态代理首先动态代理需要哪些角色呢?1.抽象角色。这个抽象角色必须为接口。2.具体角色。这个具体角色必须实现抽象... 查看详情
再谈组合(代码片段)
#include<iostream>intnum[20];usingnamespacestd;intsum=0;voidinit(intnn,intmm)inti;if(mm==0)//当mm==0时,num[1]~num[5]中存储的就是枚举的这五个数sum++;for(inti=1;i<=5;i++)cout<<num[i]<<"";cout< 查看详情
再谈rocketmqbrokerbusy(实战篇)(代码片段)
本文将在RocketMQ消息发送systembusy、brokerbusy原因分析与解决方案的基础上,结合生产上的日志尝试再次理解brokerbusy以及探讨解决方案。首先,brokerbusy相关的日志关键字如下:[REJECTREQUEST]systembusytoomanyrequestsandsystemthreadpoolbusy[PC_SYNCH... 查看详情
再谈linux的ulimit(代码片段)
(测试系统Centos7)测试脚本[[email protected]~]#cat/tmp/test_ulimit.pyimportsysdeftest_n():total=[]try:foriinrange(0,100000):total.append(open(‘/tmp/-‘.format(‘jjkkll‘,i),‘w‘))exceptExceptionaserr:prin 查看详情
再谈opencv(代码片段)
Matme=(Mat_<float>(4,4)<<1,0,1,0,0,1,0,1,0,0,1,0,0,0,0,1);Matmeinv=me.inv();cout<<"me="<<me<<endl;cout<<"meinv="<<endl<<meinv<<endl<<endl; 查看详情
从一起“盗币”事件再谈合约安全问题(代码片段)
目录从一起“盗币”事件再谈合约安全问题一.起因二.solidity复杂变量的地址计算问题一个示例简单变量的地址动态数组以及Map的地址三.先来玩demo部署合约初次调用newRecords从失败中找到正确方法构造成功的调用四.再一起来玩DV... 查看详情
再谈数据库隔离级别
前言在“数据库事务和事务的隔离级别”一文中,事务的隔离级别有如下4中隔离级别,1.未授权读取,readuncommited2.授权读取,readcommited3.可重复读取,repeatableread4.串行化,serializable 这次我以mysql为例,通过实际操作演... 查看详情
再谈kbmmw垃圾回收(代码片段)
很早就写了关于kbmMWServer如何实现的垃圾回收,但最近一段时间还是为此遇到问题,实现的Server不能稳定运行,并因此不能响应客户端的查询请求,只能重启Server。这是让人最头的问题,还好,今天终于搞定。先复习一下早前写... 查看详情
再谈python的变量作用域(代码片段)
一直觉得python的作用域不是什么难点,但是前几天在另一个博客平台上看到一段代码,又把我弄糊涂了,于是看书,把作用域又看了一遍,重新总结一下知识要点和坑,做个笔记所有代码基于python2.7.10基本概念和知识点不多说,直接上代... 查看详情
再谈vimsubstitute替换命令(代码片段)
在Vim替换命令一文介绍过,substitute命令的语法格式为::[range]s[ubstitute]/pattern/string/[flags]。[flags]表示可选的标志位,常用的包括g、c、n、e等。其中,标志位g使得substitute命令可以修改一行内的所有匹配,而不仅仅是第一处匹配;... 查看详情
再谈我对合约执行的理解(代码片段)
以太坊Tx的理解以太坊所有的交易都会创建EVM进行执行,因为如果to是空,那就是部署合约,当然要创建EVM如果to不是空,但是to可能是合约地址,这时候会触发合约代码执行fallback,所以也需要创建EVM.因此只要to不是空的Tx,都会执行func(evm... 查看详情
真实案例出发,再谈retrofit封装(代码片段)
原文链接:Anthony的简书博客项目代码:CameloeAnthony/Ant前言在使用了一段时间的Retrofit之后,今天终于在这里讲解到了网络的部分。目前开源的HTTP框架有很多,Volley,AndroidAsyncHttp,以及OkHttp+Retrofit等。而... 查看详情
再谈collections模块defaultdict()和namedtuple()(代码片段)
defaultdict()和namedtuple()是collections模块里面2个很实用的扩展类型。一个继承自dict系统内置类型,一个继承自tuple系统内置类型。在扩展的同时都添加了额外的很酷的特性,而且在特定的场合都很实用。defaultdict()定义以及作用返回... 查看详情
day354.再谈类的加载器-jvm(代码片段)
再谈类的加载器一、概述1、大厂面试题2、类加载器的分类3、类加载器的必要性4、命名空间代码解释:结果:解释:rootDir后面的地址是我们使用javacUser.class指令生成的class文件地址,然后loader1和loader2是两个用户... 查看详情