盘一盘MySQL事务和锁
MySQL事务与锁
事务
事务的概念
事务Transaction,是指作为一个Session执行的一系列SQL语句的操作,要么完全的执行,要么完全的都不执行。
事务最经典也经常被拿出来说例子就是转账了。
假如小明要给小红转账1000元,这个转账会涉及到两个关键操作就是:先将小明的余额减少1000元,然后将小红的余额增加1000元。
万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。如果突然出现了错误,就会回滚,使所有之前执行完成的语句都被撤销;如果事务执行成功了,那么即便在成功后系统立即出现了错误,所有的操作都依旧有效。
事物的四大特性(ACID)?
关系性数据库需要遵循ACID规则,具体内容如下:
- Atomicity(原子性):一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即事务不可分割、不可约简。
- Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等。拿转账来说,假设用户A和用户B两者的钱加起来一共是20000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是20000,这就是事务的一致性。
- Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括未提交读(Read uncommitted)、提交读(read committed)、可重复读(repeatable read)和串行化(Serializable)。
- Durability(持久性):事务提交后,对数据的修改就是永久的,即便系统故障也不会丢失。即事务正确提交后,即便此时数据库出现故障,记录也必定保证更新到了数据库中。
事务的隔离级别
事务隔离级别指的是在处理同一个数据的多个事务中,一个事务修改数据后,其他事务何时能看到修改后的结果。数据库事务的隔离级别有4个,由低到高依次为Read uncommitted 、Read committed 、Repeatable read 、Serializable ,这四个级别可以逐个解决脏读 、不可重复读 、幻读这几类问题。
- Serializable:串行化,一个事务一个事务的执行。是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻读。
- Repeatable read:重复读,无论其他事务是否修改并提交了数据,在这个事务中看到的数据值始终不受其他事务影响,可以避免不可重复读,但还有可能出现幻读 ;
- Read committed:读取已提交,其他事务提交了对数据的修改后,本事务就能读取到修改后的数据值,避免了脏读,但是可能会造成不可重复读;
- Read uncommitted:读取未提交,其他事务只要修改了数据,即使事务未提交,本事务也能看到修改后的数据值,就可能出现脏读;
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容),但是你要知道的是InnoDB 存储引擎默认使用 **REPEATABLE-READ(可重读)**并不会有任何性能损失。
InnoDB 存储引擎在 分布式事务 的情况下一般会用到SERIALIZABLE(可串行化)隔离级别。
什么是脏读?幻读?不可重复读?
- 脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个回滚了操作,则后一个事务所读取的数据就会是不正确的。
- 不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
- 幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。
以下是在各个隔离级别中,脏读、不可重复读、幻读是否会发生:
修改MySQL数据库事务隔离级别
场景
使用乐观锁的时候,如果一个事务修改了库存并提交了事务,那其他的事务应该可以读取到修改后的数据值,所以不能使用可重复读的隔离级别,应该修改为读取已提交(Read committed)。
# 修改配置文件
cd /etc/mysql/mysql.conf.d/
sudo vim mysqld.cnf# 添加以下代码
transaction-isolation=READ-COMMITTED
事务的使用
1. 开启事务
begin;
或者
start transaction;
说明:
开启事务后执行修改命令,变更会维护到本地缓存中,而不维护到物理表中;
MySQL数据库默认采用自动提交(autocommit)模式,如果没有显示的开启一个事务,那么每条sql语句都会被当作一个事务执行提交的操作
当设置autocommit=0就是取消了自动提交事务模式,直到显示的执行commit和rollback表示该事务结束。
set autocommit = 0 表示取消自动提交事务模式,需要手动执行commit完成事务的提交
set autocommit = 0;
insert into students(name) values('刘三峰');
-- 需要执行手动提交,数据才会真正添加到表中, 验证的话需要重新打开一个连接窗口查看表的数据信息,因为是临时关闭自动提交模式
commit-- 重新打开一个终端窗口,连接MySQL数据库服务端
mysql -uroot -p-- 然后查询数据,如果上个窗口执行了commit,这个窗口才能看到数据
select * from students;
2. 提交事务:
commit;
- 将缓存的数据变更维护到物理表中;当commit一瞬间才执行以上sql语句,完成数据的更新。;
- 回滚事务:放弃缓存变更的数据,表示事务执行失败,应该回到开始事务前的状态;
rollback;
3. 事务演练的SQL语句:
begin;
insert into students(name) values('李白');
-- 查询数据,此时有新增的数据, 注意: 如果这里后续没有执行提交事务操作,那么数据是没有真正的更新到物理表中
select * from students;
-- 只有这里提交事务,才把数据真正插入到物理表中
commit;-- 新打开一个终端,重新连接MySQL数据库,查询students表,这时没有显示新增的数据,说明之前的事务没有提交,这就是事务的隔离性
-- 一个事务所做的修改操作在提交事务之前,对于其他事务来说是不可见的
select * from students;
锁
分类
从对数据操作的类型分:
- 读锁(共享锁,多个读操作同时进行不会阻塞)
- 写锁(排他锁,一次只能进行一个写操作,其他阻塞)
从对数据操作的粒度分:
- 表锁(走了索引的for update是表锁,不会出现死锁,发生锁冲突几率高,并发低)
- 行锁(没走索引的for update是行锁,会出现死锁,发生锁冲突几率低,并发高)
锁冲突
例如说事务A将某几行上锁后,事务B又对其上锁,锁不能共存否则会出现锁冲突。(但是共享锁可以共存,共享锁和排它锁不能共存,排它锁和排他锁也不可以)
死锁
例如说两个事务,事务A锁住了1-5行,同时事务B锁住了6-10行,此时事务A请求锁住6-10行,就会阻塞直到事务B施放6-10行的锁,而随后事务B又请求锁住1-5行,事务B也阻塞直到事务A释放1-5行的锁。死锁发生时,会产生Deadlock错误。
表锁是对表操作的,所以自然锁住全表的表锁就不会出现死锁。
一个session对表加了读锁,那么它可以查询这个表,但是不能(会报错)修改(insert、update、delete)这个表,也不能查询其他表,即便那些表没有加锁;其他session可以查询这个表,但是修改(insert、update、delete)会阻塞,直到表锁被释放。
一个session对表加了写锁,那么它可以读写这个表,但是不能(会报错)读写其他表;其他session对这个表进行增删改查任何操作都会阻塞。
乐观锁和悲观锁
悲观锁
当认为数据被并发修改的几率比较大,需要在修改之前借助于数据库锁机制先对数据进行加锁的思想被称为悲观锁,又称PCC(Pessimistic Concurrency Control)。在效率方面,处理锁的操作会产生了额外的开销,而且增加了死锁的机会。当一个线程在处理某行数据的时候,其它线程只能等待。
悲观锁的实现方式
悲观锁的实现是依赖于数据库提供的锁机制,流程如下:
- 修改记录前,对记录加上排他锁(exclusive locking)
- 如果加锁失败,说明这条数据正在被修改,那么当前查询要等待或者抛出异常,这由开发者决定
- 如果加锁成功,可以对这条数据修改了,事务完成解锁
- 加锁修改期间,其他事务也想对这条记录进行操作时,都要阻塞等待或者直接抛出异常
在使用mysql Innodb引擎实现悲观锁时,必须关闭mysql的事务自动提交,否则就得进行显式事务声明BEGIN或START TRANSATION,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。通过set autocommit=0关闭事务自动提交;
举例说明下单扣减库存如何使用悲观锁实现
//0.开始事务
BEGIN; //START TRANSACTION
mysql> SELECT * FROM t2 WHERE salary = 2001 FOR UPDATE;//加上排他锁,salary需要是索引字段
+----+----------------+--------+---------+
| id | name | salary | version |
+----+----------------+--------+---------+
| 1 | ZhanHong Zhang | 2001 | 4 |
+----+----------------+--------+---------+
1 row in set (0.00 sec)mysql> UPDATE t2 SET salary = 2000,version = version + 1 WHERE id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0mysql> COMMIT;
使用select…for update会锁住记录,即查询到的数据会被加上一个排他锁,其它事务可以读取,但不能进行更新和插入操作。MySQL InnoDB默认行级锁,行级锁都是基于索引的,如果一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁把整张表锁住,这点需要注意。
乐观锁
乐观锁思想认为,数据一般是不会造成冲突的。只有在提交数据的时候,才会对数据的冲突进行检测。当发现冲突的时候,返回错误的信息,让用户决定如何去做。
乐观锁不会使用数据库提供的锁机制,一般的实现方式就是记录数据版本。
乐观锁的实现方式
乐观锁的实现不需要借助于数据库锁机制,只要就是两个步骤:冲突检测和数据更新,其中一种典型的是实现方法就是CAS(Compare and Swap)
CAS实现乐观锁
CAS是一种乐观锁实现方式,顾名思义就是先比较后更新。在对一个数据进行更新前,先持有对这个数据原有值的备份。比如,要将a=2更新为a=3,在进行更新前会比较此刻a是否为2。如果是2,才会进行更新操作。当多个线程尝试使用CAS同时更新一个变量时,只有一个线程能够成功,其余都是失败。失败的线程不会被挂起,而是被告知这次竞争失败,并且可以再次尝试。
乐观锁方式实现如下:
mysql> SELECT * FROM t2;
+----+----------------+--------+---------+
| id | name | salary | version |
+----+----------------+--------+---------+
| 1 | ZhanHong Zhang | 2000 | 5 |
| 3 | zzh | 1000 | 1 |
+----+----------------+--------+---------+
2 rows in set (0.00 sec)mysql> UPDATE t2 SET salary = 2001 , version = version + 1 WHERE id = 1 AND salary = 2000;
在更新之前,先查询表中当前salary值,然后在做update时,以salary值作为一个修改条件。当进行提交更新的时候,判断数据库的当前salary值与第一次取出来的salary值进行比对,相等则更新,否则认为是过期数据。
但是这种更新存在一个比较严重的问题,即ABA问题(自行百度)。
解决ABA问题的一个方法是通过一个顺序递增的version字段:
mysql> UPDATE t2 SET salary = 2001 , version = version + 1 WHERE id = 1 AND version = 6;
Query OK, 1 row affected (0.08 sec)
Rows matched: 1 Changed: 1 Warnings: 0
在每次执行数据的修改操作时,都会带上一个版本号,一旦版本号和数据版本号一致就可以执行修改操作并对版本号执行+1操作,否则执行失败。因为每次修改操作都会将版本号增加,所以不会出现ABA问题。还可以使用时间戳,因为时间戳天然具有顺序递增性。
乐观锁和悲观锁对比
乐观锁并不是真正的加锁,优点是效率高,缺点是更新失败的概率比较高;悲观锁依赖于数据库锁机制,更新失败的概率比较低,但是效率也低。
锁的操作
加表锁
LOCK TABLE tableName1 [read|write],tableName2 [read|write...;
解表锁
UNLOCK TABLES;
加行锁
加(行锁)共享锁的写法:lock in share mode(读)
select math from zje where math>60 lock in share mode;
加(行锁)排它锁的写法:for update(写)
select math from zje where math >60 for update;
行锁在事务提交后解锁。
查看哪些表被加锁
SHOW OPEN TABLES;
结合事务与锁进行研究
InnoDB存储引擎的锁的算法有三种
假如数据库中有90条记录,id从1 ~ 40,51 ~ 100。
- Record lock:单个行记录上的锁,即这90条记录每一条都有一个锁
- Gap lock:间隙锁,锁定一个范围,不包括记录本身,即给id=41 ~ 50的记录加锁(现在还没有id=41 ~ 50的记录,这里加锁的意思是指,如果其他事务想要插入一条id=41 ~ 50的记录,就会被阻塞)。
- Next-key lock:record+gap 锁定一个范围,包含记录本身,即把id = 1 ~ 100的所有行记录都加锁,解决了幻读问题。
相关知识点:
- innodb对于行的查询使用next-key lock
- Next-key lock为了解决幻读问题
- 当查询的索引含有唯一属性时,将next-key lock降级为record key
- Gap锁设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生
- 有两种方式显式关闭gap锁:(除了外键约束和唯一性检查外,其余情况仅使用record lock)
A. 将事务隔离级别设置为RC
B. 将参数innodb_locks_unsafe_for_binlog设置为1
Multiversion concurrency control 多版本并发控制(MVCC)
原文链接:https://juejin.im/post/5c68a4056fb9a049e063e0ab
并发访问(读或者写)数据库时,对正在事务内处理的数据做多版本的管理,用来避免由于写操作的堵塞,而引发读操作失败的并发问题。
如果有人从数据库中读数据的同时,有另外的人写入数据,有可能读数据的人会看到『半写』或者不一致的数据。有很多种方法来解决这个问题,叫做并发控制方法。最简单的方法,通过加锁,让所有的读者等待写者工作完成,但是这样效率会很差。MVCC 使用了一种不同的手段,每个连接到数据库的读者,在某个瞬间看到的是数据库的一个快照,写者写操作造成的变化在写操作完成之前(或者数据库事务提交之前)对于其他的读者来说是不可见的。
当一个 MVCC 数据库需要更新一条数据记录的时候,它不会直接用新数据覆盖旧数据,而是将旧数据标记为过时(obsolete)并在别处增加新版本的数据。这样就会有存储多个版本的数据,但是只有一个是最新的。这种方式允许读者读取在他读之前已经存在的数据,即使这些在读的过程中半路被别人修改、删除了,也对先前正在读的用户没有影响。这种多版本的方式避免了填充删除操作在内存和磁盘存储结构造成的空洞的开销,但是需要系统周期性整理(sweep through)以真实删除老的、过时的数据。
MVCC 提供了时点(point in time)一致性视图。MVCC 并发控制下的读事务一般使用时间戳或者事务 ID去标记当前读的数据库的状态(版本),读取这个版本的数据。读、写事务相互隔离,不需要加锁。读写并存的时候,写操作会根据目前数据库的状态,创建一个新版本,并发的读则依旧访问旧版本的数据。
一句话总结就是:
MVCC(
Multiversion concurrency control
) 就是 同一份数据临时保留多版本的一种方式,进而实现并发控制
那么此处需要注意的点就是:
- 在读写并发的过程中如何实现多版本?
- 在读写并发之后,如何实现旧版本的删除(毕竟很多时候只需要一份最新版的数据就够了)?
下面介绍一下MySQL中对于 MVCC 的逻辑实现
MVCC逻辑流程-插入
在MySQL中建表时,每个表都会有三列隐藏记录,其中和MVCC有关系的有两列
- 数据行的版本号 (DB_TRX_ID)
- 删除版本号 (DB_ROLL_PT)
id | test_id | DB_TRX_ID | DB_ROLL_PT |
---|---|---|---|
在插入数据的时候,假设系统的全局事务ID从1开始,以下SQL语句执行分析参考注释信息:
begin;-- 获取到全局事务ID
insert into `test_zq` (`id`, `test_id`) values('5','68');
insert into `test_zq` (`id`, `test_id`) values('6','78');
commit;-- 提交事务
当执行完以上SQL语句之后,表格中的内容会变成:
id | test_id | DB_TRX_ID | DB_ROLL_PT |
---|---|---|---|
5 | 68 | 1 | NULL |
6 | 78 | 1 | NULL |
可以看到,插入的过程中会把全局事务ID记录到列 DB_TRX_ID 中去
MVCC逻辑流程-删除
对上述表格做删除逻辑,执行以下SQL语句(假设获取到的事务逻辑ID为 3)
begin;--获得全局事务ID = 3
delete test_zq where id = 6;
commit;
执行完上述SQL之后数据并没有被真正删除,而是对删除版本号做改变,如下所示:
id | test_id | DB_TRX_ID | DB_ROLL_PT |
---|---|---|---|
5 | 68 | 1 | NULL |
6 | 78 | 1 | 3 |
MVCC逻辑流程-修改
修改逻辑和删除逻辑有点相似,修改数据的时候 会先复制一条当前记录行数据,同事标记这条数据的数据行版本号为当前是事务版本号,最后把原来的数据行的删除版本号标记为当前是事务。
执行以下SQL语句:
begin;-- 获取全局系统事务ID 假设为 10
update test_zq set test_id = 22 where id = 5;
commit;
复制代码
执行后表格实际数据应该是:
id | test_id | DB_TRX_ID | DB_ROLL_PT |
---|---|---|---|
5 | 68 | 1 | 10 |
6 | 78 | 1 | 3 |
5 | 22 | 10 | NULL |
MVCC逻辑流程-查询
此时,数据查询规则如下:
查找数据行版本号早于当前事务版本号的数据行记录
也就是说,数据行的版本号要小于或等于当前是事务的系统版本号,这样也就确保了读取到的数据是当前事务开始前已经存在的数据,或者是自身事务改变过的数据
查找删除版本号要么为NULL,要么大于当前事务版本号的记录
这样确保查询出来的数据行记录在事务开启之前没有被删除
根据上述规则,我们继续以上张表格为例,对此做查询操作
begin;-- 假设拿到的系统事务ID为 12
select * from test_zq;
commit;
执行结果应该是:
id | test_id | DB_TRX_ID | DB_ROLL_PT |
---|---|---|---|
6 | 22 | 10 | NULL |
MySQL 中 MVCC 版本控制案例
回到文章刚开始的哪个例子,我们使用 MVCC 机制分析一遍
为了方便描述,对SQL语句做如下标记:
begin;--假设当前获取到的事务 ID 为 2 ----1
select * from test_zq; ----2
commit;begin;--假设当前获取到的事务 ID 为 3 ----3
UPDATE test_zq SET test_id = 20 WHERE id = 1; ----4
commit;
对表中数据做初始化:
begin;
insert into `test_zq` (`id`, `test_id`) values('1','18');
insert into `test_zq` (`id`, `test_id`) values('4','8');
commit;
表中的原始数据为:
id | test_id | DB_TRX_ID | DB_ROLL_PT |
---|---|---|---|
1 | 18 | 1 | NULL |
4 | 8 | 1 | NULL |
案例1
执行顺序为 1 2 3 4 2
1 2
步骤执行结果为:
id | test_id |
---|---|
1 | 18 |
4 | 8 |
3 4
步骤执行结果为:
id | test_id | DB_TRX_ID | DB_ROLL_PT |
---|---|---|---|
1 | 18 | 1 | 3 |
4 | 8 | 1 | NULL |
1 | 20 | 3 | NULL |
2
执行后的结果为:
id | test_id |
---|---|
1 | 18 |
4 | 8 |
上述结果符合预期,接下来看案例2
案例2
执行顺序为3 4 1 2
3 4
步骤执行后结果为:
id | test_id | DB_TRX_ID | DB_ROLL_PT |
---|---|---|---|
1 | 18 | 1 | 3 |
4 | 8 | 1 | NULL |
1 | 20 | 3 | NULL |
1 2
步骤执行后结果为:
假设此时的事务ID为 txid = 4
则查询结果是 :
id | test_id |
---|---|
1 | 20 |
4 | 8 |
显然,结果应该是不对的,但是我们在文章开头也是按照这样的顺序执行的,但是MySQL的返回结果没有任何问题,可是这里根据MVCC机制来分析却出现了这样的状况,所以问题出在哪里?
我们大概可以猜测到:
此处问题不是出在 MVCC 机制,MySQL解决不可重复读和脏读并不是单纯利用 MVCC 机制来实现的。
MySQL数据库事务:从未提交读—>MVCC机制—>Next-Key Lock,各种隔离级别及其解决对应问题的原理
如果是Innodb引擎,由于引入了MVCC多版本并发控制和Next-Key Lock等锁机制,从而使得隔离级别 “可重复读” 也能解决幻读问题,那么也就变成了如下
尽是问题 (丢失修改)
未提交读—————————————————————————————— (隔离线)脏读问题
提交读————————————————————————————————不可重复 + 幻读问题
可重复读/串行化————————————————————————
1> 读未提交(READ_UNCOMMITED)解决丢失修改
[ 丢失修改:多个事务同时盯上了一个数据,然后各写各的,谁把谁覆盖了都不知道,总之谁写的快谁就会被覆盖丢失信息。]
为了解决丢失修改的写覆盖问题,未提交读规定:
1.事务A对当前被读取的数据不加锁
2.事务A开始更新一行数据时,必须先对其加共享锁,直到事务结束才释放
从第二点就可以看出,事务A在写入数据的时候加了共享锁,其他事务只能读,不能写,所以事务A在写的过程中也就不会被覆盖,从而就解决了丢失修改的问题
然而根据未提交读的原理, 其会引入新的问题:脏读
2> 读已提交(READ_COMMITED)解决脏读
[ 脏读:某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个回滚了操作,则后一个事务所读取的数据就会是不正确的。比如银行不小心转了一个亿到你的账户,银行人员发现幸好还没提交,此时银行人员马上回滚撤销操作,但你却在其之前取读发现自己账户居然有一个亿,这就是脏读 ]
为了解决未提交读下的脏读问题,提交读规定:
1.事务A对当前被读取的数据加共享锁,一旦读完该行,立即释放该共享锁(注意是读完立即释放)
2.事务A在更新某行数据时,必须对其加上排他锁,直到事务结束才释放(注意是事务结束才释放)
从第二点就可以看出,事务A在写入数据时加了排他锁,意味着其他事务根本就不能读到A更新但未提交的数据,如果把A换成银行,其他事务换成你,那么在上面的例子中你就不可能读到银行转了但未提交的一个亿,所以也就避免了脏读。因为排它锁从根本上使得事务只能读取到已提交的数据。
然而根据提交读的原理,其会引入新的问题:不可重复读
3> 可重复读(REPEATABLE READ)解决不可重复读
[ 不可重复读:两次读之间有别的事务修改,导致读的结果不一致,比如你两次快速读支苻宝之间你的花呗迅速还款并提交了数据,这样你会发现你第二次读突然少了一笔钱,这就是不可重复读。当然有些时候不可重复读根本不是个问题,比如你会觉得少一笔钱很正常,猜得到是花呗还了。但有些时候不行,举个例子,A和B跑马拉松,本来都是100KM,但在A和B同时读取跑步距离的一瞬间,赛委会调整了比赛距离为20KM并提交了数据,此时一个人就知道跑20KM,另一个则会继续跑100KM ]
为了解决不可重复读,可重复读规定:
1.事务A在读取某数据时,必须先对其加共享锁,直到事务结束才释放(注意是事务结束才释放)
2.事务A在更新某数据时,必须先对其加排他锁,直到事务结束才释放(注意是事务结束才释放)
从第一点就可以看出,事务A在加的是共享锁,并且要到事务结束才会释放该锁,也就意味着A在两次读取数据期间,其他事务不能对该数据进行更改,也就不会出现上面跑马拉松时对比赛距离的修改,从而解决了不可重复读
然而根据可重复读的原理,其会引入新的问题:幻读
4> 串行化(SERIALIZABLE)解决幻读
[ 幻读:两次读之间有别的事务增删,比如事务A想统计年薪100W以上的有多少人,当A两次读数据之间有其他事务新添加了一个CTO的记录,他的年薪也是100W+,所以A第二次读取到的数据突然多了一个,仿佛出现了幻觉一般,这就是一种幻读 ]
为了解决幻读,串行化规定:
1.事务在读取数据时,必须先对其加表级共享锁(注意这里是表级) ,直到事务结束才释放;
2.事务在更新数据时,必须先对其加表级排他锁(注意这里是表级) ,直到事务结束才释放。
从表级锁就可以看出,通过在一次操作中对整张表进行加锁,从而其他事务对整张表既不能insert,也不能delete,所以不会有行记录的增加或减少,从而保证了当前事务两次读之间数据的一致性,解决了幻读问题
然而根串行化的原理,其会导致写冲突,因此并发度急剧下降,一般不推荐使用该隔离级别。
盘一盘MySQL事务和锁相关推荐
- 五、Mysql事务以及锁原理
五.Mysql事务以及锁原理 事务(ACID) 场景:小明向小强转账10元 原子性(Atomicity) 转账操作是一个不可分割的操作,要么转失败,要么转成功,不能存在中间的状态,也就是转了一半的这种 ...
- MySQL事务及锁机制大揭秘 - 公开课笔记
Spring事务和数据库事务有什么区别? Spring提供了一个类,由这个类以AOP的方式管理,只需要@Transactional即可 为什么要有事务? 事务的基本概念:要不然全成功,要不然全失败,为 ...
- 超详细图解!【MySQL进阶篇】MySQL事务和锁
ACID 特性 在关系型数据库管理系统中,一个逻辑工作单元要成为事务,必须满足这 4 个特性,即所谓的 ACID: 原子性(Atomicity).一致性(Consistency).隔离性(Isolat ...
- Mysql事务和锁原理
一.mysql事务:MySql开启事务:begin. 1.并发事务产生的读问题 1)更新丢失:后提交事务会覆盖先提交的事务.乐观锁可解决. 2)脏读:A读到B未提交update数据.不符合一致性 3 ...
- mysql如何实现读提交锁_MySQL学习笔记(二)—MySQL事务及锁详解
一.事务 数组库的一组操作,要么全部成功,要么全部失败 举例:银行转账 A账户向B账户转100 A账户余额扣去100 B账户余额增加100 上述两个操作要么全部成功,要么全部失败,部分成功或失败,数据 ...
- mysql事务和锁InnoDB(转)
背景 MySQL/InnoDB的加锁分析,一直是一个比较困难的话题.我在工作过程中,经常会有同事咨询这方面的问题.同时,微博上也经常会收到MySQL锁相关的私信,让我帮助解决一些死锁的问题.本文,准备 ...
- javaweb(三十八)——mysql事务和锁InnoDB(扩展)
MySQL/InnoDB的加锁分析,一直是一个比较困难的话题.我在工作过程中,经常会有同事咨询这方面的问题.同时,微博上也经常会收到MySQL锁相关的私信,让我帮助解决一些死锁的问题.本文,准备就My ...
- 面试中的老大难-mysql事务和锁,一次性讲清楚!
转自:https://juejin.cn/post/6855129007336521741 众所周知,事务和锁是mysql中非常重要功能,同时也是面试的重点和难点.本文会详细介绍事务和锁的相关概念及其 ...
- 深入理解Mysql - 事务与锁原理
一.事务的定义 事务是数据库管理系统执行过程中的一个逻辑单位,有一个有限的数据库操作序列构成.mysql 的存储引擎中只有InnoDB支持事务. 二.事务的四大特性 原子性(Atomicity):原子 ...
最新文章
- 驾校计算机岗位管理制度,驾校计算机的规章制度.doc
- document.all使用
- Unity Dotween官方案例学习
- 旋转遍历矩阵 Spiral Matrix
- gcc——预处理(预编译),编译,汇编,链接
- Git客户端(TortoiseGit)基本使用详解
- Python Django设置中文语言及时区
- 音视频技术开发周刊 | 187
- php2588,搞清楚一下必胜2588z和2582z哪个好点?都有些什么区别?内幕评测分析
- 简书 c语言 指针,C语言-指针
- hashmap初试数组大小为什么一定要是2 的倍数
- 如何做接口测试呢?接口测试有哪些工具【小白都会系列】
- java 修改mac地址_XP下修改MAC地址
- 标注数据类型及对应的监督学习方案
- 一文读懂中国历代龙纹演变(推荐收藏)
- c语言编写图书检索系统,求C语言编写图书管理系统
- 深度探索C++对象模型pdf
- php网线入侵,普通网线能poe供电吗
- 4个字母的排列组合c语言,1,2,3,4四个数字有多少种排列组合,是怎样的
- 有道云笔记 - Markdown模板(文首附markdown源码,即.md文件)
热门文章
- Docker安装Jenkins打包Maven项目为Docker镜像并运行【保姆级图文教学】
- mac修改vmware flusion网络适配器的nat配置
- 使用崩溃服务,获取不到崩溃报告怎么办
- 一大波高薪内推招聘职位来袭
- 进军“世界工厂”立成电商第五家分公司正式成立
- 11.10 VLAN实验总结
- 布林通道参数用20还是26_布林线指标参数设置为13、20、26、30、60、99,那个才是最佳?...
- 能否设置鼠标滚轮每次滚轮的行数?
- R数据分析|标准化回归系数
- 11种超好的WordPress网站分析方案