Mysql锁

一、知识结构及面试题目分析

MySQL 锁比较抽象,日常开发中你甚至感觉不到它的存在,隐藏得比较深,但它却是 MySQL 的一个核心知识点,基于它的面试题可以很基础,也可能很灵活。通常来讲,MySQL 锁有三类面试题:

一是基本知识点的考察,各种锁的类型及应用;

二是和锁相关的概念及应用;

三是锁和实际用例结合起来的面试题;

二、典型面试例题及思路分析

问题 1:什么是乐观锁和悲观锁?

悲观锁:对于同一个数据的并发操作,认在使用数据时一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。

乐观锁:认为在使用数据时不会有别的线程修改数据,所以不会添加锁,只是在更新数据的时候去判断之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果数据已经被其他线程更新,则根据不同的实现方式执行不同的操作(例如报错或者自动重试)。

点评:

乐观锁与悲观锁是一种广义上的思想概念,广泛应用于 JDK、数据库乃至工程设计思想中。

悲观锁机制在传统的关系型数据库中使用较广,比如行锁,表锁等,都是在做操作之前先上锁。Java 中 synchronized 和 ReentrantLock 等独占锁也是悲观锁思想的实现。

乐观锁可以用版本号机制或 CAS 算法实现,Java 中的 AtomicInteger 等原子变量类使用的就是乐观锁 CAS 算法。

两种锁的使用场景:

乐观锁和悲观锁各有优劣,不存在一种好于另一种,只有适合的场景,需要具体业务具体分析:

(1)响应速度:如果需要非常高的响应速度,建议采用乐观锁方案,成功就执行,不成功就失败,不需要等待其他并发去释放锁;

(2)冲突频率:如果冲突频率非常高,建议采用悲观锁,保证成功率,如果冲突频率大,乐观锁会需要多次重试才能成功,代价比较大;

(3)重试代价:如果重试代价大,建议采用悲观锁;

(4)业务场景:读多写少倾向于乐观锁,读少写多倾向于悲观锁;

锁分类

MySQL 中锁分类较多,小结如下:
图片描述

问题 2:详述一下数据库事务隔离的四个级别

(1)Read Uncommitted(读未提交):事务可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。而且可能出现脏读(Dirty Read),即一个事务读取到另一个事务还有没提交的记录。

(2) Read Committed(读提交) :事务只能看见已经提交的事务所做的改变。这种隔离级别可能导致不可重复读(Nonrepeatable Read),即同一事务的其他实例在该实例处理其间可能会有新的 commit,所以同一个查询操作执行两次或多次的结果不一致。

(3)Repeatable Read(可重复读) , 事务的多个实例在并发读取数据时读到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读(PhantomRead),它是指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的 “幻影” 数据。

(4)Serializable(串行化) :通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

点评:

MySQL 的默认事务处理级别是可重复读,Oracle 的默认事务处理级别是读提交。

问题 3:如何实现一个分布式锁?

通常有三种方案:

(1)基于数据库实现。 利用主键唯一的特性,如果有多个请求同时提交到数据库的话,数据库会保证只有一个操作可以成功,那么我们就可以认为操作成功的那个线程获得了该方法的锁,当方法执行完毕之后,想要释放锁的话,删除这条数据库记录即可。易于理解,但是可能出现单点故障,性能也可能成为瓶颈。

(2)基于 redis 实现。 主要利用 redis 的 setnx () 和 expire () 方法。使用步骤:

A、setnx (lockkey, 1) 如果返回 0,则说明占位失败;如果返回 1,则说明占位成功;

B、expire () 命令对 lockkey 设置超时时间,为的是避免死锁问题;

C、执行完业务代码后,可以通过 delete 命令删除 key。

(3)基于 Zookeeper 实现分布式锁。 利用临时节点与 watch 机制。每个锁占用一个普通节点 /lock,当需要获取锁时在 /lock 目录下创建一个临时节点,创建成功则表示获取锁成功,失败则 watch/lock 节点,有删除操作后再去争锁。临时节点好处在于当进程挂掉后能自动上锁的节点自动删除即取消锁。

点评:

这是一道半开放式的问题,开放性问题不存在标准答案,但要求候选人逻辑自洽,但这道题业界又有常用的解决方案,所以算是半开放式的。候选人可以基于自己的理解对其中内容进行调整。

比如说基于 redis 的方案,如果 setnx 和 expire 命令之间发生了宕机的现象,那么就依然会出现死锁的问题。这里可以有一个改进方案:使用 redis 的 setnx ()、get () 和 getset () 方法来实现分布式锁。

A、setnx (lockkey, 当前时间 + 过期超时时间),如果返回 1,则获取锁成功;如果返回 0 则没有获取到锁,转向 B。

B、get (lockkey) 获取值 oldExpireTime ,并将这个 value 值与当前的系统时间进行比较,如果小于当前系统时间,则认为这个锁已经超时,可以允许别的请求重新获取,转向 C。

C、计算 newExpireTime = 当前时间 + 过期超时时间,然后 getset (lockkey, newExpireTime) 会返回调用前的 lockkey 的值 currentExpireTime。

D、判断 currentExpireTime 与 oldExpireTime 是否相等,如果相等,说明当前 getset 设置成功,获取到了锁。如果不相等,说明这个锁又被别的请求获取走了,那么当前请求可以直接返回失败,或者继续重试。

E、在获取到锁之后,当前线程可以开始自己的业务处理,当处理完毕后,比较自己的处理时间和对于锁设置的超时时间,如果小于锁设置的超时时间,则直接执行 delete 释放锁;如果大于锁设置的超时时间,则不需要再锁进行处理。

三、总结

这三道题基本对应于文章前面的三类面试题,总体上偏基础知识点一些。如果面试官考察得更深入和更灵活的话,可能会采用锁和场景结合的面试题,其典型形式可以参照思考题。这类题目可能看起来唬人,不过等你真正在机器上试运行一下,然后思考一下背后的逻辑,也就豁然开朗了。

四、扩展阅读及思考题

问:事务是什么?

事务(Transaction)是并发控制的基本单位。所谓的事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。事务是数据库维护数据一致性的单位,在每个事务结束时,都能保持数据一致性。

问:事务有哪些特性?

1、原子性(Atomicity):事务是一个不可分割的单位,事务中的所有SQL等操作要么都发生,要么都不发生。

2、一致性(Consistency):事务发生前和发生后,数据的完整性必须保持一致。

3、隔离性(Isolation):当并发访问数据库时,一个正在执行的事务在执行完毕前,对应其他的会话是不可见的,多个并发事务之间的数据是相互隔离的。备份的参数 --single-transaction

4、持久性(Durability):一个事务一旦被提交,它对数据库中的数据改变就是永久性的。如果出了错误,事务也不允许撤销,只能通过“补偿性事务”。

问:MySQL中InnoDB引擎的行锁是通过加在什么上完成(或称实现)的?为什么是这样子的?

InnoDB是基于索引来完成行锁。

例如在select * from tab_with_index where id = 1 for update中。for update 可以根据条件来完成行锁锁定,并且 id 是有索引键的列,如果 id 不是索引键那么InnoDB将完成表锁,并发将无从谈起。

问:并发事务带来哪些问题?

在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对统一数据进行操作)。并发虽然是必须的,但可能会导致以下的问题:

  • 脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
  • 丢失修改(Lost to modify): 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。
  • 不可重复读(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
  • 幻读(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。

不可重复度和幻读区别:

不可重复读的重点是修改,幻读的重点在于新增或者删除。

例1(同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 ):事务1中的A先生读取自己的工资为 1000的操作还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读自己的工资时工资变为 2000;这就是不可重复读。

例2(同样的条件, 第1次和第2次读出来的记录数不一样 ):假某工资单表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,共查到4条记录,这时事务2 又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条,这样就导致了幻读。

问:什么是基本表?什么是视图?

基本表是本身独立存在的表,在 SQL 中一个关系就对应一个表。 视图是从一个或几个基本表导出的表。视图本身不独立存储在数据库中,是一个虚表。

问:试述视图的优点?

(1) 视图能够简化用户的操作 (2) 视图使用户能以多种角度看待同一数据; (3) 视图为数据库提供了一定程度的逻辑独立性; (4) 视图能够对机密数据提供安全保护。

问:请你介绍一下mysql的MVCC机制

MVCC是一种多版本并发控制机制,是MySQL的InnoDB存储引擎实现隔离级别的一种具体方式,用于实现提交读和可重复读这两种隔离级别。MVCC是通过保存数据在某个时间点的快照来实现该机制,其在每行记录后面保存两个隐藏的列,分别保存这个行的创建版本号和删除版本号,然后Innodb的MVCC使用到的快照存储在Undo日志中,该日志通过回滚指针把一个数据行所有快照连接起来。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值