mysql使用锁的分析
MySQL中的共享锁与排他锁
1,共享锁与排他锁 1.首先说明:数据库的增删改操作默认都会加排他锁,而查询不会加任何锁。 |--共享锁:对某一资源加共享锁,自身可以读该资源,其他人也可以读该资源(也可以再继续加共享锁,即 共享锁可多个共存),但无法修改。要想修改就必须等所有共享锁都释放完之后。语法为: select * from table lock in share mode |--排他锁:对某一资源加排他锁,自身可以进行增删改查,其他人无法进行任何操作。语法为: select * from table for update --增删改自动加了排他锁
共享锁(Share Lock)
在查询语句后面增加LOCK IN SHARE MODE
,Mysql会对查询结果中的每行都加共享锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请共享锁,否则会被阻塞。其他线程也可以读取使用了共享锁的表,而且这些线程读取的是同一个版本的数据。
例1:-------------------------------------------------------------------------------------------------------------------------------------
T1:select * from table lock in share mode(假设查询会花很长时间,下面的例子也都这么假设) T2:update table set column1='hello'
T1代表一个数据库执行请求,T2代表另一个请求,也可以理解为T1为一个线程,T2 为另一个线程。
过程: T1运行(并加共享锁) T2运行 If T1还没执行完 T2等...... else锁被释放 T2执行 endif T2 之所以要等,是因为 T2 在执行 update 前,试图对 table 表加一个排他锁,而数据库规定同一资源上不能同时共存共享锁和排他锁。所以 T2 必须等 T1 执行完,释放了共享锁,才能加上排他锁,然后才能开始执行 update 语句。 例2:------------------------------------------------------------------------------------------------------------------------------------- T1:select * from table lock in share mode T2:select * from table lock in share mode 这里T2不用等待T1执行完,而是可以马上执行。 分析: T1运行,则 table 被加锁,比如叫lockA T2运行,再对 table 加一个共享锁,比如叫lockB两个锁是可以同时存在于同一资源上的(比如同一个表上)。这被称为共享锁与共享锁兼容。这意味着共享锁不阻止其它人同时读资源,但阻止其它人修改资源。 例3:------------------------------------------------------------------------------------------------------------------------------------- T1:select * from table lock in share mode T2:select * from table lock in share mode T3:update table set column1='hello' T2 不用等 T1 运行完就能运行,T3 却要等 T1 和 T2 都运行完才能运行。因为 T3 必须等 T1 和 T2 的共享锁全部释放才能进行加排他锁然后执行 update 操作。
排他锁(eXclusive Lock)
排他锁又称写锁,如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。
用法
SELECT ... FOR UPDATE;
在查询语句后面增加FOR UPDATE
,Mysql会对查询结果中的每行都加排他锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请排他锁,否则会被阻塞。
意向锁
InnoDB还有两个表锁:
意向共享锁(IS):表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须先取得该表的IS锁
意向排他锁(IX):类似上面,表示事务准备给数据行加入排他锁,说明事务在一个数据行加排他锁前必须先取得该表的IX锁。
意向锁是InnoDB自动加的,不需要用户干预。
对于insert、update、delete,InnoDB会自动给涉及的数据加排他锁(X);对于一般的Select语句,InnoDB不会加任何锁,事务可以通过以下语句给显示加共享锁或排他锁。
共享锁:SELECT ... LOCK IN SHARE MODE;
排他锁:SELECT ... FOR UPDATE;
乐观锁与悲观锁 案例: 某商品,用户购买后库存数应-1,而某两个或多个用户同时购买,此时三个执行程序均同时读得库存为n,之后进行了一些操作,最后将均执行update table set 库存数=n-1,那么,很显然这是错误的。 解决: 1.使用悲观锁(其实说白了也就是排他锁) |--程序A在查询库存数时使用排他锁(select * from table where id=10 for update) |--然后进行后续的操作,包括更新库存数,最后提交事务。 |--程序B在查询库存数时,如果A还未释放排他锁,它将等待。 |--程序C同B…… 2.使用乐观锁(靠表设计和代码来实现) |--一般是在该商品表添加version版本字段或者timestamp时间戳字段 |--程序A查询后,执行更新变成了: update table set num=num-1 where id=10 and version=23 这样,保证了修改的数据是和它查询出来的数据是一致的,而其他执行程序未进行修改。当然,如果更新失败,表示在更新操作之前,有其他执行程序已经更新了该库存数,那么就可以尝试重试来保证更新成功。为了尽可能避免更新失败,可以合理调整重试次
数(阿里巴巴开发手册规定重试次数不低于三次)。 总结:对于以上,可以看得出来乐观锁和悲观锁的区别。 1.悲观锁使用了排他锁,当程序独占锁时,其他程序就连查询都是不允许的,导致吞吐较低。如果在查询较多的情况下,可使用乐观锁。 2.乐观锁更新有可能会失败,甚至是更新几次都失败,这是有风险的。所以如果写入较频繁,对吞吐要求不高,可使用悲观锁。 也就是一句话:读用乐观锁,写用悲观锁。