事务
事务(Transaction)是一组SQL组成的执行单元(unit),是数据库并发控制和恢复回滚的基本单位
一个事务可能包含多个SQL,要么都失败,要么都成功
事务具备4个基本属性:
Atomic,同一个事务里,要么都提交,要么都回滚
Consistency,即在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏
Isolation,并发事务间的数据是彼此隔离的
Durability,事务提交后,所有结果务必被持久化
支持事务的引擎:InnoDB、TokuDB、MyRocks
不支持事务的引擎:MyISAM、MEMORY/HEAP
mysql> show enginesG
显示开始事务
START TRANSACTION
[READ WRITE] 默认
[WITH CONSISTENT SNAPSHOT]
[READ ONLY]
BEGIN/BEGIN WORK
开启/关闭自动提交
set autocommit | @@autocommit = 0 | 1
提交事务
显示提交
COMMIT
隐式提交
BEGIN/BEGIN WORK
START TRANSACTION
SET AUTOCOMMIT = 1
其他非事务语句(DDL/DCL)
回滚事务
显式回滚
ROLLBACK
隐式回滚
连接断开,mysql > exit;
超时断开,mysql > timeout; 锁等待超时
被kill,mysql > kill x;
异常宕机
autocommit = 0 必要吗
好处:
多语句提交时,不会每个SQL单独提交,提高事务提交效率
麻烦:
有个事务忘记提交,锁一直未释放
另一个事务长期锁等待,严重影响tps
Python中的autocommit
import MySQLdb
from time import sleep
conn = MySQLdb.connect(host='1.2.3.4',port=3306,user='app')
conn.autocommit = True
cur = conn.cursor()
cur.execute("show global status")
while 1:
sleep(1)
是Python方法,而不是数据库的
cur.execute("set autocommit=1") 才是数据库的
事务隔离级别
如果没有事务控制的话,那么并发读写数据库会有什么隐患
脏读:
事务T1修改了一行数据,事务T2在事务T1提交之前读到了该行数据
不可重复读:
同样的条件,读取过的数据,再次读取出来发现值不一样了,更改数据(修改或删除)
幻读:
同样的条件,第1次和第2次读取出来的记录数不一样,新增数据
丢失更新:
第一类丢失更新,即回滚导致的另一个事务的更新丢失
第二类丢失更新,即事务A覆盖事务B已经提交的数据
Read Uncommitted (读未提交) 隔离级别最低
允许脏读,允许事务查看其他事务所进行的未提交的更改
Read Committed (读已提交)
允许幻读,不可重复读,允许事务查看其他事务所进行的已提交的更改
Repeatable Read (可重复读)
消除了脏读,不可重复读,幻读,保证事务一致性
确保每个事务的读取结果总是一样,默认隔离级别
Serializable (串行) 隔离级别最高
串行化读,每次读都需要获得表级共享锁,读写间相互都会阻塞
隔离级别和可能发现的现象总结如下:
my.cnf配置
[mysqld]分段中,加入一行
transaction-isolation = "READ-COMMITTED" #默认值是REPEATABLE-READ
在线(全局)修改
set [global] transaction isolation level read committed;
查看当前隔离级别
select @@global.transaction_isolation,@@session.transaction_isolation;
https://blog.csdn.net/qq_37771475/article/details/86493631 很详细
InnoDB读
快照读,snapshot read
基于read view读可见版本,不加锁
start transaction with consistent read + select
普通select
由基于某个时间点的一组InnoDB内部(活跃)事务构建而成的列表
当前读,current read
读(已提交的)最新版本,并加锁
select ... for update/lock in share mode
DML
InnoDB只读事务
5.6开始支持
5.7进一步优化,不记录redo log
5.7起,非显示声明的事务,默认都是以只读模式启动,事务过程中有数据被修改时,才自动变更为读写模式
显式声明的只读事务时innodb_trx.trx_is_read_only = 1
mysql> start transaction read only;
mysql> select * from t1;
InnoDB是如何解决幻读的
RR级别下解决了幻读问题
引入gap lock,把2条记录中间的gap锁住,避免其他事务写入
存在幻读的条件:
<= RC级别
innodb_locks_unsafe_for_binlog = 1
8.0版本innodb_lock_unsafe_for_binlog没有这个参数
InnoDB semi-consistent read
semi-consistent read是read committed与consistent read的结合
一个update语句,如果读到一行已经加锁的记录,此时InnoDB返回记录最近提交的版本,由MySQL上层判断此版本是否满足update的where条件。若满足,则MySQL会重新发起一次读操作,此时会读取行的最新版本(并加锁)
semi-consistent read发生的条件:
<= read committed隔离级别
innodb_locks_unsafe_for_binlog = 1 时
update请求(不含insert、delete)