前言

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

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

# 添加一个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. Python代码实现猜数字游戏

    1 # -*- coding:utf-8 -*- 2 # 作者:IT小学生蔡坨坨 3 # 时间:2020/12/9 23:02 4 5 # 猜数字游戏: 6 # 代码中生成一个随机整数. 7 # 然后 ...

  2. k8s之深入解剖Pod(三)

    目录: Pod的调度 Pod的扩容和缩容 Pod的滚动升级 一.Pod的调度 Pod只是容器的载体,通常需要通过RC.Deployment.DaemonSet.Job等对象来完成Pod的调度和自动控制 ...

  3. TypeError: filter() got an unexpected keyword argument 'XXX'

    Flask使用SQLAlchemy查询报如下错误: TypeError: filter() got an unexpected keyword argument 'XXX' 出错原因: 查询错误,应该 ...

  4. 深入浅出Mybatis系列 强大的动态SQL

    上篇文章<深入浅出Mybatis系列(八)---mapper映射文件配置之select.resultMap>简单介绍了mybatis的查询,至此,CRUD都已讲完.本文将介绍mybatis ...

  5. java Stream学习笔记

    1.Stream与io无关. 2.Stream和用普通的循环没有太大区别,甚至时间复杂度更高,代码可读性差(通常代码行数更少). 3.流操作就是把循环要做的任务单独抽取出来,最终通过编译在一起. 来看 ...

  6. JAVA基础--数据类型和运算符总结

    一,JAVA的数据类型 逻辑型----boolean 只允许取值true和false 字符型----char java采用unicode编码(utf16),每个字符占用两个字节 整数型----byte ...

  7. SparkStreaming推测机制:面试被问遇到什么问题,说这个显水平!

    背景 老刘最近晚上会刷刷牛客网的大数据开发面经,总是会看到一个高频的面试题,那就是你在学习过程中遇到过什么问题吗? 这个问题其实有点难回答,如果我说的太简单了,会不会让面试官觉得水平太低,那我应该讲什 ...

  8. RocketMQ 简介

    本文根据阿里云 RocketMQ产品文档整理 地址:https://help.aliyun.com/document_detail/29532.html?userCode=qtldtin2 简介 Ro ...

  9. Java并发编程实战(4)- 死锁

    在这篇文章中,我们主要讨论一下死锁及其解决办法. 目录 概述 死锁案例 死锁的原因和预防 破坏占用且等待条件 破坏不可抢占条件 破坏循环条件 使用等待-通知机制 Java中的等待-通知机制 条件曾经满 ...

  10. 对Java集合的概述

    前言 大部分编程语言都提供了数组来保存对象,数组是非常重要的数据结构之一.但是数组在初始化时就已经定义了数组长度,不可变,使用起来颇为麻烦.因此,Java 在 JDK 1.2 版本中添加了集合框架,用 ...