💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] # innodb锁的模式 ![](https://img.kancloud.cn/2c/df/2cdf7cab6418a8364cebeeef18e10a9a_835x280.png) ## 悲观锁(多写情况) #### 1.是什么? > 每次处理数据时, 都认为别人也会修改, 所以每次处理数据时, 先提前加锁, 再处理; ## 乐观锁(多读情况) #### &emsp;1.是什么? > 每次处理数据时, 都认为别人不会修改, 所以每次处理数据时, 都不加锁, 只是在更新数据时, 判断下别人有没有在此期间修改过数据; #### &emsp;2.两种实现方式 #### &emsp;&emsp;2.1 \. 版本号机制 > 一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。 #### &emsp;&emsp;2.2 \. CAS算法 > 即**compare and swap(比较与交换)**,是一种有名的**无锁算法**。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。 ## 共享锁(读锁 s) > 多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改 > 加锁:   select  \* from tb\_student  where id = 1 **lock  in share mode** 释放:  commit  /  rollback ## 排他锁 (写锁 x): > 排他锁就是不能与其他所并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。 > InnoDB引擎默认的修改数据语句,update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型 > 加过排他锁的数据行在其他事务种是不能修改数据的,也不能通过for update和lock in share mode锁的方式查询数据,但可以直接通过select ...from...查询数据,因为普通查询没有任何锁机制 > select  \*  from  tb\_student where id = 1 ** for update** ### 表锁 > 加表锁:       lock table \[tableName\] \[read | write\] > 释放表锁:   unlock table ###意向锁 表锁和行锁存在冲突,比如: 1. transactionA获取了tableA中某一行的共享锁,其它事务就不能再修改这行数据了 2. trsactionB如果获取了tableA的表级排它锁,就可以对tableA的所有数据修改 这里 transactionB的表锁和transactionA的行锁就是冲突的 意向锁就是为了解决行锁和表锁的冲突,意向锁本身也是表级别的锁,事务在获取行锁前,必须先获取相应的意向锁,意向锁相当于一个标记,用来显示当前是否有事务锁住了表中的某一行 事务在获取表中某一行的  共享锁 前必须先获取到 意向共享锁或者更高级别的锁 事务在获取表中某一行的 排它锁 前必须先获取到 意向排它锁 #### 意向共享锁(Is): #### 意向排他锁 (IX): ![](https://img.kancloud.cn/34/d2/34d23f2b28705b8dce9546081bd584cd_882x556.png) ### 记录锁(Record Lock) select \* from \[tb\_name\] where   id =  #{id}  for update id列上有唯一索引,并且查询条件可以唯一确定一条记录,这时候innodb使用记录锁,只会锁住查出来的这一行记录 ### 间隙锁(Gap Lock): > where后面的字段有索引,但不是唯一索引,或者使用了>,  <  等范围的查询条件时,查询条件范围内的索引值之间的间隙会被加锁,结果就是被加锁的间隙之间不能插入索引值 > 区间是左开右闭的 > 在InnoDB下,间隙锁的产生需要满足三个条件: * 隔离级别为RR * 当前读 * 查询条件能够走到索引 ### 锁(Next-key Lock) 是行锁与间隙锁的组合 # 死锁 ### 什么是死锁? > 当两个事务都需要获得对方持有的排他锁才能完成事务,这样就导致了循环锁等待,也就是常见的死锁类型 ### 产生的原因? > Mysql行级锁并不是直接锁记录,而是锁索引。索引分为主键索引和非主键索引两种,如果一条sql语句操作了主键索引,那么Mysql就会锁定这个主键索引,如果sql语句操作的是非主键索引,那么Mysql会先锁定这个非主键索引,再去锁定主键索引。 > 在UPDATE 和 DELETE操作时Mysql不仅会锁定所有WHERE 条件扫描过得索引,还会锁定相邻的键值。 ### 解决死锁的方法? > 1、  数据库参数 > 2、  应用中尽量约定程序读取表的顺序一样 > 3、  应用中处理一个表时,尽量对处理的顺序排序 > 4、  调整事务隔离级别(避免两个事务同时操作一行不存在的数据,容易发生死锁) ### 死锁的案例 > Test:(ID,STATE,TIME)  主键索引:ID  非主键索引:STATE > 当执行"UPDATE  STATE =1011 WHERE STATE=1000"  语句的时候会锁定STATE索引,由于STATE 是非主键索引,所以Mysql还会去请求锁定ID索引 > 当另一个SQL语句与语句1几乎同时执行时:“UPDATE STATE=1010 WHERE ID=1”  对于语句2 Mysql会先锁定ID索引,由于语句2操作了STATE字段,所以Mysql还会请求锁定STATE索引。这时。彼此锁定着对方需要的索引,又都在等待对方释放锁定。所以出现了"死锁"的情况。 # MVCC * ## 是什么? > `MVCC (Multiversion Concurrency Control)` 中文全称叫多版本并发控制,是现代数据库(包括 `MySQL`、`Oracle`、`PostgreSQL` 等)引擎实现中常用的处理读写冲突的手段,目的在于提高数据库高并发场景下的吞吐性能。 * ## 实现流程? #### &emsp;&emsp;1.update修改的实现流程 > 1. 通过主键(或隐藏主键 DB_ROW_ID), 对该行加排他锁 > 2. 将这条记录, 原封不到的放入`undo log` 中, 记做nudolog1; > 3. 执行修改, 会产生新的 事务, 所以在`undo log` 中产生新的一条记录,记做undolog2, 相比较undolog1, undolog2中会修改两个值; > 3.1 修改第一个值: 事务ID(DATA_TRX_ID), 将其改成新的事务ID(例如 原本nudolog1中该值是1 , 那么undolog2中改成2); > 3.2 修改第二个值: 指向回滚段指针(DATA_ROLL_PRE), 将其值指向undolog1; > 如果对该行记录执行连续的update操作, 则重复上面2,3操作, 然后`undo log` 会形成链表, 遍历这个链表可以看到这条记录的变迁; > 4. 记录 `redo log`,包括 `undo log` 中的修改 #### &emsp;&emsp;2.delete修改的实现流程 #### &emsp;&emsp;3.insert修改的实现流程 * ## 如何实现一致性读? > 1.RU隔离级别下 > 直接读版本的最新记录就可以 > 2.RC 和 RR隔离级别下 (MVCC运行在这两个级别下) > 在 `SELECT` 数据时就会用到版本链, 但是要读哪个版本下的记录就要决定于 , `ReadView`(可读视图)了 > 3.S隔离级别下 > 是通过加锁互斥来访问数据的, 用不到MVCC * # 相关命令操作