再谈事务(代码片段)

91洲际哥的笔记 91洲际哥的笔记     2022-11-15     409

关键词:

上一篇咱们简单了解了事务,这一篇我们再深入一下吧

Ⅰ、事务隔离级别

事务一共有四种隔离级别

简称 全称 -
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是两个用户... 查看详情