【mysql】关于悲观锁
关于mysql中的锁
在并发环境下,有可能会出现脏读(Dirty Read)、不可重复读(Unrepeatable Read)、 幻读(Phantom Read)、更新丢失(Lost update)等情况,所以mysql引入了很多锁的概念
MySQL InnoDB对数据行的锁定类型一共有四种:共享锁(读锁,S锁)、排他锁(写锁,X锁)、意向共享锁(IS锁)和意向排他锁(IX锁),支持三种行锁定方式:
- 行锁(Record Lock):锁直接加在索引记录上面。
- 间隙锁(Gap Lock):锁加在不存在的空闲空间,可以是两个索引记录之间,也可能是第一个索引记录之前或最后一个索引之后的空间。
- Next-Key Lock:行锁与间隙锁组合起来用就叫做Next-Key Lock。
默认情况下,InnoDB工作在可重复读隔离级别下,并且以Next-Key Lock的方式对数据行进行加锁,这样可以有效防止幻读的发生。Next-Key Lock是行锁与间隙锁的组合,这样,当InnoDB扫描索引记录的时候,会首先对选中的索引记录加上行锁(Record Lock),再对索引记录两边的间隙加上间隙锁(Gap Lock)。如果一个间隙被事务T1加了锁,其它事务是不能在这个间隙插入记录的。
- 在可重复读级别下,InnoDB以Next-Key Lock的方式对索引加锁;在读已提交级别下,InnoDB以Index-Record Lock的方式对索引加锁。
- 被加锁的索引如果不是聚族索引,那被锁的索引所指向的聚族索引以及其它指向相同聚族索引的索引也会被加锁。
- SELECT * FROM ... LOCK IN SHARE MODE对索引加共享锁;SELECT * FROM ... FOR UPDATE对索引加排他锁。
- SELECT * FROM ... 是非阻塞式读,(除Serializable级别)不会对索引加锁。在读已提交级别下,总是查询记录的最新、有效的版本;在可重复读级别下,会记住第一次查询时的版本,之后的查询会基于该版本。例外的情况是在串行化级别,这时会以Next-Key Lock的方式对索引加共享锁。
- UPDATE ... WHERE 与DELETE ... WHERE对索引加排他锁。
- INSERT INTO ... 以Index-Record Lock的方式对索引加排他锁
什么是悲观锁
悲观锁是指对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态,在悲观锁的情况下,为了保证事务的隔离性,就需要一致性锁定读。读取数据时给加锁,其它事务无法修改这些数据。修改删除数据时也要加锁,其它事务无法读取这些数据。
悲观锁(pessimistic locking)体现了一种谨慎的处事态度。其流程如下:
- 在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)
- 如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常
- 如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了
- 其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常
悲观锁确实很严谨,有效保证了数据的一致性,在C/S应用上有诸多成熟方案。 但是他的缺点与优点一样的明显
- 悲观锁适用于可靠的持续性连接,诸如C/S应用。 对于Web应用的HTTP连接,先天不适用
- 锁的使用意味着性能的损耗,在高并发、锁定持续时间长的情况下,尤其严重。 Web应用的性能瓶颈多在数据库处,使用悲观锁,进一步收紧了瓶颈
- 非正常中止情况下的解锁机制,设计和实现起来很麻烦,成本还很高
- 不够严谨的设计下,可能产生莫名其妙的,不易被发现的, 的死锁问题
自动提交
MySQL 采用 autocommit 模式运行。这意味着,当执行一个用于更新(修改)表的语句之后,MySQL立刻把更新到buffer中,同时记录锁也会被释放。因此如果事务要执行多条更新(修改)语句,那么从第2条更新语句开始就是在无锁条件下执行了,这样会导致事务失效,破坏数据一致性
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1 |
+--------------+
关闭自动提交
set autocommit=0; or [mysqld]
init_connect='SET autocommit=0' ;
避免此问题的方法就是关闭 autocommit,然后通过执行 commit 语句来提交事务
$db->begin();
$db->query("SET autocommit=0");
...
...
$db->commit();
注意:
1、不能将"关闭autocommit"作为缺省设置,否则在 innodb 表上执行的查询操作也将因为没有执行 commit 或者 rollback 而一直锁表!因此只能在需要时局部关闭 autocommit,并在操作完成后开启 autocommit
2、连接mysql用户的权限不能大于启动mysql的用户的权限,不然init_connect='SET autocommit=0'根本不会启作用,也不会报任何错误
If a user has SUPER privilege, init_connect will not execute,(otherwise if init_connect will a wrong query no one can connect to server).
Note, if init_connect is a wrong query, the connection is closing without any errors and next command will clause 'lost connection' error.
使用举例
要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.01 sec) mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 0 |
+--------------+
1 row in set (0.00 sec)
Connect a:
mysql> begin;
Query OK, 0 rows affected (0.00 sec) mysql> select age from users where id =1 for update;
+-----+
| age |
+-----+
| 24 |
+-----+
1 row in set (0.00 sec) mysql> update users set age = 25 where id =1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0 mysql> commit;
Query OK, 0 rows affected (0.01 sec)
Connect b: 在a长时间未提交的时候
mysql> select * from users where id =1 for update;
ERROR 1205 : Lock wait timeout exceeded; try restarting transaction
mysql> update users set age = 22 where id =1;
ERROR 1205 : Lock wait timeout exceeded; try restarting transaction
connect b 只有在 connect a 提交之后才会执行,否则会一直等待
在事务中,只有SELECT ... FOR UPDATE 或LOCK IN SHARE MODE 会等待其它事务结束后才执行,一般SELECT ... 则不受此影响
MySQL select…for update 的 Row Lock 与 Table Lock
上面我们提到,使用select…for update会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB默认Row-Level Lock,所以只有「明确」地指定主键/索引,MySQL 才会执行Row lock (只锁住被选取的数据) ,否则MySQL 将会执行Table Lock (将整个数据表单给锁住)
如果一个条件无法通过索引快速过滤,存储引擎层面就会将所有记录加锁后返回,再由MySQL Server层进行过滤,但在实际使用过程当中,MySQL做了一些改进,在MySQL Server过滤条件,发现不满足后,会调用unlock_row方法,把不满足条件的记录释放锁 (违背了二段锁协议的约束)。这样做,保证了最后只会持有满足条件记录上的锁,但是每条记录的加锁操作还是不能省略的。可见即使是MySQL,为了效率也是会违反规范的
这种情况同样适用于MySQL的默认隔离级别RR。所以对一个数据量很大的表做批量修改的时候,如果无法使用相应的索引,MySQL Server过滤数据的的时候特别慢,就会出现虽然没有修改某些行的数据,但是它们还是被锁住了的现象
优缺点
悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;
另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数据
悲观的缺陷是不论是页锁还是行锁,加锁的时间可能会很长,这样可能会长时间的限制其他用户的访问,也就是说悲观锁的并发访问性不好
乐观锁则认为其他用户企图改变你正在更改的对象的概率是很小的,因此乐观锁直到你准备提交所作的更改时才将对象锁住,当你读取以及改变该对象时并不加锁。可见乐观锁加锁的时间要比悲观锁短,乐观锁可以用较大的锁粒度获得较好的并发访问性能
参考文章
http://tech.meituan.com/innodb-lock.html
http://hedengcheng.com/?p=771
http://ouyanggod.iteye.com/blog/2166384
【mysql】关于悲观锁的更多相关文章
- 【MySQL】悲观锁&乐观锁
悲观锁与乐观锁是两种常见的资源并发锁设计思路,也是并发编程中一个非常基础的概念.本文将对这两种常见的锁机制在数据库数据上的实现进行比较系统的介绍. 悲观锁(Pessimistic Lock) 悲观锁的 ...
- 谈谈mysql的悲观和乐观锁
悲观锁与乐观锁是两种常见的资源并发锁设计思路,也是并发编程中一个非常基础的概念.之前有写过一篇文章关于并发的处理思路和解决方案,这里我单独将对这两种常见的锁机制在数据库数据上的实现进行比较系统的介绍一 ...
- Mysql乐观锁与悲观锁
乐观锁和悲观锁是两种常见的资源并发锁设计思路,也是并发编程中一个非常重要的基础理念. Mysql的悲观锁 什么是悲观锁(Pessimistic Lock): 悲观锁的特点是先获取锁,再进行业务操作,即 ...
- mysql悲观锁
悲观锁与乐观锁是两种常见的资源并发锁设计思路,也是并发编程中一个非常基础的概念. 悲观锁(Pessimistic Lock) 悲观锁的特点是先获取锁,再进行业务操作,即“悲观”的认为获取锁是非常有可能 ...
- 悲观锁,乐观锁,排他锁,行锁----MYSQL
在说具体的锁结构时,先思考一个问题,那就是为什么要上锁?然后我要如何选择锁?锁具体如何实现? 在文章得末尾我给出了我的个人答案. 一.什么是悲观锁? 1.悲观锁就是在操作数据时,认为此操作会出现数据冲 ...
- 关于MySql悲观锁与乐观锁
悲观锁与乐观锁是两种常见的资源并发锁设计思路,也是并发编程中一个非常基础的概念.本文将对这两种常见的锁机制在数据库数据上的实现进行比较系统的介绍. 悲观锁(Pessimistic Lock) 悲观锁的 ...
- 【MySQL锁】MySQL悲观锁和乐观锁概念
悲观锁与乐观锁是两种常见的资源并发锁设计思路,也是并发编程中一个非常基础的概念.本文将对这两种常见的锁机制在数据库数据上的实现进行比较系统的介绍. 悲观锁(Pessimistic Lock) 悲观锁的 ...
- mysql的乐观锁和悲观锁
悲观锁与乐观锁是两种常见的资源并发锁设计思路,也是并发编程中一个非常基础的概念.本文将对这两种常见的锁机制在数据库数据上的实现进行比较系统的介绍. 悲观锁(Pessimistic Lock) 悲观锁的 ...
- mysql中的乐观锁和悲观锁
mysql中的乐观锁和悲观锁的简介以及如何简单运用. 关于mysql中的乐观锁和悲观锁面试的时候被问到的概率还是比较大的. mysql的悲观锁: 其实理解起来非常简单,当数据被外界修改持保守态度,包括 ...
- Mysql悲观锁乐观锁区别与使用场景
本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...
随机推荐
- 一种C#生成符合Java规则的二进制文件方法
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.一个项目中的真实问题 实际项目中,本想通过C#制作小工具生成SHP ...
- NET开发学习项目资源
最近在整理资料时发现自己当初学习NET的一些项目资源,一直放在硬盘里不如拿来分享给初学者学习还是不错的. 项目代码为<精通ASP.NET20+SQL Server2005项目开发>书中源码 ...
- SQL Server里的闩锁耦合(Latch Coupling)
几年前,我写了篇关于闩锁和为什么SQL Server需要它们的文章.在今天的文章里,我想进一步谈下非缓存区闩锁(Non-Buffer Latches),还有在索引查找操作期间,SQL Server如何 ...
- 12306火车票查询--python
最近我看到看到使用python实现火车票查询,我自己也实现了,感觉收获蛮多的,下面我就把每一步骤都详细给分享出来.(注意使用的是python3) 首先我将最终结果给展示出来: 在cmd命令行执行:py ...
- JavaScript的作用域和块级作用域概念理解
作用域 作用域永远都是任何一门编程语言中的重中之重,因为它控制着变量与参数的可见性与生命周期.说到这里我们需要理解两个概念:块级作用域与函数作用域. 函数作用域 这个应该好理解,函数作用域就是说定义在 ...
- ubuntu 解决 “E: Problem with MergeList /var/lib/apt/lists/”错误
这种错误的意思:无法解析或打开软件包的列表或是状态文件. 出现的原因:无法解析或打开软件包列表多数情况是安装的软件与本身系统有一些冲突之类的问题,或者曾在更新软件源或下载软件的时候意外中断造成的. 解 ...
- C#使用资源文件的方法
其实,对于资源文件的使用,说白点就是通过强制类型转换,将资源文件里的数据强行的转换成你需要的(换种方式说,就是你原来存进去什么,就用什么类型拿出来). 主要通过System.Resources.Res ...
- csharp: Oracle Stored Procedure DAL using ODP.NET
paging : http://www.codeproject.com/Articles/44858/Custom-Paging-GridView-in-ASP-NET-Oracle https:// ...
- 利用节点更改table内容
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <title> new document ...
- Oracle 编程
编程:储存过程,自定义函数,触发器,包. 储存过程 在命令窗口用exec调 作用:存储在数据库中用于完成某个功能的程序 创建在 procedure里 create or replace procedu ...