事务
简单的讲就是保证一组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 中 事务的一致性是需要通过锁实现,事务处理不好可能出现死锁,锁可以解决事务中数据不一致的情况