面试官问:请介绍一下MySQL数据库的锁机制?

为什么要加锁

问题背景

  • 当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。

要解决的问题

  • 多用户环境下保证数据库完整性和一致性

锁是什么

  • 在计算机科学中,锁是在执行多线程时用于强行限制资源访问的同步机制,即用于在并发控制中保证对互斥要求的满足。
  • 加锁是实现数据库并发控制的一个非常重要的技术。当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁。加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更新操作。

锁的分类

  • 行级锁
  • 行级锁是Mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。行级锁分为共享锁 和 排他锁。
  • 特点
  • 开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

  • 表级锁是MySQL中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。最常使用的MYISAM与INNODB都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。
  • 特点
  • 开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发度最低。

  • 页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。BDB支持页级锁
  • 特点
  • 开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般

MySQL常用存储引擎的锁机制

MyISAM和MEMORY采用表级锁(table-level locking)
BDB采用页面锁(page-level locking)或表级锁,默认为页面锁
InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁
  • InnoDB行锁是通过给索引上的索引项加锁来实现的,InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!

在实际应用中,要特别注意InnoDB行锁的这一特性,不然的话,可能导致大量的锁冲突,从而影响并发性能。

行级锁都是基于索引的,如果一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁。行级锁的缺点是:由于需要请求大量的锁资源,所以速度慢,内存消耗大。

实例说明
  • MySQL InnoDB引擎默认的修改数据语句:update,delete,insert都会自动给涉及到的数据加上排他锁。

select语句默认不会加任何锁类型,如果加排他锁可以使用select …for update语句,加共享锁可以使用select … lock in share mode语句。

所以加过排他锁的数据行在其他事务种是不能修改数据的,也不能通过for update和lock in share mode锁的方式查询数据,但可以直接通过select …from…查询数据,因为普通查询没有任何锁机制。

行级锁与死锁

MyISAM中是不会产生死锁的,因为MyISAM总是一次性获得所需的全部锁,要么全部满足,要么全部等待。而在InnoDB中,锁是逐步获得的,就造成了死锁的可能。

在MySQL中,行级锁并不是直接锁记录,而是锁索引。索引分为主键索引和非主键索引两种,如果一条sql语句操作了主键索引,MySQL就会锁定这条主键索引;如果一条语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。在UPDATE、DELETE操作时,MySQL不仅锁定WHERE条件扫描过的所有索引记录,而且会锁定相邻的键值,即所谓的next-key locking。

当两个事务同时执行,一个锁住了主键索引,在等待其他相关索引。另一个锁定了非主键索引,在等待主键索引。这样就会发生死锁。

发生死锁后,InnoDB一般都可以检测到,并使一个事务释放锁回退,另一个获取锁完成事务。

共享锁与排它锁

共享锁(Share Lock)
  • 共享锁又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。

如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。

用法 SELECT … LOCK IN SHARE MODE;

在查询语句后面增加LOCK IN SHARE MODE,Mysql会对查询结果中的每行都加共享锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请共享锁,否则会被阻塞。其他线程也可以读取使用了共享锁的表,而且这些线程读取的是同一个版本的数据。

排它锁(eXclusive Lock)
  • 排他锁又称写锁,如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。

用法 SELECT … FOR UPDATE;

在查询语句后面增加FOR UPDATE,Mysql会对查询结果中的每行都加排他锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请排他锁,否则会被阻塞。

乐观锁(Optimistic Lock)

是什么
  • 假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。

相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本。

数据版本,为数据增加的一个版本标识。当读取数据时,将版本标识的值一同读出,数据每更新一次,同时对版本标识进行更新。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的版本标识进行比对,如果数据库表当前版本号与第一次取出来的版本标识值相等,则予以更新,否则认为是过期数据。

实现数据版本有两种方式,第一种是使用版本号,第二种是使用时间戳。

使用版本号实现乐观锁
  • 使用版本号时,可以在数据初始化时指定一个版本号,每次对数据的更新操作都对版本号执行+1操作。并判断当前版本号是不是该数据的最新的版本号。
1.查询出商品信息select (status,status,version) from t_goods where id=#{id}2.根据商品信息生成订单3.修改商品status为2update t_goodsset status=2,version=version+1where id=#{id} and version=#{version};
优点与不足
  • 乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但如果直接简单这么做,还是有可能会遇到不可预期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就遇到了问题

悲观锁(Pessimistic Lock)

是什么
  • 在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制 (也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)
悲观锁的流程
  • 在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)。
  • 如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。具体响应方式由开发者根据实际需要决定。
  • 如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。
  • 其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常。
MySQL InnoDB中使用悲观锁
  • 要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。set autocommit=0;
//0.开始事务
begin;
//1.查询出商品信息
select status from t_goods where id=1 for update;
//2.根据商品信息生成订单
insert into t_orders (id,goods_id) values (null,1);
//3.修改商品status为2
update t_goods set status=2;
//4.提交事务
commit;

上面的查询语句中,我们使用了select…for update的方式,这样就通过开启排他锁的方式实现了悲观锁。此时在t_goods表中,id为1的 那条数据就被我们锁定了,其它的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改。

Java的锁机制

线程的同步问题
  • 一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在java里边就是拿到某个同步对象的锁(一个对象只有一把锁);如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池等待队列中)。取到锁后,他就开始执行同步代码(被synchronized修饰的代码);线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中等待的某个线程就可以拿到锁执行同步代码了。这样就保证了同步代码在统一时刻只有一个线程在执行。
线程的同步方法:
  • 1. 在需要同步的方法的方法签名中加入synchronized关键字。
  • 2. 使用synchronized块对需要进行同步的代码段进行同步。
  • 3. 使用JDK 5中提供的java.util.concurrent.lock包中的Lock对象。
ThreadLocal

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。使用ThreadLocal的典型场景如数据库连接管理,线程会话管理等场景,只适用于独立变量副本的情况,如果变量为全局共享的,则不适用在高并发下使用。

Image placeholder
onlookerend
未设置
  54人点赞

没有讨论,发表一下自己的看法吧

推荐文章
MySQL锁机制

课程推荐:PHP开发工程师--学习猿地精品课程 内部锁MySQL在自身服务器内部执行内部锁,以管理多个会话对表内容的争用。内部锁又可以分为行锁和表锁。外部锁MySQL为客户会话提供选项来显式地获取表锁

工商银行MySQL数据库架构解密

本文根据DTCC数据库大会分享内容整理而成,将介绍工行IT架构转型中传统OLTP数据库架构面临的挑战和诉求,构建基于MySQL分布式企业级解决方案实践历程,包括技术选择、高可用设计、两地三中心容灾、运

吊打面试官!MySQL灵魂100问,你能答出多少?

推荐阅读:面试机会不等人,资料看精不看多!史上最全Java技术资料合集!2019年中总结,400道一线大厂高频精选面试题合集(JVM+Spring+RabbitMQ+Mybatis+Redis+分布式

Mysql数据实时同步实践

关于小米内部使用的数据库你知道多少?背景Mysql由于自身简单、高效、可靠的特点,成为小米内部使用最广泛的数据库,但是当数据量达到千万/亿级别的时候,mysql的相关操作会变的非常迟缓;如果这时还有实

【Golang+MySQL】记一次 MySQL 数据库迁移(一)

【Golang+mysql】记一次mysql数据库迁移(一)文章地址:https://github.com/stayfoo/stayfoo-hub一、准备目标: 腾讯云CVM自建mysql数据迁移到腾

面试题:请解释一下什么是虚拟内存?

内存对于用户来说就是一个字节数组,我们可以根据地址来访问到某个字节或者某些字节:很久之前的内存很久很久之前,一台机器上只放置一个程序,操作系统仅仅作为一个函数库存在。对于内存来说,除去操作系统的代码和

MySQL 数据库操作:创建和查看数据库

数据库是数据的集合。MySQL允许我们高效地存储和检索数据库中的数据。在MySQL中,我们可以使用CREATEDATABASE语句创建数据库。但是,如果数据库已经存在,则会引发错误。为了避免该错误,我

MySQL 数据库操作:删除数据库

使用MySQL的DROPDATABASE命令可以很容易的删除一个数据库。数据库删除的同时,所属的数据表将一起被删除。如果删除的数据库不存在,则会引发错误。为了避免错误的发生,可以在DROPDATABA

阿里面试题:如何保证缓存与数据库的双写一致性?

作者:你是我的海啸出处:https://blog.csdn.net/chang384915878/article/details/86756463只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只

MySQL 亿级数据数据库优化方案测试-银行交易流水记录的查询

作者:逸宸a链接:https://www.jianshu.com/p/cbdef47fb837对MySQL的性能和亿级数据的处理方法思考,以及分库分表到底该如何做,在什么场景比较合适?比如银行交易流水

面试题总结:可能是全网最好的MySQL重要知识点

作者:Snailclimb 整理编辑:SegmentFault本文原载于SegmentFault专栏JavaGuide,如侵删。标题有点标题党的意思,但希望你在看了文章之后不会有这个想法——这篇文章是

【搞定 Java 并发面试】面试最常问的 Java 并发基础常见面试题总结!

Java并发基础常见面试题总结 1.什么是线程和进程? 1.1.何为进程? 进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。

MySQL 性能优化:8 种常见 SQL 错误用法!

1、LIMIT语句分页查询是最常用的场景之一,但也通常也是最容易出问题的地方。比如对于下面简单的语句,一般DBA想到的办法是在type,name,create_time字段上加组合索引。这样条件排序都

Oracle/云MySQL/MsSQL“大迁移”真相及最优方案

最近一段时间碰到一些数据迁移的项目,如:Oracle迁移到MySQL,MsSQL迁移到MySQL,云MySQL迁移到本地MySQL。对于这方面做了系统的整理。包括:迁移方案的选择、如何跳出迁移遇到的坑

一条SQL语句在MySQL中如何执行的

前两天发了一条SQL慢的原因有哪些,在那篇文章我没有说到优化器之类的,我觉得如果配合一条SQL是如何执行的,会更好,所以特地找了一篇。来源:JavaGuide  |作者:木木匠本篇文章会分析一个sql

mysql 进行update时,要更新的字段中有单引号或者双引号导致不能批量生成sql的问题

前言将数据从一张表迁移到另外一张表的过程中,通过mysql的concat方法批量生成sql时遇到了一个问题,即进行UPDATE更新操作时如果原表中的字段中包含单引号'或者双引号",那么就会生成不正确的

Mysql中,21个写SQL的好习惯,你值得拥有呀

课程推荐:PHP开发工程师--学习猿地精品课程 前言每一个好习惯都是一笔财富,本文分SQL后悔药,SQL性能优化,SQL规范优雅三个方向,分享写SQL的21个好习惯,谢谢阅读,加油哈~github地址

2019年8月数据库流行度排行:双星闪耀 MySQL 成月度最大赢家

炎炎夏日,DB-Engines的8月榜单已经发布,本月积分MySQL获得了最显著的增长,较上月增加了24分,Oracle获得了18分的增长,Oracle公司的两个王牌产品,闪耀8月。以下是前10名的榜

2019年9月数据库流行度排行:MySQL 强劲增长完成深 V 反转

导读:DB-Engines的2019年9月数据库流行度排行榜已经发布,本月最耀眼的明星是MySQL,分值大幅增长25.39分,较年初已经上升了125分,增幅达10%,完成了一次深V反转。相较之下,Or

欧洲最大MySQL用户之一,Booking.com数据库构架探秘!

吴鑫Booking.com数据库工程师TeamLead2015年加入总部位于阿姆斯特丹的Booking.com数据团队,现任数据库工程师团队负责人,主要是负责Booking.com里MySQL相关的运

SQL Server 2014的数据库引擎新增功能(参考sqlserver官方文档)

SQLServer2014数据库引擎引入了一些新功能和增强功能,这些功能可以提高设计、开发和维护数据存储系统的架构师、开发人员和管理员的能力和工作效率。  以下是 数据库引擎已增强的方面。数据库引擎功

Laravel-Binlog 扩展(用于实时监听 MySQL 数据变更、数据同步等场景)

Laravel-Binlogv0.2.1 (该扩展当前用于我司测试环境实时同步Mysql数据变更到ElasticSearch,稳定性待测试!!哈哈哈)我司正式环境走的阿里云DTS数据订阅 基于Sw

Nebula 架构剖析系列(二)图数据库的查询引擎设计

摘要上文(存储篇)说到数据库重要的两部分为存储和计算,本篇内容为你解读图数据库Nebula在查询引擎QueryEngine方面的设计实践。在Nebula中,QueryEngine是用来处理Nebula

如何保证缓存与数据库的双写一致性?

分布式缓存是现在很多分布式应用中必不可少的组件,但是用到了分布式缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题?CacheAsidePa

合理建立Hadoop数据库的7个步骤

数据湖的概念起源于大数据的出现——且数据已成为企业的核心资产,Hadoop则是作为存储和管理数据的平台而出现。但是,盲目地投入Hadoop数据湖建设并不一定会使您的企业进入大数据时代——至少不是以一种