Mysql 锁 小结

MyISAM :

MyISAM存储引擎使用的锁定机制完全是由MySQL提供的表级锁定实现,MyISAM在执行查询语句(SELECT)前, 会自动给涉及的所有表加读锁 ,在执行更新操作(UPDATE、DELETE、INSERT等)前,会 自动给涉及的表加写锁 ,这个过程并不需要用户干预,因此,用户一般不需要直接用LOCK TABLE命令给MyISAM表显式加锁。



下面主要针对的是InnoDB 支持事务的存储引擎来考虑

为了实现ACID atom,consistency,insolation,durable Mysql 通过锁和 MVCC(多版本并发控制)

锁 (行锁)
S (share 锁) 即读锁
X(exclusive Lock 排它锁) 即写锁
IX(Intension Exclusive Lock) select * from xxx for update;
IS (Inetension Share Lock) select * from xxx LOCK IN SHARE MODE

上面 两语句不属于SQL规范

另外 悲观锁和 乐观锁是程序实现的参考 https://blog.csdn.net/top_code/article/details/56842746

摘抄部分:

乐观锁与悲观锁的区别
乐观锁的思路一般是表中增加版本字段,更新时where语句中增加版本的判断,算是一种CAS(Compare And Set)操作,商品库存场景中number起到了版本控制(相当于version)的作用( AND number=#{number})。

悲观锁之所以是悲观,在于他认为本次操作会发生并发冲突,所以一开始就对商品加上锁(SELECT … FOR UPDATE),然后就可以安心的做判断和更新,因为这时候不会有别人更新这条商品库存 java的synchronized 就是悲观锁

除了上面说道的锁还有更多的解决方案 redis 和zookeper 实现分布式锁 (再做研究) https://blog.csdn.net/u010963948/article/details/79006572



数据会出现“脏读”,“不可重复读”,“幻读”


Mysql 一共有4个隔离级别


1. read uncommit
读不加锁 写会加x锁

读包括 : select
写包括
select * from xxx for update;
select * from xxx LOCK IN SHARE MODE (S 锁)
update ,delete, insert

根据索引的不同,加锁的情况也不尽相同
切换隔离级别 : set session transaction isolation level read uncommitted;

此模式下 读不加锁
写: 1) 如果没有索引,那么会先锁定所有记录,然后在Mysql server 端对不满足条件的进行解锁 不满足二段锁的概念 2) 如果有索引会针对特定的记录加锁X

2.read commit
读不加锁 写会加锁,和上面一个不同的是,其中使用了MVCC 功能 , MVCC 记录了 tx_id, history_id
通过view log 可以实现 能看到什么和不能看到什么

我直接截了一部分别人写的: 摘自 http://www.cnblogs.com/chenpingzhao/p/5065316.html
MVCC实现原理
MVCC主要在RR和RC隔离中使用。A、B客户端所示的数据相互隔离,互相更新不可见

了解innodb的行结构、Read-View的结构对于理解innodb mvcc的实现由重要意义
innodb存储的最基本row中包含一些额外的存储信息 DATA_TRX_ID DATA_ROLL_PTR DB_ROW_ID ,DELETE BIT
  • 6字节的DATA_TRX_ID 标记了最新更新这条行记录的transaction id,每处理一个事务,其值自动+1
  • 7字节的DATA_ROLL_PTR 指向当前记录项的rollback segment的undo log记录,找之前版本的数据就是通过这个指针
  • 6字节的DB_ROW_ID,当由innodb自动产生聚集索引时,聚集索引包括这个DB_ROW_ID的值,否则聚集索引中不包括这个值.,这个用于索引当中
  • DELETE BIT位用于标识该记录是否被删除,这里的不是真正的删除数据,而是标志出来的删除。真正意义的删除是在commit的时候

关于 ROW_ID 摘自 Mysql文档 ( https://dev.mysql.com/doc/refman/5.5/en/innodb-index-types.html

If the table has no  PRIMARY KEY  or suitable  UNIQUE  index,  InnoDB   internally generates a hidden clustered index named  GEN_CLUST_INDEX  on a synthetic(联合的) column containing row ID values. The rows are ordered by the ID that  InnoDB  assigns to the rows in such a table. T he row ID is a 6-byte field that increases monotonically as new rows are inserted. Thus, the rows ordered by the row ID are physically in insertion order.(没有自增长key的时候这个隐藏的一栏就负责自增长 6byte长度 6*8=48位
2^48位 够)


具体的执行过程
begin-> 用排他锁锁定该行 ->记录redo log->记录undo log->修改当前行的值,写事务编号,回滚指针指向undo log中的修改前的行



上述过程确切地说是描述了UPDATE的事务过程,其实undo log分insert和update undo log,因为insert时,原始的数据并不存在, 所以回滚时把insert undo log丢弃即可,而update undo log则必须遵守上述过程

[redo Log 也是一个比较重要的模块 能够实现mysql断电恢复,还有一个题外话的log 叫binlog 可以实现增量备份 好东西]


插入简介: read_view


read_view 保存了当前事务开启时整个MySQL中所有活跃事务列表(预示着 这个是会变的),如下图所示,在当前事务开启的时候,系统中活跃的事务有trx4、trx6、trx7以及trx10。另外,up_trx_id表示当前事务启动时,当前事务链表中最小的事务ID;low_trx_id表示当前事务启动时,当前事务链表中最大的事务ID。




read_view是实现MVCC的一个关键点,它用来判断记录的哪个版本对当前事务可见。如果当前事务要读取某行记录,该行记录的版本号(事务ID) 为trxid,那么

1. 如果trxid < up_trx_id,说明该行记录所在的事务已经在当前事务创建之前就提交了,所以该行记录对当前事务可见。
2. 如果trxid > low_trx_id,说明该行事务所在的事务是在当前事务创建之后才开启,所以该行记录对当前事务不可见。
3. 如果up_trx_id < trxid < low_trx_id, 那么表明该行记录所在事务在本次新事务创建的时候处于活动状态。从up_trx_id到low_trx_id进行遍历,如果trxid等于他们之中的某个事务id的话,那么不可见,否则可见。
以下面行记录为例,该行记录存在多个版本(trx2、trx5、trx7以及trx12),其中trx12是最新版本。看看该行记录中哪个版本对当前事务可见。
1. 该行记录的最新版本为trx12,与当前事务read_view进行对比发现,trx12大于当前活跃事务列表中的最大事务trx10,表示trx12是在当前事务创建之后才开启的,因此不可见。
2. 再查看该行记录的第二个最新版本为trx7,与当前事务read_view对比发现,trx7介于当前活跃事务列表最小事务ID和最大事务ID之间,表明该行记录所在事务在当前事务创建的时候处于活动状态,在活跃列表中遍历发现trx7确实存在,说明该事务还没有提交,所以对当前事务不可见。
3. 继续查看该记录的第三个最新版本trx5,也介于当前活跃事务列表最小事务ID和最大事务ID之间,表明该行记录所在事务在当前事务创建的时候处于活动状态,但遍历发现该版本并不在活跃事务列表中,说明trx5对应事务已经提交(注:事务提交时间与事务编号没有任何关联,有可能事务编号大的事务先提交,事务编号小的事务后提交),因此trx5版本行记录对当前事务可见,直接返回。

一言以蔽之 : 说白了 就是从undo log 中拿出一个一个的版本【tx12,tx7 tx5 tx2】和read_view【tx4,tx6,tx7,tx10】进行比较

1)当前事务版本是tx10 另外一个事务修改了同一个记录最新的版本是tx12 在本事务来看不可见
2)tx7 小于当前事务tx10 说明 在本事务创建之前就已经存在 ,查看read_view 【是否处于活跃状态】发现tx7 还处于活跃状态,那么说明tx7 对当前不可见 【如果更新了read_view 那么tx7的可见性就不一样了】
3)tx5 不再 read view 中说明 已经提交了 所有可以看到得到 ,tx2 比最小的 tx4还小 那就不用比较了 可见!

read commit 不脏读的原因就是有这个 read_view

3. repeatable read 可重复读
和RC 不同点仅仅在于read view 是从begin 开始就不更新,直到 整个事务提交或者 rollback
RC 是每次select 的时候都会生成一个新的 read_view所以RC 会出现不可重复读的情况

4.Serialiable 序列化)
读加锁 写加锁 性能最差 安全性最高


怎么查看查看锁的情况



在InnoDB 1.0 版本之前,用户只能通过SHOW FULL PROCESSLIST,SHOW ENGINE INNODB STATUS来查看当前数据库中锁的请求,然后在判断事物锁的情况,从InnoDB 1.0开始。在I nformation_schema 架构下添加了表 INNODB_TRX(查看事务) INNODB_LOCKS(查看事物的锁) INNODB_LOCK_WAITS(查看锁的等待状态) 。通过这三张表,用户可以更方便的监控到当前事务并发分析可能存在的锁问题。看一下INNODB_TRX结构
( 该表只是显示了当前运行的innoDB事务 )


information_schema.innodb_locks:







猜你喜欢

转载自blog.csdn.net/successdd/article/details/80021104