mysql行锁(for update)解决高并发问题

 更新时间:2023年08月29日 16:38:51   作者:wbj16116  
这篇文章主要介绍了mysql行锁(for update)解决高并发问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

mysql行锁解决高并发

for update 必须在事务中执行

(避免高并发时库存为负数)

where条件有主键是行锁 否则是表锁

$pdo = new PDO('mysql:host=127.0.0.1;port=3306; dbname=test','root','123456');
$pdo->beginTransaction();//开启事务
$sql="select `number` from storage where id=1 *for UPDATE* ";//利用for update 开启行锁
$res = $pdo->query($sql)->fetch();
$number = $res['number'];
if($number>0)
{
  $sql ="insert into `order` VALUES (null,$number)";
  $order_id = $pdo->query($sql);
  if($order_id)
  {
    $sql="update storage set `number`=`number`-1 WHERE id=1";
    if($pdo->query($sql))
    {
      $pdo->commit();//提交事务
    }
    else
    {
      $pdo->rollBack();//回滚
    }
  }
  else
  {
    $pdo->rollBack();//回滚
  }
}

mysql行锁、表锁&间隙锁

事务隔离级别的实现原理:锁

表级锁&行级锁

表级锁:对整张表加锁。开销小,加锁快,不会出现死锁;锁粒度大,发生锁冲突的概率高,并发度低。

行级锁:对某行记录加锁。开销大,加锁慢,会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度高。

注:

  • 对于InnoDB引擎,绝大部分情况应该使用行锁
  • 使用表锁中,表比较大,事务需要更新全部或大部分数据
  • 事务涉及到多个表,比较复杂,可能引起死锁,造成大量的事务回滚

排它锁和共享锁

共享锁(Shared),又称为S锁,读锁

共享锁锁定的资源可以被其他用户读取,但不能修改

在进行SELECT的时候,会将对象进行共享锁锁定,当数据读取完毕之后,就会释放共享锁,这样就可以保证数据在读取时不被修改。

排它锁(Exclusive),又称为X锁,写锁

排它锁锁定的数据只允许进行锁定操作的事务使用,其他事务无法对已锁定的数据进行查询或修改

X锁和S锁之间有以下的关系:SS(读-读)可以兼容的,SX(读-写)、XX(写-写)之间是互斥的

  • 一个事务对数据对象O加了S锁,可以对O进行读取操作,但不能进行更新操作。加锁期间其他事务能对O加S锁但不能加X锁
  • 一个事务对数据对象O加了X锁,就可以对O进行读取和更新。加速期间其他事务不能对O加任何锁。
//对某一行加上共享锁
select uid from student where uid=1 lock in share mode; 
//对某个数据行上添加排它锁
select uid from student where uid=1 for update;

InnoDB行级锁

InnoDB存储引擎支持事务处理,表支持行级锁定,并发能力更好

行级锁

  • InnoDB的行锁是通过给在索引上的索引项加锁来实现的,是给索引在加锁,并不是给单纯表的行记录在加锁;索引若过滤条件没有索引的话,使用的就是表锁,而不是行锁!!!
  • 由于InnoDB的行锁实现是针对索引字段添加的锁,不是针对行记录加的锁,因此虽然访问的是InnoDB引擎下表的不同行,但若使用相同的索引字段作为过滤条件,依然会发生锁冲突,只能串行进行,不能并发进行
  • 即使SQL中使用了索引,但是经过MySQL的优化器后,若认为全表扫描比使用索引效率更高,此时会放弃使用索引,因此也不会使用行锁,而是使用表锁,比如对一些很小的表,MySQL就不会去使用索引。

间隙锁(gap lock)(串行化隔离级别怎么解决幻读问题?)

间隙锁是专门用于解决幻读这种问题的锁,它锁的是行与行之间的间隙,能够阻塞新插入的操作

间隙锁的引入也带来了一些新的问题,比如:降低并发度,可能导致死锁。

注意:读读不互斥,读写/写读/写写实互斥的,但是间隙锁之间是不冲突的,间隙锁会阻塞插入操作。另外,间隙锁在可重复读级别下才是有效的。

幻读场景:

第一类条件:范围查询

注:当使用索引时,经过MySQL优化器,认为全盘扫描比使用索引效率高,则变成表级锁,当前只能插入表头之前或表尾之后。

第二类条件:等值查询引入上图场景所用表进行解读

注:若age是主键索引和唯一索引(值是不允许重复的),那就只有行锁

间隙锁和next-key lock:

行锁和间隙锁合称为next-key lock,这个锁是左开右闭的区。

意向共享锁和意向排他锁

1、意向锁是由InnoDB存储引擎获取行锁之前自己获取的

2、意向锁之间都是兼容的,不会产生冲突

3、意向锁存在的意义是为了更高效的获取表锁(表格中的X和S指的是表锁,不是行锁!!!)

4、意向锁是表级锁,协调表锁和行锁的共存关系。主要目的是显示事务正在锁定某行或者试图锁定某行。

InnoDB表级锁

在绝大部分情况下都应该使用行锁,因为事务和行锁往往是选择InnoDB的理由,但个别情况下也使用 表级锁;

1)事务需要更新大部分或全部数据,表又比较大,如果使用默认的行锁,不仅这个事务执行效率低,而且可能造成其他事务长时间等待和 锁冲突;

2)事务涉及多个表,比较复杂,很可能引起死锁,造成大量事务回滚。

如:

LOCK TABLE user READ;读锁锁表
LOCK TABLE user WRITE; 写锁锁表

事务执行…

COMMIT/ROLLBACK; 事务提交或者回滚
UNLOCK TABLES; 本身自带提交事务,释放线程占用的所有表锁

死锁

MyISAM 表锁是 deadlock free 的, 这是因为 MyISAM 总是一次获得所需的全部锁,要么全部满足,要么等待,因此不会出现死锁。

但在 InnoDB 中,除单个 SQL 组成的事务外,锁是逐步获得的,即锁的粒度比较小,这就决定了在 InnoDB 中发生死锁是可能的。

mysql> select * from test_dead_lock where id=1 for update;
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

死锁问题一般都是我们自己的应用造成的,和多线程编程的死锁情况相似,大部分都是由于我们多个线程在获取多个锁资源的时候,获取的顺序不同而导致的死锁问题。

因此我们应用在对数据库的多个表做更新的时候,不同的代码段,应对这些表按相同的顺序进行更新操作,以防止锁冲突导致死锁问题。

锁的优化建议

1.尽量使用较低的隔离级别

2.设计合理的索引并尽量使用索引访问数据,使加锁更加准确,减少锁冲突的机会提高并发能力

3.选择合理的事务大小,小事务发生锁冲突的概率小

4.不同的程序访问一组表时,应尽量约定以相同的顺序访问各表,对一个表而言,尽可能以固定的顺序存取表中的行。这样可以大大减少死锁的机会

5.尽量用相等条件访问数据,这样可以避免间隙锁对并发插入的影响

6.不要申请超过实际需要的锁级别

7.除非必须,查询时不要显示加锁

总结

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

相关文章

  • MySQL的缓存策略方式

    MySQL的缓存策略方式

    MySQL缓存方案主要用于减轻数据库读写压力,通过使用Redis缓存用户定义的热点数据,用户可以直接从缓存中获取数据,文章还讨论了如何通过读写分离、连接池和异步连接等技术提升MySQL的访问性能,此外,还探讨了缓存方案中的一致性问题、读写策略以及缓存穿透
    2024-09-09
  • 详解MySQL实时同步到Oracle解决方案

    详解MySQL实时同步到Oracle解决方案

    这篇文章主要介绍了详解MySQL实时同步到Oracle解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • 关于MySQL日期类型的选择建议

    关于MySQL日期类型的选择建议

    在软件开发中,时间记录是不可或缺的功能,如记录操作时间、交易时间等,通常不建议使用字符串存储日期,因为它占用空间大,并且效率低下,MySQL提供的Datetime和Timestamp是常用的时间存储类型,Datetime没有时区信息,而Timestamp与时区有关
    2024-10-10
  • MySQL mysqladmin客户端的使用简介

    MySQL mysqladmin客户端的使用简介

    这篇文章主要介绍了MySQL mysqladmin客户端的使用简介,帮助大家更好的理解和学习使用MySQL,感兴趣的朋友可以了解下
    2021-03-03
  • Mysql下自动删除指定时间以前的记录的操作方法

    Mysql下自动删除指定时间以前的记录的操作方法

    这篇文章主要介绍了MySQL下自动删除指定时间以前的记录的操作方法,需要的朋友可以参考下
    2018-08-08
  • MySQL CTE (Common Table Expressions)示例全解析

    MySQL CTE (Common Table Expressions)示例全解

    MySQL 8.0引入CTE,支持递归查询,可创建临时命名结果集,提升复杂查询的可读性与维护性,适用于层次结构数据处理,但需注意性能和递归深度限制,本文给大家介绍MySQL CTE (Common Table Expressions)示例,感兴趣的朋友一起看看吧
    2025-07-07
  • mysql Load Data InFile 的用法

    mysql Load Data InFile 的用法

    Load Data InFile是用于批量向数据表中导入记录。
    2009-05-05
  • mysql连接数清理方案

    mysql连接数清理方案

    这篇文章主要介绍了mysql连接数清理方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-10-10
  • MySQL的锁机制及排查锁问题解析

    MySQL的锁机制及排查锁问题解析

    MySQL的锁机制包括行锁和表锁,行锁进一步细分为RecordLock、GapLock和Next-keyLock,行锁因其细粒度而减少冲突但开销大,可能引起死锁,本文介绍MySQL的锁机制及排查锁问题,感兴趣的朋友一起看看吧
    2025-01-01
  • 导致mysqld无法启动的一个错误问题及解决

    导致mysqld无法启动的一个错误问题及解决

    这篇文章主要介绍了导致mysqld无法启动的一个错误问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02

最新评论