MySQL数据库事务
2023-09-08 16:08:45 0 举报
Mysql事务与锁的基础知识
作者其他创作
大纲/内容
事务隔离级别:
READ UNCOMMITTED(读取未提交内容)
所有事务都可以“看到”未提交事务的执行结果
“脏读”就是读取未提交的数据
存在并发问题
READ COMMITTED(读取提交内容)
支持不可重复读(Nonrepeatable Read)
解决脏读
REPEATABLE READ(可重读)
出现“幻读”(Phantom Read)
解决不可重复读(两次读取,中间可能被别人修改了),提交覆盖
不过,在InnoDB 和Falcon 存储引擎下通过多版本并发控制(MVCC,Multiversion Concurrency Control)解决了幻读
MVCC
MVCC 是通过保存数据在某个时间点的快照实现的
实现了非阻塞读,写操作只锁定必要行
只在MySQL的两个隔离级别下工作
Read Committed
Repeatable Read
MVCC的三种实现方式:
隐藏字段(DB_ROW_ID、DB_TRX_ID、DB_ROLL_PTR)
回滚日志(undo log)
一致性读(consistent read)
MVCC的类型
快照读
当前读
工作原理
DB_ROW_ID 隐含ID
DB_TRX_ID 事务ID
DB_ROLL_PTR 回滚指针
MySQL的默认隔离级别
SERIALIZABLE(可串行化)
最高级别的隔离级
通过强制事务排序,使之不发生冲突
InnoDB的查询也会被加上行锁。如果查询的是一个范围,那么该范围内的所有记录行包括每行记录所在的间隙区间范围都会被加锁,即使该行数据还没有被插入。
解决幻读(两次读取,中间被别人插入了,第二次读的更多或更少)
锁
对数据库操作的粒度分:
表锁
每次操作锁住整张表,开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;一般用在整表数据迁移的场景。
行锁
每次操作锁住一行数据。开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度最高。
性能上区分的锁:
乐观锁(用版本对比来实现)
通过代码实现
通过新增字段version,在update操作时,把第一次读出的version作为update的条件
框架实现
Mybatis OptimisticLockerInterceptor
@Version private int version;
悲观锁
表锁
lock table t1 read;
lock table t1 write;
show open tables;
unlock tables;
表级的读锁,会阻塞写操作,不会阻塞读操作
写锁,会阻塞读,写操作
共享锁
select ... from lock in share mode 只能读取,修改被阻塞
排他锁
select ... for update
基于索引,如果没有索引,锁全表
当前事务可以读取,修改,其他事务不能修改
从数据库操作类型区分的锁:
读锁(也是共享锁):
针对同一份数据,多个读操作可以同时进行而不会互相影响
写锁(悲观锁)
当前写操作没有完成前,阻断其他写锁和读锁
锁的分类()
显式锁
select ....... lock in share mode (加共享锁)
select ........ for update (加排他锁)
隐式锁
锁的类型
共享锁
排他锁
意向锁(Intention Lock)(表级别的)
意向共享锁
意向排他锁
一次性锁定读取
select .... for update. 加X锁
select .... lock in share mode. 加S锁
一次性非锁定读
在read committed隔离级别下,读取被锁定行的最新一份快照数据. 产生了不可重复读的问题
在repeatable read 事务隔离级别下,读取事务开始时的行数据版本. 解决不可重复读的问题
死锁
InnoDB 锁算法
InnoDB 行锁通过对索引数据页上的记录加锁实现
InnoDB 行锁的三种方式:
Read LOCK 行锁(RC和RR)
单个行记录的锁
GAP LOCK 间隙锁(RR)
锁定一个范围,但不包含记录本身.
间隙锁在可重复读隔离级别下才会生效
主键加锁
仅在id=10的主键索引记录上加X锁
唯一主键:
现在唯一索引id上加X锁,然后再id=10的主键索引上加X锁
非唯一主键:
对满足id=10的条件的记录和主键分别加X锁,然后再(6,c)-(10,b) ,(10,b)-(10,d),(10,d)-(11,f) 范围分别加GapLock。
无索引:
表里所有行和间隙都会加X锁
当没有索引时,会导致全表锁定,因为Innodb引擎锁机制是基于索引实现的记录锁定
Next-Key Lock(RR)
Gap Lock+Record Lock 锁定一个范围并锁定记录本身.
InnoDB 规则:
上面所说的锁定的对象均为: 索引记录. 如果InnoDB存储引擎在建立的时候没有设置任何一个索引,那么这时,InnoDB存储引擎会使用隐式的主键进行锁定
当查询的索引含有唯一属性时,InnoDB存储引擎会对Next-Key Lock进行优化,降级为Record Lock
InnoDB在不同隔离级别下产生"不可重复读" 和 "幻读" 和解决它 的根本原因
InnoDB存储引擎默认的事务隔离级别(repeatable read)下,采用的是 Next-Key Locking的方式来加锁
在 RR 隔离级别,InnoDB 对记录加锁行为都是先采用 Next-key Lock,当 SQL 操作包含索引时,InnoDB 会对 Netx-key进行优化,降级为 RecordLock,仅锁住索引本身而非范围
read committed隔离级别下采用的是: Record Lock 的方式来加锁
InnoDB的行锁是针对索引加的锁,不是针对记录加的锁。并且该索引不能失效,否则都会从行锁升级为表锁
存储引擎
事务存储引擎(三种)
InnDB
Falcon
NDB Cluster
MySQL默认的事务隔离级别是可重复读,用Spring开发程序时,如果不设置隔离级别默认用MySQL设置的隔离级别,如果Spring设置了就用已设置的隔离级别
InnoDB和MyISAM最大的不同点
InnoDB支持事务
InnoDB支持行锁
MyISAM在执行查询语句前,会自动给涉及的表加读锁;执行update、insert、delete操作加写锁;
InnoDB在执行查询语句前(非串行隔离级别),不会加锁;执行update、insert、delete操作会加行锁。
读锁会阻塞写,但不会阻塞读。而写锁会把读写都阻塞。
可重复读的隔离级别下使用了MVCC(multi-version concurrency control)机制(该机制在RC和RR都有使用),select操作不会更新版本号,是快照读(历史版本);insert、update和delete会更新版本号,是当前读(当前版本)
锁主要是加在索引上,如果对非索引字段更新,行锁可能会变表锁。
锁优化建议:
尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁;
合理设计索引,尽量缩小锁的范围;
尽可能减少索引条件范围,避免间隙锁;
尽量控制事务大小,减少锁定资源量和时间长度,涉及事务加锁的sql尽量放在事务最后执行;
尽可能低级别事务隔离
事务隔离级别与锁的关系
事务隔离级别是锁的操作,锁是底层实现
先控制事务级别,如果无法解决问题再用锁解决
ACID特性
Atomicity原子
Isolation 隔离
读未提交
读提交
可重复读
可串行化
Durability持久
Binlog落地
发送Binlog
存储引擎提交
flush_logs
check_point
事务提交标记
Consistency 一致
约束一致性
数据一致性
收藏
收藏
0 条评论
下一页