前言

锁总体可以分为乐观锁和悲观锁,简单说,乐观锁用版本号控制,悲观锁用锁控制。

下面是待会要用来测试的数据

# 添加一个user表
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`name` varchar(255) NOT NULL COMMENT '姓名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
# 插入3条数据
INSERT INTO `users` (`id`, `name`)
VALUES
(1, '雪山飞猪'),
(2, 'chenqionghe'),
(3, 'cqh');

查询结果如下:

一、乐观锁

核心原理是增加一个version的字段来控制。

举个场景,我们希望并发更新单行记录的时候的时候,只有一个进程更新成功,如下

UPDATE users SET name="雪山飞猪" WHERE id=3
UPDATE users SET name="chenqionghe" WHERE id=3

上面这两个sql最终都会更新成功,且以最后更新结果为主。

解决办法是添加一个version字段

添加version字段

ALTER TABLE users ADD `version` INT NOT NULL DEFAULT '0'

解决办法是添加一个version字段,每个更新时where条件都加上它,并且也更新它

UPDATE users SET name="雪山飞猪",version=version+1 WHERE id=3 AND version=0
UPDATE users SET name="chenqionghe",version=version+1 WHERE id=3 AND version=0

这次变成了只会更新成功一次,谁先抢到这条记录以谁为主,因为当前一个进程更新成功后版本号已经变化了,第二个进程找不到这条记录了。

这就是最简单的CAS机制。

二、悲观锁

其实类似Go语言里的Mutex和RwMutex,可以参考这篇文章:Go语言中的互斥锁和读写锁(Mutex和RWMutex)

读锁

也叫共享锁或S锁,当给数据表加上共享锁的时候,表就变成了只读模式。

我们可以锁全表,也可以锁全表或部分行,如下

全表锁(LOCK TABLE 表 READ)

语法如下

LOCK TABLE 表 READ
UNLOCK TABLE;

我们来测试一个,第一个进程执行

LOCK TABLE users READ;

第二个进程执行正常读

SELECT * FROM users WHERE id=1;



可以正常查询。我们再来执行一下更新

UPDATE users SET name="chenqionghe" WHERE id=1



出现了等待。

我们给第一个进程解锁

再看第二个进程,已经更新成功

行锁(SELECT ... LOCK IN SHARE MODE)

BEGIN;
SELECT * FROM users WHERE id IN (1,2) LOCK IN SHARE MODE
COMMIT;

必须配合事务使用,BEIN开始后,锁定的行,外部只能查询,不能更新

我们来测试一下,第一个进程执行

BEGIN;
SELECT * FROM users WHERE id IN (1,2) LOCK IN SHARE MODE

注意:这时候事务没提交



这里锁定了id为1和2的记录行。我们第二个进程执行更新

UPDATE users SET name="雪山飞猪" WHERE id=1



又一次出现了等待。

好,这时候我们将第一个进程的事务提交

COMMIT;



第二个进程更新成功了,如下

写锁

也排他锁、独占锁,理解成读和写都不行了,语法如下

全表锁(LOCK TABLE 表 WRITE)

LOCK TABLE 表 WRITE
UNLOCK TABLE;

我们来测试一下,第一个进程执行

LOCK TABLE users WRITE;



这时候已经锁定全表,我们再用另一个进程查询一下id为1的数据

SELECT * FROM users WHERE id=1



可以看到,查询已经发生了等待。

我们再将第一个进程解锁

UNLOCK TABLE



这时候,第二个进程立马查询成功

行锁(SELECT ... FOR UPDATE)

当我们对数据进行更新的时候(INSERT、DELETE、UPDATE)的时候,数据库会自动使用排它锁,防止其他事务操作该数据

BEGIN;
SELECT * FROM users WHERE id IN (1,2) LOCK IN SHARE MODE
COMMIT;

我们再来测试一下,第一个进程锁定id为1和2的记录

BEGIN;
SELECT * FROM users WHERE id IN (1,2) FOR UPDATE

注意:这时候事务没提交

我们先用第二个进程来更新id为3的记录(未被锁定)

UPDATE users SET name="chenqionghe" WHERE id=3



执行成功了。

我们再来更新一个id为1的记录

UPDATE users SET name="chenqionghe" WHERE id=1

发生了等待,说明已经被锁定了。

好,我们提交第一个进程的事务

COMMIT;

再去看第二个进程,已经更新成功

简单说乐观锁用version控制,悲观锁的表锁一般用不着,行的读锁用 LOCK IN SHARE MODE,写锁用FRO UPDATE,就是这么简单!

以上内容由chenqionghe提供,别忘了点个赞哈~~

MySQL如何加锁控制并发的更多相关文章

  1. JAVA程序对MYSQL数据库加锁实验

    什么是脏读,不可重复读,幻读 1. 脏读 :脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据. 2. 不可重 ...

  2. java并发编程学习:用 Semaphore (信号量)控制并发资源

    并发编程这方面以前关注得比较少,恶补一下,推荐一个好的网站:并发编程网 - ifeve.com,上面全是各种大牛原创或编译的并发编程文章. 今天先来学习Semaphore(信号量),字面上看,根本不知 ...

  3. 使用mysql悲观锁解决并发问题

    最近学习了一下数据库的悲观锁和乐观锁,根据自己的理解和网上参考资料总结如下: 悲观锁介绍(百科): 悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持 ...

  4. PHP通过加锁实现并发情况下抢码功能

    本文基于php语言使用加锁实现并发情况下抢码功能,特定时间段开放抢码并不允许开放的码重复: 需求:抢码功能 要求: 1.特定时间段才开放抢码: 2.每个时间段放开的码是有限的: 3.每个码不允许重复: ...

  5. MySQL InnoDB 实现高并发原理

    MySQL 原理篇 MySQL 索引机制 MySQL 体系结构及存储引擎 MySQL 语句执行过程详解 MySQL 执行计划详解 MySQL InnoDB 缓冲池 MySQL InnoDB 事务 My ...

  6. Nodejs - 如何用 eventproxy 模块控制并发

    本文目标 本文的目标是获取 ZOJ 1001-1010 每道题 best solution 的作者 id,取得数据后一次性输出在控制台. 前文 如何用 Nodejs 分析一个简单页面 我们讲了如何用 ...

  7. Java--Semaphore控制并发线程数量

    package com; import java.util.concurrent.Semaphore; /** * Created by yangyu on 16/11/28. */ /** * Se ...

  8. entity framework如何控制并发

     entity framework如何控制并发 针对字段http://msdn.microsoft.com/en-us/library/vstudio/bb738618(v=vs.100).aspx ...

  9. Database(Mysql)发版控制二

    author:skate time:2014/08/18 Database(Mysql)发版控制 The Liquibase Tool related Database 一.Installation ...

随机推荐

  1. 【命令】glances命令和dstat命令

    https://www.cnblogs.com/l75790/articles/9197829.html 文章里面的进程和作业管理篇

  2. 微信支付(PC扫码支付和H5公众号支付)

    最近在做微信支付,微信支付比较坑,官方居然只有.NET.C#.PHP的demo居然没有java的demo.然后微信支付是不提供测试账号的需要直接用正式的公众号.首先来介绍下微信扫码支付吧,微信扫码有两 ...

  3. JavaDailyReports10_20

    1 package varycode; 2 class Grandparent 3 { 4 public Grandparent() 5 { 6 7 System.out.println(" ...

  4. VIM和正则表达式

    1.VIM 1.1vim简介 vim是一款强大的文本编辑器,它和 vi 使用方法一致,但功能更为强大.官网:www.vim.org.中文手册:http://vimcdoc.sourceforge.ne ...

  5. 别再费劲去找后台的前端框架了,2021 年就用 Fantastic-admin 吧

    前言 你知道光是基于 Vue 的后台框架在 Github 上有多少个仓库么? 如果你搜索 vue admin 会得到 13120 个仓库,如果用 vue 后台 会得到 7596 个仓库,如果把两者结合 ...

  6. 面试官:数据库自增ID用完了会怎么样?

    看到这个问题,我想起当初玩魔兽世界的时候,25H难度的脑残吼的血量已经超过了21亿,所以那时候副本的BOSS都设计成了转阶段.回血的模式,因为魔兽的血量是int型,不能超过2^32大小. 估计暴雪的设 ...

  7. ASP.NET Core路由中间件[3]: 终结点(Endpoint)

    到目前为止,ASP.NET Core提供了两种不同的路由解决方案.传统的路由系统以IRouter对象为核心,我们姑且将其称为IRouter路由.本章介绍的是最早发布于ASP.NET Core 2.2中 ...

  8. Redis学习之路(三)常用命令总结

    一.集群 查看Redis集群有多少个库 192.168.200.100:7001> config get databases 1) "databases" 2) " ...

  9. linux串口通信 接收信息不完整 读取不全

    类似这种 ready.o是我用来读取串口信息的一个程序 执行结果如下: [root@localhost testPlc]# ./ready.o 0 02 1 30 2 30 3 46 4 46 5 3 ...

  10. Java基础--接口回调(接口 对象名 = new 类名)理解

    接口 对象名1 = new 类名和类名 对象名2 = new 类名的区别是什么? 实例 /** *Person.java 接口 */ public interface Person { void in ...