MySQL(四)锁机制详解

### 锁机制

##### 🐛:

  [数据库两大神器【索引和锁】 (juejin.cn)](https://juejin.cn/post/6844903645125820424#heading-16)



#### 1. 锁介绍及类别



###### 锁介绍

​		**当数据库有并发事务的时候,可能会产生数据的不一致,这时候需要一些机制来保证访问的次序,锁机制就是这样的一个机制.**



**定心丸**:即使我们不会这些锁知识,我们的程序在**一般情况下**还是可以跑得好好的。因为这些锁数据库**隐式**帮我们加了

- 对于`UPDATE、DELETE、INSERT`语句,**InnoDB****自动**给涉及数据集加排他锁(X)
- **MyISAM**在执行查询语句`SELECT`前,会**自动**给涉及的所有表加**读锁**,在执行更新操作(`UPDATE、DELETE、INSERT`等)前,会**自动**给涉及的表加**写锁**,这个过程并**不需要用户干预**

只会在某些特定的场景下才需要**手动**加锁,学习数据库锁知识就是为了:

- 能让我们在特定的场景下派得上用场

- 更好**把控自己写的程序**

- 在跟别人聊数据库技术的时候可以搭上几句话

- **构建自己的知识库体系**!在面试的时候不虚

  

###### 类别

锁的类别上来讲,有共享锁和排他锁:.

- 共享锁: 又叫做读锁. 当用户要进行数据的读取时,对数据加上共享锁.共享锁可以同时加上多个.
- 排他锁: 又叫做写锁. 当用户要进行数据的写入时,对数据加上排他锁.排他锁只可以加一个,他和其他的排
  他锁,共享锁都相斥.
  (用例子来说就是用户的行为有两种,一种是来看房,多个用户一起看房是可以接受的. 一种是真正的
  入住一晚,在这期间,无论是想入住的还是想看房的都不可以.
  锁的粒度取决于具体的存储引擎,InnoDB实现了行级锁,页级锁,表级锁.)




#### 2.  表锁与行

##### **MyISAM 和 InnoDB 存储引擎使用的锁:**

- MyISAM 采用表级锁(table-level locking)- InnoDB 支持行级锁(row-level locking)和表级锁; (**InnoDB的行锁是基于索引的**!  具体解释为: InnoDB只有通过索引条件检索数据才使用行级锁,否则,InnoDB将使用表锁)

  

##### **表级锁和行级锁对比:**

- **表级锁:** MySQL 中锁定 **粒度最大** 的一种锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM 和 InnoDB 引擎都支持表级锁。

- **行级锁:** MySQL 中锁定 **粒度最小** 的一种锁,只针对当前操作的行进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。

  



**🐛 拓展:**

**表锁下又分为两种模式**:

表读锁(Table Read Lock)

表写锁(Table Write Lock)



在表读锁和表写锁的环境下表现为:**读读不阻塞****读写阻塞****写写阻塞**

- 读读不阻塞:当前用户在读数据,其他的用户也在读数据,不会加锁

- 读写阻塞:当前用户在读数据,其他的用户**不能修改当前用户读的数据**,会加锁!

- 写写阻塞:当前用户在修改数据,其他的用户**不能修改当前用户正在修改的数据**,会加锁

  

从以上得知:**读锁和写锁是互斥的,读写操作是串行**- 如果某个进程想要获取读锁,**同时**另外一个进程想要获取写锁。在mysql里边,**写锁是优先于读锁的**- 另外:  写锁和读锁优先级的问题是可以通过参数调节的:`max_write_lock_count`和`low-priority-updates`

  

**MyISAM可以**支持查询和插入操作的**并发**进行。可以通过系统变量`concurrent_insert`来指定哪种模式,在**MyISAM**中它默认是:如果MyISAM表中没有空洞(即表的中间没有被删除的行),MyISAM允许在一个进程读表的同时,另一个进程从**表尾**插入记录。

但是**InnoDB存储引擎是不支持的**!



#### 3.   乐观锁与悲观锁

#####    何谓乐观锁悲观锁:

-  悲观锁  :  对应于生活中悲观的人总想着事情往坏的方向发展;
-  乐观锁  : 对应于生活中乐观的人总是想着事情往好的方向发展

​     这两种人视场景而定,各有优缺点;



#####   悲观锁:

​       顾名思义,悲观锁是基于一种悲观的态度(每次去拿数据的时候都会认为别人会修改)来防止一切数据冲突,它是以一种预防的姿态在修改数据之前把数据锁住,然后再对数据进行读写,在它释放锁之前任何人都不能对其数据进行操作,直到前面一个人把锁释放后下一个人数据加锁才可对数据进行加锁,然后才可以对数据进行操作,一般数据库本身锁的机制都是基于悲观锁的机制实现的;**特点:**可以完全保证数据的独占性和正确性,因为每次请求都会先对数据进行加锁, 然后进行数据操作,最后再解锁,而加锁释放锁的过程会造成消耗,所以性能不高;

手动加悲观锁:

读锁LOCK tables test_db read释放锁UNLOCK TABLES;

写锁:LOCK tables test_db WRITE释放锁UNLOCK TABLES;



- `select * from xxxx for update`

  ​	在select 语句后边加了 `for update`相当于加了排它锁(写锁),加了写锁以后,其他的事务就不能对它修改了!需要等待当前事务修改完之后才可以修改.

- 也就是说,如果张三使用`select ... for update`,李四就无法对该条记录修改了~

  

#####  乐观锁: 

​     乐观锁是对于数据冲突保持一种乐观态度,操作数据时不会对操作的数据进行加锁(这使得多个任务可以并行的对数据进行操作),只有到数据提交的时候才通过一种机制来验证数据是否存在冲突(一般实现方式是通过加版本号然后进行版本号的对比方式实现,或者通过CAS算法实现);**特点:**乐观锁是一种并发类型的锁,其本身不对数据进行加锁通而是通过业务实现锁的功能,不对数据进行加锁就意味着允许多个请求同时访问数据,同时也省掉了对数据加锁和解锁的过程,这种方式大大的提高了数据操作的性能;

**乐观锁和悲观锁的使用场景**

  乐观锁**适用于写比较少(多读场景)**,即冲突很少发生的时候,这样省去了锁的开销,加大了系统的吞吐量;

  悲观锁 适用于多写的场景下;



##### 间隙锁GAP

当我们**用范围条件检索数据**而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给**符合范围条件的已有数据记录的索引项加锁**;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”。InnoDB会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁。

值得注意的是:间隙锁只会在`Repeatable read`隔离级别下使用~

例子:假如emp表中只有101条记录,其empid的值分别是1,2,...,100,101

Select * from emp where empid > 100 for update;

复制代码


上面是一个区间范围查询,InnoDB**不仅**会对符合条件的empid值为101的记录加锁,也会对**empid大于101(这些记录并不存在)的“间隙”加锁**, 这个时候另一个会话去插入这个区间的数据,就必须等待上一个结束。

InnoDB使用间隙锁的目的有两个:

- **为了防止幻读**(上面也说了,`Repeatable read`隔离级别下再通过GAP锁即可避免了幻读)
- 满足恢复和复制的需要
- MySQL的恢复机制要求:**在一个事务未提交前,其他并发事务不能插入满足其锁定条件的任何记录,也就是不允许出现幻读**







##### 死锁

并发的问题就少不了死锁,在MySQL中同样会存在死锁的问题。

但一般来说MySQL通过回滚帮我们解决了不少死锁的问题了,但死锁是无法完全避免的,可以通过以下的经验参考,来尽可能少遇到死锁:

- 1)以**固定的顺序**访问表和行。比如对两个job批量更新的情形,简单方法是对id列表先排序,后执行,这样就避免了交叉等待锁的情形;将两个事务的sql顺序调整为一致,也能避免死锁。
- 2)**大事务拆小**。大事务更倾向于死锁,如果业务允许,将大事务拆小。
- 3)在同一个事务中,尽可能做到**一次锁定**所需要的所有资源,减少死锁概率。
- 4)**降低隔离级别**。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁。
- 5)**为表添加合理的索引**。可以看到如果不走索引将会为表的每一行记录添加上锁,死锁的概率大大增大。







##### 如何保证事务的一致性和隔离性的,同时最大程度地并发(OCC和MVCC)?

​       数据库中,并发控制是指在多个用户/进程/线程同时对数据库进行操作时,如何保证事务的一致性和隔离性的,同时最大程度地并发。

当多个用户/进程/线程同时对数据库进行操作时,会出现3种冲突情形:

1. 读-读,不存在任何问题

2. 读-写,有隔离性问题,可能遇到脏读(会读到未提交的数据) ,幻影读等。

3. 写-写,可能丢失更新

   

​       乐观并发控制(OCC)是一种用来**解决写-写冲突**的无锁并发控制,认为事务间争用没有那么多,所以先进行修改,在提交事务前,检查一下事务开始后,有没有新提交改变,如果没有就提交,如果有就放弃并重试。乐观并发控制类似自选锁。乐观并发控制适用于低数据争用,写冲突比较少的环境。



​      多版本并发控制(MVCC)是一种用来**解决读-写冲突**的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照。 这样在读操作不用阻塞写操作,写操作不用阻塞读操作的同时,避免了脏读和不可重复读;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值