Myql事务与锁

12 篇文章 0 订阅

事务

简单的讲就是保证一组sql 能同时成功 或者失败

mysql 只有Innodb 支持事务

事务回滚原理==通过undo-log 实现,大概结构看下图
在这里插入图片描述

事务特性:ACID

原子性: 事务内的sql执行状态是统一的 要不都成功 要不都失败;通过redo-log 日志实现,只有提交后才持久化到磁盘,未提交的都记录为日志

隔离性: 指多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果;通过多版本控制MVCC实现

持久性: 指事务所提交后对数据库所作的更改便持久的保存在数据库之中,并不会被回滚;

一致性: 事务的最终追求,开启事务执行sql 提交成功 与不开事务执行sql成功后的最后数据是一致的,不会因为事务而改变结果,上面三个特性保证事务的一致性

mysql 事务隔离级别
Read uncommitted: 读未提交,普通查询不带锁,可以查询到别的事务未提交的结果,简称脏读;

Read Committed: 读已提交,普通查询不带锁,只能查询到已提交的结果;

Repeatable Reads: 可重复读,mysql默认级别,普通查询不带锁,查寻到当前事务开始之前已提交的结果;

Serializable:串行化,多个并发事务会相互影响时,后面的事务操作必须等前面的事务完成后才能进行;(并发差就是等于普通查询自带读锁)

相关查询sql

1.查看当前会话隔离级别
select @@tx_isolation;
2.查看系统当前隔离级别
select @@global.tx_isolation;
3.设置当前会话隔离级别 
set session transaction isolation level Serializable
4.设置系统当前隔离级别
set global transaction isolation level repeatable read;

不同隔离级别会出现的现象(mysql的Innodb 可重复读 不会出现幻读,使用间隙锁解决)
在这里插入图片描述
脏读: 读取到别的事务未提交的数据,万一别的事务执行失败回滚,读取的数据就不正确了;

不可重复读:在一个事务内多次读取同一个数据出现不一样的结果;(在A事务内,B事务未提交时读取一次数据与B提交成功后在读取一次,两次数据不一致,需要隔离级别为读已提交才会出现)

幻读: 在同一事务中,同一查询多次进行时候,由于其他插入操作(insert)的事务提交,导致每次返回不同的结果集(mysql Innodb 在默认隔离级别基本不会出现幻读,通过间隙锁解决,只有在当前事务恰好更新别的事务新增进来的记录时,在查找被更新的记录才会出现幻读,原因是该记录的事务被改为当前事务了)

总结:mysql 只有Innodb 引擎支持事务,事务不同隔离级别会出现不同的数据读取不一致现象,隔离实现方式是多版本控制(MVCC 快照方式)

锁(Mysql5.644版本)

读锁 :又称读共享锁,允许持有读锁的线程读取对应数据
写锁 :锁住后不允许其他带锁进程获取数据;

不管读锁写锁 针对不带锁的查询都是可以查到数据的,mysql只有在Serializable 隔离级别下并且autocommit=1时默认查询才会带读锁

主动加锁

 #增 删 改 都会自动带写锁 
 select .....  lock in share mode;  #主动加读锁
 select......  for update; #主动加写锁

表锁 :直接锁整张表,影响到表数据的更新都会阻塞,并发性能比较差;

页面锁 :锁同一个数据页的所有数据,就是读取硬盘的最小单位,innodb 一般是16k;

行锁: :
行锁只针对innodb,通过锁定索引实现,没有使用二级索引会直接锁主键索引,使用了二级索引会同时锁住二级索引跟主键索引,锁的数据跟释放锁的逻辑比较复杂,跟隔离级别,查找方式,数据结果等都有关联

行锁又包含下面的具体实现

1:记录锁, 当查询使用到索引并且能准确锁定对应行,用 ‘’=" 或者 in() 精确查询时会使用记录锁,只锁住当前的行主键索引;

2:间隙锁(只有在可重复读隔离级别有,条件:查找数据为空)间隙就是没有数据的索引空间,当查询使用到索引并且使用等值或者范围索引时,如 ="= >= <="等并且查找数据为空时则会使用间隙锁,直接锁住查找范围左右临近第一个有值的所有间隙,目的是防止新数据插入,出现幻读

3:临键锁(next key lock范围查找,有数据并且有间隙),就是间隙锁+记录锁组合一起

间隙锁跟临建锁是Innodb 为了解决幻读设计,只有隔离级别在可重复读以下时出现,加锁机制就是为了解决幻读,比如 id>10 那你往后面加数据是可能出现幻读的 这时就不能加,id>5&&id<20 往后面加数据是不会影响这个查询的 索引只需要锁id>5&&<20的所有空隙行,就是不能从中间添加一行

锁什么时候获取什么时候释放?

Myisam: 开始执行sql 前会获取所有需要的表锁,执行完所有整条sql 后统一释放所有锁,所以Myisam 引擎不会出现死锁

Innodb: 在Indb里锁必须是针对事务来说的,没开启事务的sql 则按自动提交(执行完sql 就提交)处理,在事务中顺序获取对应的锁,只有在事务提交或者回滚后才统一释放所有的锁,所以Innodb 是可能出现死锁的;

死锁案例:

#事务A 执行下面的sql 暂未提交
BEGIN;
SET * FROM member WHERE uid=1 FOR UPDATE;  #先锁住id=1

#事务B 执行面的sql 暂未提交
BEGIN;
SET * FROM member WHERE uid=2 FOR UPDATE;   #锁住id=2
SET * FROM member WHERE uid=1 FOR UPDATE;   #等待id=1 的锁

#此时事务A 又执行下面sql 就会出现死锁
SET * FROM member WHERE uid=2 FOR UPDATE;   #等待id=2 的锁

Innodb解决死锁是通过检测循环依赖实现,一旦检测到死锁会选择影响行数比较少的事务直接回滚后抛出异常,并不会一直阻塞

锁跟事务的联系:
仅在Innodb 中 事务的一致性是需要通过锁实现,事务处理不好可能出现死锁,锁可以解决事务中数据不一致的情况

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值