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数据库双机热备其中的难点分析以及重要环节的经验心得,需要的朋友收藏分享下吧。
    2017-12-12
  • MySQL 之压力测试工具的使用方法

    MySQL 之压力测试工具的使用方法

    这篇文章主要介绍了MySQL 之压力测试工具的使用方法,mysqlslap是mysql自带的基准测试工具,该工具查询数据,语法简单,灵活容易使用,感兴趣的可以了解一下
    2020-05-05
  • 深度解析MySQL 5.7之中文全文检索

    深度解析MySQL 5.7之中文全文检索

    InnoDB默认的全文索引parser非常合适于Latin,因为Latin是通过空格来分词的。但对于像中文,日文和韩文来说,没有这样的分隔符。一个词可以由多个字来组成,所以我们需要用不同的方式来处理。在MySQL 5.7.6中我们能使用一个新的全文索引插件来处理它们:n-gram parser。
    2016-12-12
  • cmd进入mysql以及常用的mysql操作教程

    cmd进入mysql以及常用的mysql操作教程

    对于从来自事计算机专业工作的人而言,MySQL并不陌生,下面这篇文章主要给大家介绍了关于cmd进入mysql以及常用的mysql操作的相关资料,需要的朋友可以参考下
    2023-11-11
  • win10下mysql 8.0.16 winx64安装配置方法图文教程

    win10下mysql 8.0.16 winx64安装配置方法图文教程

    这篇文章主要为大家详细介绍了win10下mysql 8.0.16 winx64安装配置方法图文教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-05-05
  • MySQL rownumber SQL生成自增长序号使用介绍

    MySQL rownumber SQL生成自增长序号使用介绍

    MySQL 几乎模拟了 Oracle,SQL Server等商业数据库的大部分功能,函数。但很可惜,到目前的版本(5.1.33)为止,仍没有实现ROWNUM这个功能
    2011-10-10
  • Linux系统彻底删除Mysql的详细教程

    Linux系统彻底删除Mysql的详细教程

    我们在重新安装MySQL、或更新MySQL版本时,一定会遇到mysql数据残留(脏数据),或组件冲突等问题,下面这篇文章主要给大家介绍了关于Linux系统彻底删除Mysql的详细教程,需要的朋友可以参考下
    2023-02-02
  • mysql报错ERROR 1396 (HY000): Operation ALTER USER failed for root@localhost解决方式

    mysql报错ERROR 1396 (HY000): Operation ALT

    这篇文章主要给大家介绍了关于mysql报错ERROR 1396 (HY000): Operation ALTER USER failed for root@localhost的解决方式,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2024-05-05
  • MySQL多版本并发控制mvcc原理浅析

    MySQL多版本并发控制mvcc原理浅析

    mvcc多版本并发控制是一种数据库的并发控制机制,本文主要介绍了MySQL多版本并发控制mvcc原理浅析,具有一定的参考价值,感兴趣的可以了解一下
    2024-07-07
  • MySQL五步走JDBC编程全解读

    MySQL五步走JDBC编程全解读

    JDBC是指Java数据库连接,是一种标准Java应用编程接口( JAVA API),用来连接 Java 编程语言和广泛的数据库。从根本上来说,JDBC 是一种规范,它提供了一套完整的接口,允许便携式访问到底层数据库,本篇文章我们来了解MySQL连接JDBC的五步走流程方法
    2022-01-01

最新评论