Mysql锁之共享锁(读锁)和排他锁(写锁)详解

 更新时间:2023年07月27日 09:55:40   作者:烟雨楼台笑江湖  
这篇文章主要介绍了Mysql锁之共享锁(读锁)和排他锁(写锁),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

InnoDB和MyISAM

Mysql在5.5之前默认使用MyISAM存储引擎,之后使用InnoDB。

查看当前存储引擎:

show variables like ‘%storage_engine%';

MyISAM操作数据都是使用的表锁,你更新一条记录就要锁整个表,导致性能较低,并发不高。当然它也不会存在死锁问题。

InnoDB与M有ISAM的最大不同有两点

  • InnoDB支持事务
  • InnoDB采用了行级锁。也就是你需要修改哪行,就可以只锁定哪行。

在Mysql中,行级锁并不是直接锁记录,而是锁索引。

索引分为主键索引和非主键索引两种,如果一条sql语句操作了主键索引,Mysql就会锁定这条主键索引;如果一条语句操作了非主键索引,Mysql会先锁定该非主键索引,再锁定相关的主键索引。

InnoDB行锁是通过给索引加锁实现的,如果没有索引,InnoDB会通过因此的聚簇索引来对记录加锁。

也就是说:如果不通过索引条件检索数据,那么InnoDB将对表中所有数据加锁,实际效果跟表锁一样。因为没有了索引,找到记录就得扫描全表,要扫描全表,就得锁定表。

共享锁与排他锁

首先说明:InnoDB引擎默认对update,delete,insert加排它锁,select语句默认不加锁

共享锁

共享锁shared locks(S锁)也称读锁:用于不更改或不更新数据的操作(只读操作),可以查看但无法修改和删除的一种数据锁,如select语句。

如果事务T对数据A加上共享锁后,则其他事务只能对数据A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。共享锁下其它用户可以并发读取,查询数据。但不能修改,增加,删除数据。资源共享。

加锁方式

select ... lock in share mode

注意:

  • 对于使用共享锁的事务,其他事务只能读,不可写
  • 如果执行了更新操作则会一直等待,直到当前事务commit或者rollback
  • 如果当前事务也执行了其他事务处于等待的那条sql语句,当前事务将会执行成功,而其他事务会报死锁
  • 允许其他锁共存

排它锁

排它锁Exclusive Locks(X锁)也称写锁、独占锁:用于数据修改操作,例如insert、update或delete。确保不会同时对同一资源进行多重更新。

如果事务T对数据A加上排它锁后,则其他事务不能在对A加任何类型的锁。获准排他锁的事务既能读数据,又能修改数据。我们在操作数据库的时候,可能会由于并发问题而引起的数据的不一致性(数据冲突)

加锁方式

select ... for update

**for update:**InnoDB默认是行级别的锁,当有明确指定的主键是,使用的是行锁;否则使用表锁。使用情况详细如下:

明确指定主键,并且存在此记录,行级锁。例如:

-- id是主键
select name,age from table_user where id = '1' for update;

明确指定主键,若查无记录,无锁。例如:

-- id是主键,单不存在id = 1的数据
select name,age from table_user where id = '1' for update;

无主键,表级锁。例如:

-- age是普通字段
select name,age from table_user where age = 12 for update;

主键不明确,表级锁。例如:

-- id是主键,age不是,但数据库
select name,age from table_user where age = 12 and id = '1' for update;

注意:

  • 对于排它锁的事务,其它事务可读,但不可进行更新操作
  • for update仅使用与InnoDB,并且必须开启事务,在begin和commit之间才生效
  • 当一个事务进行for update的时候,另一个事务也有for update时会一直等待,直到之前的事务commit或rollback或断开连接释放锁才能拿到锁进行后面的操作(排它锁不能共存)
  • InnoDB引擎默认对update,delete,insert加排它锁,select语句默认不加锁
  • 加过排他锁的数据行在其他事务中是不能修改数据的,也不能通过for update和lock in share mode锁的方式查询数据,但可以直接通过select … from …查询数据,因为普通查询没有任何锁机制

乐观锁与悲观锁

首先说明:乐观锁和悲观锁都是针对读(select)来说的。

乐观锁

乐观锁不是数据库自带的,需要我们自己去实现。

乐观锁是指操作数据库时(更新操作),想法很乐观,认为这次的操作不会导致冲突,在操作数据时,并不进行任何其他的特殊处理(也就是不加锁),而在进行更新后,再去判断是否有冲突了。

悲观锁

悲观锁就是在操作数据时,认为此操作会出现数据冲突,所以在进行每次操作时都要通过获取锁才能进行对相同数据的操作,这点跟Java中的synchronized很相似,所以悲观锁需要耗费较多的时间。

另外与乐观锁相对应,悲观锁是由数据库自己实现了的,要用的时候,我们直接调用数据库的相关语句就可以了。

由悲观锁涉及到的另外两个锁概念,就是共享锁与排他锁。共享锁和排他锁是悲观锁的不同实现,它俩都属于悲观锁的范畴。

案例

某商品,用户购买后库存应-1,而某两个或多个用户同时购买,此时三个执行程序均同时读得库存为“n”,之后进行了一些操作,最后将均执行update table set 库存书 = n - 1,那么,很显然这是错误的。

解决

1.使用悲观锁(也就是排他锁)

  • 程序A在查询库存数时使用排他锁(select * from table where id = 10 for update)
  • 然后进行后续操作,包括更新库存数,最后提交事务
  • 程序B在查询库存数时,如果A还未释放排他锁,程序B将等待。。。
  • 程序C同B。。。

2.使用乐观锁(靠表设计和代码来实现)

一般是在该商品表添加version版本字段或者timestamp时间戳字段

程序A查询后,执行更新变成了:

update table set num = num - 1 and version = 23

这样,保证了修改的数据是和它查询出来的数据是一致的(其他执行程序肯定未进行修改)。当然,如果更新失败,表示在更新操作之前,有其他执行程序已经更新了该库存数,那么就可以尝试重试来保证更新成功。为了尽可能避免更新失败,可以合理调整重试次数(阿里巴巴开发手册规定重试次数不低于三次)。

乐观锁和悲观锁的区别

悲观锁实际使用了排他锁来实现**(select … for update)**。InnoDB加行锁的前提是:必须是通过索引条件来检索数据,否则会切换为表锁。

因此,悲观锁在未通过索引条件检索数据时,会锁定整张表。导致其他程序不允许**“加锁的查询操作”**,影响吞吐。因此,如果在查询居多的情况下,推荐使用乐观锁。

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

乐观锁更新有可能会失败,甚至是更新几次都失败,这是有风险的。所以如果写入居多,对吞吐要求不高,可使用悲观锁。

结尾:读用乐观锁,写用悲观锁。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • MySQL数据库定时备份的几种实现方法

    MySQL数据库定时备份的几种实现方法

    本文主要介绍了MySQL数据库定时备份的几种实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-07-07
  • ubuntu下mysql版本升级到5.7

    ubuntu下mysql版本升级到5.7

    这篇文章主要为大家详细介绍了ubuntu下mysql版本升级到5.7的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11
  • 详解mysql中explain的type

    详解mysql中explain的type

    这篇文章主要介绍了mysql中explain的type的相关资料,帮助大家更好的理解和使用MySQL,感兴趣的朋友可以了解下
    2020-11-11
  • mysql用户管理操作实例分析

    mysql用户管理操作实例分析

    这篇文章主要介绍了mysql用户管理操作,结合实例形式分析了mysql用户管理查看、创建、修改、删除等相关操作技巧,需要的朋友可以参考下
    2020-04-04
  • 远程连接mysql报错“Host xxx is not allowed to connect to this MySQL server“简单解决办法

    远程连接mysql报错“Host xxx is not allowed to connect to th

    这篇文章主要给大家介绍了关于远程连接mysql报错“Host xxx is not allowed to connect to this MySQL server“的解决办法,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-07-07
  • MySQL字段类型与Java实体类类型对应转换关系详解

    MySQL字段类型与Java实体类类型对应转换关系详解

    这篇文章主要介绍了MySQL字段类型与Java实体类类型对应转换关系,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • MySQL kill指令使用指南

    MySQL kill指令使用指南

    这篇文章主要介绍了MySQL kill指令的使用方法,帮助大家更好的理解和使用MySQL,感兴趣的朋友可以了解下
    2020-12-12
  • MySQL高可用解决方案MMM(mysql多主复制管理器)

    MySQL高可用解决方案MMM(mysql多主复制管理器)

    MySQL本身没有提供replication failover的解决方案,通过MMM方案能实现服务器的故障转移,从而实现mysql的高可用。MMM不仅能提供浮动IP的功能,如果当前的主服务器挂掉后,会将你后端的从服务器自动转向新的主服务器进行同步复制,不用手工更改同步配置
    2017-09-09
  • MySQL复制表常用的四种方式小结

    MySQL复制表常用的四种方式小结

    mysql 想必大家比较熟悉了,我们常见的crud  sql想必大家也是手到拈来,但可能会有人不会写mysql中复制表,所以接下来本文我会介绍四种常用的复制表的方式,需要的朋友可以参考下
    2023-07-07
  • MySQL常用聚合函数详解

    MySQL常用聚合函数详解

    下面小编就为大家带来一篇MySQL常用聚合函数详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06

最新评论