mysql 事务和锁

事务

事务是一组不可分割的数据操作的集合,要么全部成功,要么全部失败。mysql中事务由引擎层实现,并非所有引擎都支持事务(例如MyISAM引擎就不支持事务)

ACID

事务的ACID:

  1. 原子性 :事务中的操作不可分割,要么全部成功,要么全部失败;
  2. 一致性 :事务执行前后,数据总是从一致状态到达另一个一致状态,完整性没有被破坏;
  3. 隔离性 :多个事务对相同数据块的并发读写能力,定义有四个隔离级别,支持不同程度的并发;
  4. 持久性 :事务对数据的修改是永久的。

原子性、隔离性和持久性是手段,一致性是最终目的。在mysql(innodb引擎)中,原子性由undo log保证,隔离性由锁+MVCC保证,持久性由redo log+binlog保证。

隔离级别

事务隔离级别包括:

  1. 读未提交:事务还未提交的时候,它做的变更可以被其他事务看到,存在脏读
  2. 读已提交:事务提交后,它做的变更才可以被其他事务看到,存在幻读
  3. 可重复读(mysql默认):事务在执行过程看到数据总是和事务开始时看到数据一致,解决部分幻读
  4. 串行化:对于同一数据行,读操作会加读锁,写操作会加写锁,当出现读写锁冲突时,必须等待持有锁的事务结束才能执行。

隔离级别越高,数据的一致性越能得到保障,但事务的并发程度越低,生产环境通常根据实际情况选择读已提交或可重复读级别。

幻读问题

围绕幻读主要有两个问题:1.什么是幻读?2.可重复读级别下是否解决了幻读问题?

  1. 什么是幻读:在同一个事务中,针对多次相同条件的查询,后一次查到了前一次不存在的数据。
  2. 可重复读是否解决了幻读:没有彻底解决,但解决了部分场景的幻读。

解释可重复读级别下的幻读问题,先说明下数据库读操作的两种类型:1.快照读:普通的select…语句;2.当前读:读取已提交的最新值,select…lock in share mode或select…for update,以及所有的写语句。
然后我们再看下两种类型的读操作是否会产生幻读问题:
1.如果前后两次都是快照读,在MVCC机制作用下,两次查询的结果是一致的;
2.如果第一次是当前读,因为当前读会给读到的记录加next-key lock,在提交前其他事务无法插入新记录,所以不存在幻读;
3.如果第一次是快照读,第二次是当前读,两次读操作之间另一个事务插入新记录并完成提交,那么第二次读就会读到这条新记录,产生幻读;

  1. 锁加在索引记录上
  2. 二阶段锁,sql执行时加锁,事务提交时再释放
  3. 扫描过的记录都会加锁,当发生全表扫描时,在读提交模式下,不满足条件的记录会马上释放锁
    在这里插入图片描述

服务层实现的锁

  1. 全局锁:flush table with read lock 命令,使全表处于只读状态,通常用语整库备份。
  2. 表锁:lock tables… read/write
  3. 元数据锁(MDL锁):执行DML语句加MDL读锁,执行DDL语句加MDL写锁,且MDL写锁优先级高于MDL读锁,当写锁进入等待,后面读锁请求也会被阻塞,直到写锁事务完成。

InnoDB实现的锁

行级锁

  1. 行锁:锁住一条记录;
  2. gap锁:锁住两个索引之间的区间,在可重复读级别下有效,防止一定程度的幻读,gap锁之间不互斥;
  3. next-key锁:行锁 + gap锁,前开后闭的区间,可重复读级别下加锁基本单位;
  4. 插入意向锁:插入时产生,包含插入区间的区间锁和插入行的行锁;

next-key锁包含两个优化:1.唯一索引的等值查询,退化为行锁,仅锁住命中的记录;2.索引的等值查询,如果向右查询到不符合条件的记录,退化为区间锁。
以表t为例,表t包含如下数据,其中id为主键,字段a为普通索引

idab
555
101010
151515

在可重复读和读已提交两种级别下的加锁情况:

sql可重复读读提交
select * from t where a=10不加锁不加锁
select * from t where a=10 for update区间锁(5,15),包含行锁[10]行锁[10]
select * from t where a=12 for update区间锁(10,15)不加锁
select * from t where id=10 for update行锁[10]行锁[10]
select * from t where id>=10 and id<12 for updatenext-key lock (10,15],行锁[10]行锁[10]

表级锁

意向锁是InnoDB实现的一种表级锁,是InnoDB为了兼容行锁和表锁所引入的一种优化手段。
意向共享锁:对某一行数据加读锁时,需要先获取意向共享锁;
意向排他锁:对某一行数据加写锁时,需要先获取意向排它锁;
意向锁相互之间不互斥,是为了更好的支持行级并发度。
引入意向锁的目的是为了对使用InnoDB引擎的表进行锁表时提升性能:因为InnoDB支持行锁,当试图锁表时如果去逐行判断是否存在行锁显然太耗性能,因为存在行锁的前提示是存在意向锁,因此加表锁的判断逻辑简化为判断当前表是否存在意向锁即可,存在则锁表失败,否则成功锁表。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值