【mysql】关于悲观锁
作者:网络转载 发布时间:[ 2016/3/3 11:28:26 ] 推荐标签:数据库 MySQL
在并发环境下,有可能会出现脏读(DirtyRead)、不可重复读(UnrepeatableRead)、幻读(PhantomRead)、更新丢失(Lostupdate)等情况,所以mysql引入了很多锁的概念
MySQLInnoDB对数据行的锁定类型一共有四种:共享锁(读锁,S锁)、排他锁(写锁,X锁)、意向共享锁(IS锁)和意向排他锁(IX锁),支持三种行锁定方式:
行锁(RecordLock):锁直接加在索引记录上面。
间隙锁(GapLock):锁加在不存在的空闲空间,可以是两个索引记录之间,也可能是第一个索引记录之前或后一个索引之后的空间。
Next-KeyLock:行锁与间隙锁组合起来用叫做Next-KeyLock。
默认情况下,InnoDB工作在可重复读隔离级别下,并且以Next-KeyLock的方式对数据行进行加锁,这样可以有效防止幻读的发生。Next-KeyLock是行锁与间隙锁的组合,这样,当InnoDB扫描索引记录的时候,会首先对选中的索引记录加上行锁(RecordLock),再对索引记录两边的间隙加上间隙锁(GapLock)。如果一个间隙被事务T1加了锁,其它事务是不能在这个间隙插入记录的。
在可重复读级别下,InnoDB以Next-KeyLock的方式对索引加锁;在读已提交级别下,InnoDB以Index-RecordLock的方式对索引加锁。
被加锁的索引如果不是聚族索引,那被锁的索引所指向的聚族索引以及其它指向相同聚族索引的索引也会被加锁。
SELECT*FROM...LOCKINSHAREMODE对索引加共享锁;SELECT*FROM...FORUPDATE对索引加排他锁。
SELECT*FROM...是非阻塞式读,(除Serializable级别)不会对索引加锁。在读已提交级别下,总是查询记录的新、有效的版本;在可重复读级别下,会记住第一次查询时的版本,之后的查询会基于该版本。例外的情况是在串行化级别,这时会以Next-KeyLock的方式对索引加共享锁。
UPDATE...WHERE与DELETE...WHERE对索引加排他锁。
INSERTINTO...以Index-RecordLock的方式对索引加排他锁
什么是悲观锁
悲观锁是指对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态,在悲观锁的情况下,为了保证事务的隔离性,需要一致性锁定读。读取数据时给加锁,其它事务无法修改这些数据。修改删除数据时也要加锁,其它事务无法读取这些数据。
悲观锁(pessimisticlocking)体现了一种谨慎的处事态度。其流程如下:
在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusivelocking)
如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常
如果成功加锁,那么可以对记录做修改,事务完成后会解锁了
其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常
悲观锁确实很严谨,有效保证了数据的一致性,在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.
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11