前言

锁是计算机协调多个进程或线程并发访问某一资源的控制机制。在数据库中,除了计算资源(CPU、I/O)的争用之外,数据也是一种多用户共享的资源。如何保证数据并发访问的一致性和有效性是所有数据库必须解决的一个问题。相对于其他数据库而言,mysql的锁机制比较简单,最显著的特点是不同的存储引擎支持不同的锁机制。比如MyISAM和MEMORY采用表级锁;BDB存储引擎采用页面锁,但也支持表级锁;InnoDB存储引擎采用行级锁,也支持表级锁,默认情况使用行级锁。三种锁的特点如下:

表级锁:开销小,加锁快;不会出现死锁;锁定力度大,发生锁冲突的概率最高,并发度最低。

行级锁:开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率最低,并发度也高。

页面锁:开销和加锁时间介于表锁和行锁之间;会出现死锁;锁粒度介于表锁和行锁之间,并发度一般。

表锁

Mysql表锁有两种模式:表共享读锁(table read lock)和表独占写锁(table write lock)。对表的读操作不会阻塞其他用户对同一表的读操作,但会阻塞对同一表的写请求;对表的写操作,会阻塞其他用户对同一表的读和写操作。

准备数据:

表独占写锁

1、用户1:lock table test write; 给test表加独占写锁 2、当前用户1对test表的read,update和insert都可以执行

用户2对test表的查询被阻塞,需要等待用户1对test表的写锁释放

3、用户1释放test表的写锁

用户2立刻查询到结果

表共享读锁

1、用户1给test表加共享读锁

2、当前用户1可以查询test表的数据,用户2也可以查询test表的数据

3、用户1对test表进行更新,插入报错,用户2对test表的更新和插入会等待锁

用户1更新报错:

用户2更新会等待锁释放:

4、用户1查询其他表也会报错,用户2可以查询其他表

用户1查询其他表报错:

5、用户1释放表读锁,用户2等到了锁释放,更新完成

查询表级锁竞争情况

可以通过检查table_locks_waited和table_locks_immediate状态变量来分析系统上的表锁定争夺:如果table_lock_waited的值比较高,则说明存在较严重的表级锁争用情况。

并发插入

上面提到MyISAM表的读和写是串行的,但这是就总体而言。在一定条件下,MyISAM也支持查询和插入的并发执行。MyISAM存储引擎有一个系统变量concurrent_insert,专门用来控制并发插入的行为,可以取值0,1,2;

当concurrent_insert=0,不允许并发插入。

当concurrent_insert=1,如果MyISAM表中没有空洞(即表的中间没有被删除的行),允许一个进程在查询数据,另一个进程在表尾插入记录,这也是mysql的默认设置。

当concurrent_insert=2,无论MyISAM表有没有空洞,都可以在表尾插入记录。

举例:

1、用户1对test表加共享读锁(注意这里要用read local)

2、用户2可以对test表进行插入操作,但是更新会等待锁释放

但是即使用户2插入成功,但是用户1还是只能查到加锁之前的记录,查询不到用户2新插入的记录

InnoDb行锁

两种行锁

共享锁(s):又称读锁。允许一个事务去读一行,阻止其他事务获得相同数据集的排它锁。若事务1对记录A加上S锁,则事务1可以读取A但不能修改A,其他事务只能再对A加S锁,不能加X锁。这就保证了其他事务可以读A,但在事务1释放A上的S锁之前不能对A做任何的修改。

排他锁(X):又称写锁。获取排他锁的事务可以更新数据,阻止其他事务获取相同数据集的共享读锁和排他写锁。若事务1对记录A加了X锁,则事务1可以读A,也可以修改A,其他事务不能对A加任何锁,知道事务1释放A上的锁。

注意:对于共享锁很好理解,就是多个事务只能读取数据不能修改数据。但是排他锁容易错误地理解成:如果一个事务锁住一行数据后,其他事务不能读物和修改该行数据。其实排他锁指的是一个事务对一条记录加上排他锁,其他事务不能对该记录加其他的锁。innoDb引擎默认的update,delete和insert会自动给涉及的数据加上排他锁,select语句默认不会加任何锁。所以加过排他锁的数据行在其他事务中不能修改数据,也不能通过for update加排他锁或者lock in share mode加共享锁,但是可以直接通过select…from…的方式查询数据,因为普通的查询没有任何锁机制。

事务可以通过以下语句显式给记录集加共享锁或排他锁:

共享锁(S):SELECT * FROM table_name WHERE … LOCK IN SHARE MODE。

排他锁(X):SELECT * FROM table_name WHERE … FOR UPDATE。

行锁实现方式

InnoDb行锁是通过给索引上的索引项加锁来实现的,这一点mysql与oracle不同,oracle是通过在数据块中对相应数据行加锁来实现的。InnoDb的这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDb才会使用行级锁,否则InnoDb将使用表锁。 举个例子:

在不通过索引条件查询的时候,InnoDb使用的是表锁,而不是行锁。

1、当用户1查询id=1的记录并对这行记录加上排他锁(要把自动提交关掉)

2、这时用户2查询id=2的记录却需要等待。原因是在没有索引的情况下,InnoDb只能使用表锁。

3、当我们给这个表的id字段加上一个索引后,再重复执行第1,2步骤

用户1成功锁定id=1的记录

用户2也可以成功查询并锁定id=2的记录

间隙锁(Next-Key锁)

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

例如:假如emp表中有101条数据,id的值分别是1,2,…,100,101,下面的sql:

是一个范围条件的检索,InnnoDb不仅会对符合条件的id=101的记录加锁,也会对id>101的间隙加锁。

InnoDb使用间隙锁的目的,一方面是为了防止幻读,以满足隔离级别的要求。对于上面的例子,要是不使用间隙锁,如果其他事务插入了id>100的任何记录,那么本事务如果再次执行上述语句,就会产生幻读。另一方面,是为了满足其恢复和复制的需要。

显然,在使用范围条件检索并锁定记录时,InnoDb这种锁机制会阻塞符合条件范围内键值的并发插入,这往往会造成严重的锁等待。因此,在实际应用开发中,尤其是并发插入比较多的应用,要尽量优化业务逻辑,尽量使用相等来访问和更新数据,尽量避免使用范围条件。

悲观锁和乐观锁

首先,悲观锁和乐观锁都是一种思想,并不是真实存在于数据中的一种机制。

悲观锁

当认为数据被并发修改的几率比较大,需要在修改之前借助于数据库的锁机制对数据进行加锁的思想称为悲观锁,又称PCC(Pessimisic Concurrency Control)。在效率方面,处理锁的操作会产生额外的开销,而且增加了死锁的机会。当一个线程在处理某行数据的时候,其它线程只能等待。 ** 实现方式 ** 悲观锁的实现依赖数据库的锁机制,流程如下:

修改记录前,对记录加上排他锁。

如果加锁失败,说明这条记录正在被修改,那么当前查询要等待会抛出异常。

如果加锁成功,可以对这条记录进行修改,事务完成后进行解锁。

加锁修改期间,其他事务想要对这条记录进行操作,都要等待锁释放或者不想等待抛出异常。 注意:在使用innoDb引擎实现悲观锁时,必须关闭自动提交。举例:

乐观锁

乐观锁的实现不需要借助数据库的锁机制,只要两个步骤:冲突检测(比较)和数据更新,其中一种典型的实现方法就是CAS(比较交换,Compare And Swap)。CAS这里不做详细解释,就是先比较后更新,在对一个数据进行更新前,先持有这个数据原有值得备份,如果当前更新的值和原来的备份相等才进行更新,否则判断为数据被其他线程改过了。当前线程再次进行重试。 *** ABA问题 ***

在更新之前,先查询原有的库存数,在更新库存时,用原有的库存数作为修改条件。相等则更新,否则认为是被改过的数据。但是会存在这样的情况下,比如线程A取出库存数3,线程B先将库存数更新为2,又将库存数更新为3,然后线程A更新的时候发现库存仍然是3,然后更新成功。但是这个过程可能存在问题。** 解决ABA问题一个方法是通过一个顺序递增的version字段或者时间戳**

在每次更新的时候都带上一个版本号,一旦版本号和数据版本号一致就可以执行修改并对版本号执行+1操作,否则执行失败。因为每次操作版本号递增,所以不会出现ABA问题。还可以使用时间戳,因为时间戳具有天然的顺序递增性。

乐观锁和悲观锁比较

乐观锁并不是真正的加锁,优点是效率高,缺点是更新时间的概率比较高(尤其是并发度比较高的环境中);悲观锁依赖于数据库锁机制,更新失败的概率低,但是效率也低。

小钥匙mysql_这有一把钥匙,打开MySQL的锁相关推荐

  1. c语言一把钥匙只能开一把锁阅读答案,一把钥匙开一把锁阅读附答案

    一把钥匙开一把锁 ①一把钥匙开一把锁,这是人人皆知的生活常识.当你拿错钥匙开不开锁的时候,就甭提有多着急了.但是钥匙和锁配不上,再着急也没用. ②在人体内部所进行的各种生物化学反应,也像一把把锁一样, ...

  2. 3D打印的钥匙几乎能打开所有的锁

    北京时间8月30日晚间消息,"撞匙"是锁匠用来打开简单锁具的基本工具.直到现在,许多锁的安全性仅仅是源于其他钥匙很难"撞"开锁中的针,结果是人们拥有了通过隐藏而 ...

  3. 一把钥匙与三道门:麒麟810背后的AI棋局

    时至如今,手机里需不需要AI,已经不再是一个问题. 自2017年,华为用麒麟970芯片拉开端侧AI的序幕之后,我们已经见证到了AI带给手机以非常多的想象力.手机AI摄影.AI摄像广受好评;语音助手与情 ...

  4. 了解过去与理解现在的一把钥匙

    了解过去与理解现在的一把钥匙 /猛虎之心 这是一部值得反复研读的文明史. 一部有价值的文明史,对人对物对事件都应该给读者一种位置感,才能将历史讲得深入.生动,才能结合人们眼前的现实社会,找到历史与现实 ...

  5. 由一把钥匙开启的未来车生活,腾讯Qne-key绝对不是玩笑

    在特斯拉的带动下,互联网汽车正在加速进入消费市场,百度斥巨资研发无人驾驶汽车,计划三年商用五年量产,阿里与上汽合作的"荣威超级互联网SUV"即将在北京车展亮相,腾讯在今日推出了一款 ...

  6. mac在终端打开mysql_如何在Mac上打开终端

    mac在终端打开mysql While using a Mac, sometimes you need to dig deep into settings or perhaps pull off so ...

  7. 小程序 小程序中打开其他小程序、小程序跳转其他小程序、微信群聊中打开小程序

    1.小程序中打开其他小程序可将要打开的小程序通过半屏的形态快速拉起(1)配置app.json的embeddedAppIdList字段中声明需要通过半屏形态打开的小程序,若不配置将降级为普通的小程序跳转 ...

  8. 关于小皮面板打开MySQL命令行的问题

    本次用的phpstudy环境是小皮面板,开始用不久,还不知道哪里可以找到sql的命令行框,2018版是可以直接找到的. //小皮面板打开MySQL的命令行框Win+R运行cmd命令,进入MySQL所在 ...

  9. android usb设置在哪,手机usb调试在哪,小编教你安卓手机怎么打开USB调试

    作为Android手机,如果我们需要将手机连接电脑传输文件.下载安装应用软件.备份文件甚至ROOT等,都需要打开手机的USB调试.可是每个手机的USB调试的位置和打开的方式都有所不同,不过最底层的An ...

最新文章

  1. 机器学习(MACHINE LEARNING)MATLAB灰色预测模型
  2. API网关Kong系列(一)初识
  3. windows10 系统设置一键备份
  4. nodejs实战案例(Express框架+mongoDB)——(1)——前言
  5. 如何给基于 SAP Cloud SDK 的应用增添缓存支持 Cache support
  6. 2021牛客暑期多校训练营4 B-Sample Game(概率DP)
  7. 关于IE6.7.8.FF兼容的问题
  8. 2.oracle物理结构,oracle实验2oracle物理结构管理
  9. IOC操作Bean管理XML方式(bean的作用域)
  10. nvidia-smi‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件
  11. bash 别名_Linux的10个方便的Bash别名
  12. PHP之mb_internal_encoding使用 1
  13. spring aop如何在切面类中获取切入点相关方法的参数、方法名、返回值、异常等信息
  14. C#虚基类继承与接口的区别
  15. Jira与敏捷项目管理
  16. 余额支付使用短信验证码进行二次确认
  17. Win10 最下面的任务栏不显示正在打开的窗口了,打开任何东西任务栏都不显示
  18. 一步步图解 Java G1 垃圾收集器
  19. 华为微认证华为云数据库服务实践稳过 笔记资料
  20. Unity 代码实现形成圆形及形成球形

热门文章

  1. Qt常用控件介绍(一)
  2. 2022建筑电工(建筑特殊工种)操作证考试题库及答案
  3. 市场上卖的几十块的32Gu盘是如何达到的。小心JS
  4. SVG阴影、渐变,文字
  5. Kernel Panic (Kdump) 解析实例之二:内存溢出
  6. 对于投资者来说,什么是量化投资交易策略?
  7. C++ iota()函数实践
  8. 计算机网络OSI七层协议
  9. Android Studio绑定Git步骤
  10. skywalking分布式链路追踪