MySQL系列之锁

mysql锁

隔离级别与锁

  • Read Uncommitted级别下(可读到其他事务未提交的数据),读取数据不需要加共享锁,这样就不会跟被修改的数据上的排他锁冲突。
  • 在Read Committed级别下(其他事务提交操作后,才可见),读取操作需要加共享锁,但是在语句执行完以后释放共享锁。
  • 在Repeatable Read级别下(同一事务重复读取的数据一致性),读取操作需要加共享锁,但是在事务提交之前并不释放共享锁,也就是必须等待事务执行完毕以后才释放共享锁。
  • SERIALIZABLE 是限制性最强的隔离级别,因为该级别锁定整个范围的键,并一直持有锁,直到事务完成。

疑问??什么是共享锁??

共享锁=共享读锁,排他锁=独占写锁

按锁粒度,锁有哪些?

锁机制与InnoDB锁算法

在关系型数据库中,可以按照锁的粒度把数据库锁分为行级锁(INNODB引擎)、表级锁(MYISAM引擎)和页级锁(BDB引擎 )

MyISAM采用表级锁(table-level locking)。InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁。

InnoDB实现了行级锁,页级锁,表级锁。

行级锁,表级锁和页级锁对比

  • 行级锁

    锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。开销最大;加锁慢;会出现死锁;并发度最高
    分为共享锁、排他锁。

  • 表级锁

    锁定粒度最大的一种锁,表示对当前操作的整张表加锁。
    分为表共享读锁(共享锁)、表独占写锁(排他锁)。

  • 页级锁

    MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。
    表级锁速度快,但冲突多;行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。

按锁类别,锁有哪些?

  • 共享锁

    又叫做读锁。当用户要进行数据的读取时,对数据加上共享锁。共享锁可以同时加上多个

  • 排他锁

    又叫做写锁,当用户要进行数据的写入时,对数据加上排他锁。排他锁只可以加一个,他和其他的排他锁,共享锁都相斥。

行锁如何实现?

InnoDB是基于索引来完成行锁

select * from tab_with_index where id = 1 for update;

-- for update 可以根据条件来完成行锁锁定,并且 ID 是有索引键的列,如果 ID不是索引键那么InnoDB将完成表锁,并发将无从谈起

锁算法:3种

  1. Record lock:单个行记录上的锁

  2. Gap lock:间隙锁,锁定一个范围,不包括记录本身

    为了阻止多个事务将记录插入到同一范围内,这会导致幻读问题。

  3. Next-key lock:record+gap 锁定一个范围,包含记录本身

    InnoDB对于行查询采用此

何为死锁?

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

常见的解决死锁的方法

1、如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。

2、在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;

3、对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率;

如果业务处理不好可以用分布式事务锁或者使用乐观锁

分布式锁

先来看看功能:

  1. 分布式锁使用者位于不同的机器中,所获取成功后,才可以对共享资源进行操作
  2. 锁具有重入的功能:即一个使用者可以多次获取某个锁
  3. 获取锁有超时的功能:超过指定的时间,还未获取成功,则返回获取失败
  4. 自动容错功能:A机器获取锁lock1之后,在释放lock1之前,机器A挂掉了,导致锁lock1还未释放。结果就是lock1一直被机器A占有。分布式锁能自动解决:持有锁规定持有超时时间,超时还未释放时,其余机器将争夺该锁。

乐观锁

假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。在修改数据的时候把事务锁起来,通过Version的方式来进行锁定。

实现方式:一般会使用版本号机制或CAS算法实现。

在修改表中数据记录过程如下:

  1. select获取记录R1
  2. 对R1进行编辑
  3. update更新R1

存在什么问题呢?

当A、B两个线程同时执行到步骤1,这俩获取的R1数据是一致的,然后同时执行步骤2,最后执行步骤3,最终2个线程都会更新成功。

但是后一个执行的线程会把前一个线程更新update的结果给覆盖掉,这就是并发修改数据的问题

如何解决呢?

在表中新增一个版本号,每次更新数据时将版本号作为条件,执行一次更新就版本号+1,过程优化一下,如下:

  1. 打开事务start transaction

  2. select获取记录R1,声明变量v=R1.version

  3. 对R1进行编辑

  4. 执行更新update
    update R1 set version=version+1 where user_id=#user_id# and version=#v#;

  5. 步骤4中更新update返回影响的行数,将其记录在count中,然后根据count来判断提交事务?回滚事务?

    if(count==1){
    	//提交事务
    	commit;
    }else{
    	//回滚事务
    	rollback;
    }
    

步骤4中,当多个线程同时执行到步骤2时,获取到的记录R1是一样的,但当执行到步骤4时,数据库对update的这行数据加锁,确保并发情况下排队执行

故只有第一次执行update更新操作会返回1,其余线程的更新update操作回返回0,根据count值确定事务提交 / 回滚

所有线程执行select查询时,线程的数据v都是R1记录的版本:v=R1.version

但经过一次更新update后,R1记录+1,但其余线程的数据v还是原始的version数据,故不可能执行。

update R1 set version=version+1 where user_id=#user_id# and version=#v#;

上面这种利用版本号的方式就是乐观锁,确保了数据并发修改过程中正确性

考虑⼀个问题:⽐如A机会获取了key1的锁,并设置持有锁的超时时间为10秒,但是获取锁之后,执⾏了⼀段业务操作,业务操作耗时超

过10秒了,此时机器B去获取锁时可以获取成功的,此时会导致A、B两个机器都获取锁成功了,都在执⾏业务操作,这种情况应该怎么处

理?

悲观锁

假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。在查询完数据的时候就把事务锁起来,直到提交事务。

实现方式:使用数据库中的锁机制

应用场景:

乐观锁多读,悲观锁多写

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值