MySQL之锁


一、概述

事务的隔离性由锁来实现
锁是计算机协调多个进程或线程并发访问某一资源的机制。在程序开发中会储存在多个线程同步问题,当多个线程并发控制访问某一个数据时,我们就需要保证这个数据最多只有一个线程在访问,保证数据的一致性和完整性。

二、锁的不同角度的分类

1.共享锁(读/S锁)和排他锁(写/X锁)

  • 共享锁(读锁):针对于同一份数据,多个事务的读操作可以同时进行而不会影响,相互不阻塞。
  • 排他锁(写锁):当写入操作没有完成之前,他会阻断其他写锁和读锁。可以确保在给定的时间之内,只有一个事务可以执行写入,并防止其他资源读取正在写入的同一资源。
    对于InnoDB引擎来说,读锁和写锁可以加在表上,也可也加在行上。
    ①锁定读
  • 对于读取的记录加S锁
SELECT ... LOCK IN SHARE MODE;SELECT ... FOR SHARE;
  • 对于读取的记录加X锁
SELECT ... FOR UPDATE;

②写操作
针对于DELETE和UPDATE我们都要加上X锁,但是对于INSERT我们不需要加索,通过另一种称之为隐式锁的结构来保护这条记录。

2.表级锁、页级锁和行锁

1.表锁(TABLE LOCK)

该锁会锁定整张表(MySQL的基本锁策略),不依赖存储引擎,可以很好的避免死锁问题。但是出现资源占用的概率会更高,导致并发性降低。
①意向锁
意向锁分为两种:

  • 意向共享锁:事务有意向对表中的某些行加共享锁
SELECT column FROM table ... LOCK IN SHARE MODE;
  • 意向排他锁:事务有意向对表中的某些行加排他锁
SELECT column FROM table ... FOR UPDATE;

意向锁要解决的问题:
如果我们给某一行数据加上了排他锁,数据库会自动给更大的空间,加上意向锁,告诉其他数据页或数据表已经存在排他锁。
②自增锁
可以为表的某个列添加AUTO_INCREMENT属性。在插入语句时没有显示赋值,系统会自动的为他赋上递增的值。
一个事务在持有AUTO_INC锁的过程种,其他事务的插入语句都要被阻塞,可以保证一个语句中分配的递增值是连续的。
innodb_autoinc_lock_mode = 0("传统"锁定模式):争夺锁
innodb_autoinc_lock_mode = 1("连续"锁定模式):对于插入的行数事先已知
innodb_autoinc_lock_mode = 2("交错"锁定模式):所有的insert语句都不会使用AUTO_INC锁,可以同时执行多个SQL语句,只会保证所有的insert语句中是唯一单调递增的,为任何给定语句插入的行生成的值可能不是连续的。
③元数据锁(MDL锁)
当对一个表做增删改查的时候,加MDL读锁;当要对表做结构变更操作的时候加MDL写锁
不需要显式使用会,在访问一个表的时候会自动加上。

2.行锁

行锁被称为记录锁,行级锁只在存储引擎实现
优点:锁定力度小,发生锁冲突的概率低,并发性高。
缺点:对于锁的开销较大,容易出现死锁。
①记录锁
只把一条记录加上锁,对周围数据没有影响。
记录锁分为s型记录锁和x型记录锁
②间隙锁
间隙锁只为了防止幻影记录而出现的。如果对一条记录加了间隙锁,并不会限制其他事务对这条记录的继续加索或者继续加间隙锁

  • infimum记录,表示页面中最小的记录。
  • Supremum记录,表示页面中的最大记录。
  • 我们可以给这两个值加上间隙锁,来解决幻影问题。
    ③临键锁
    既可以锁住某条记录又可以阻止其他事务在该记录前面的间隙插入新的记录。
    本质就是一个记录锁+间隙锁。
    ④插入意向锁
    插入意向锁之间互不排斥,所以即使多个事务在同一区间插入多条记录,只要记录本身不冲突,那么事物之间就不会出现等待冲突。
    虽然插入意向锁中含有意向锁三个字,但他并不属于意向锁而属于间隙锁。

3.页锁

页锁就是在页的粒度上进行锁定,锁定的数据资源比行锁要多,因为一个页中可以有多个行记录。
页锁的开销介于表锁和行锁之间,会出现死锁。锁定颗粒度介于表锁和行锁之间,并发度一般。

3.乐观锁和悲观锁

1.悲观锁

悲观锁总是假设最坏的情况,每次去修改数据的时候都认为别人会去修改,所以在每次拿到数据的时候都会上锁。如果别人想要拿到这个数据就会遇到阻塞直到拿到锁(共享资源每次只给一个线程使用,其他线程阻塞,用完后再把资源转让给其他线程)。

2.乐观锁

乐观锁认为统一数据的并发操作并不会总会发生,属于小概率事件,不用每次都对数据上锁,但是在更新数据的时候会判断一下啊在此期间别人有没有去更新这个数据。我们可以采用版本号机制或CAS机制实现。乐观锁适用于多读类型的数据。
①乐观锁的版本号机制
在表中设计一个版本号字段version,第一次读取数据的时候,就会读取版本号,然后读数据进行更新或删除操作,如果修改时vsersion=version相等,则修改成功,版本号不相等就修改失败。
②乐观锁的时间戳机制
时间戳和版本号一样,也是在更新和提交的时候,将当前数据的时间戳和更新之前的时间戳进行并比较,如果两者一致则更新成功,否则就是版本冲突。

乐观锁适合读操作多的场景,它的优点在于程序实现,不存在死锁问题。
悲观锁适合写操作多的场景,因为写操作具有排它性。

4.全局锁

就是对整个数据库实例加锁。当你需要让整个数据库处于只读的状态的时候,可以使用这个命令:

Flush tables with read lock

5.死锁

死锁是指两个或多个事务在同一资源上互相占用,并请求锁定对方占用的资源,从而导致恶性循环。

产生死锁的必要条件

①两个或两个以上的事务
②每个事务都已经持有锁并且申请新的锁
③锁资源同时只能被同一个事务持有或不兼容
④事物之间因为持有锁和申请锁导致彼此循环等待。

如何处理死锁

①等待,直到超时。
即当两个事务互相等待时,当一个事务等待时间超过设置的阈值时,就将其回滚,另外的事务继续执行。
②使用死锁检测进行死锁处理
innodb提供了一种主动的死锁检测机制(wait-for graph算法),要求数据库保存锁的信息链表和事务等待链表两部分信息。

如何避免死锁

  • 合理设计索引,是业务SQL尽可能通过索引定位更少的行,减少锁竞争。
  • 调整业务逻辑的SQL执行顺序,避免长时间持有锁的sql在事务前面。
  • 避免大事务,尽量将大事务拆成多个小事务来处理,小事务缩短锁定资源的时间,发生锁冲突的几率页更小。
  • 在并发性较高的系统中,不要显式加锁,特别是在事务里显示的加锁。
  • 41
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值