热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

数据库事务(二)Mysql数据库锁

我们已经了解了数据库事务隔离级别脏读,虚读,不可重复读,不过想要更好的对数据库操作,提高数据库读写的性能,我们还需要了解数据库的另一个机制---数据库锁!其实,刚刚使用mysql数

我们已经了解了数据库事务隔离级别脏读,虚读,不可重复读,不过想要更好的对数据库操作,提高数据库读写的性能,我们还需要了解数据库的另一个机制---数据库锁!

其实,刚刚使用mysql数据库不久的伙伴,对数据库锁可能没啥概念,而且可能还有疑问,难道数据库会死锁?没见过啊,死锁了数据库不就GG了么?首先要确认一个东西,那就是数据库当然会死锁,既然有事务,有隔离,有并发,逻辑一旦处理不好,死锁肯定是会发生的。


至于大多数小伙伴没遇到过,一个是因为你可能接触的业务并发不是特别高,业务逻辑不是特别复杂,还有一个重要的原因就是数据库内部有一个监测死锁的机制,一旦发现数据库死锁,就会触发回退操作,不会让数据库真的宕机。

下面我们来认识下数据库的锁,既然是数据隔离,高并发情况下操作数据,肯定是要加锁的,要不然就乱套了。等我们认识了锁,那死锁机制对我们来说就很简单了,而且,可以轻易的写出一个死锁的业务操作,就像使用java代码实现死锁一样,嘿嘿。

我们知道java中的锁(常见的)一般是乐观锁(CAS),继承AQS实现的锁通过阻塞对垒实现的锁,还有就是Synchronized的监视器实现的锁。那么MySQL呢?java是一种语言,主要针对的是数据逻辑或者说运行程序需要的资源进行加锁,锁的主要是资源,而MySQL数据库呢?这是一个关系型数据库,其中的锁主要是对数据的锁定,防止出现脏读等事件,所以两者的锁肯定是有差别的。

我们来看下MySQL的锁,主要是行锁(Record Locks),间隙锁(临键锁),意向锁(Insert Intention Locks),自增锁(Auto-inc Locks)等,下面我们来展开说下。

1.行锁

我们先建立一个数据表,这样看着操作说,更清楚一些。下面是建表和几条数据的sql语句。

CREATE TABLE `student` (
`id` int(10) NOT NULL AUTO_INCREMENT COMMENT 'id主键',
`age` int(11) NULL DEFAULT NULL COMMENT '年龄',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '名称',
`id_card` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '身份证号',
`stage` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '等级',
`create_date` datetime(0) NULL DEFAULT NULL COMMENT '创建日期',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `idx_card`(`id_card`) USING BTREE COMMENT '唯一索引--身份编号',
INDEX `idx_stage`(`stage`) USING BTREE COMMENT '普通索引--等级索引'
) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '个人测试索引-学生表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (1, 16, '张三', '110120200402023456', '9', '2020-10-21 14:07:15');
INSERT INTO `student` VALUES (2, 16, '张三加', '110120200402023454', '9', '2020-10-21 14:07:15');
INSERT INTO `student` VALUES (3, 16, '张四', '110120200402023457', '8', '2020-10-21 14:07:15');
INSERT INTO `student` VALUES (4, 16, '张五', '110120200402023458', '7', '2020-10-21 14:07:15');
INSERT INTO `student` VALUES (5, 16, '张六', '110120200402023459', '6', '2020-10-21 14:07:15');
INSERT INTO `student` VALUES (6, 16, '张七', '110120200402023460', '5', '2020-10-21 14:07:15');
INSERT INTO `student` VALUES (7, 16, '张八', '110120200402023465', '4', '2020-10-21 14:07:15');
INSERT INTO `student` VALUES (8, 16, '张九', '110120200402023466', '15', '2020-10-21 14:07:15');
INSERT INTO `student` VALUES (9, 16, '张氏', '110120200402023467', '16', '2020-10-21 14:07:15');
INSERT INTO `student` VALUES (10, 16, '李四', '110120200402023480', '18', '2020-10-21 14:07:15');
INSERT INTO `student` VALUES (11, 16, '李五', '110120200402023481', '19', '2020-10-21 14:07:15');
INSERT INTO `student` VALUES (12, 16, '李六', '110120200402023482', '20', '2020-10-21 14:07:15');

截个图吧,看着清楚些哈~





mysql student数据表,唯一索引 id_card​​,普通索引stage



进行讲解锁之前我们先了解接隔离级别的查看,就是下面这个语句:

select @@tx_isolation as 当前隔离级别 ,@@global.tx_isolation as 全局隔离级别; 
事务的隔离级别对锁是有影响的哦,在进行锁操作之前,建议大家最好查看下事务的隔离级别,当然MySQL默认的就是REPEATABLE-READ

好了,我们来看下这几条sql语句 。

-- 开启事务A
START TRANSACTION;
-- id=7的数据
SELECT * FROM student where id=7 for UPDATE;
-- --上半部分
-- ----------
-- --下半部分
ROLLBACK;
-- ----最好在可视化工具的两个查询页面,方便看结果
-- 启动事务B
START TRANSACTION ;
-- id=7的数据
SELECT * FROM student where id=7 for UPDATE;
-- --上半部分
-- ----------
-- --下半部分
ROLLBACK;

       我们知道查询语句后面加上for update,就是对整个student表加入了意向锁(下边说),然后对id=7的记录加上了独占锁,锁了住查询的结果数据,其实很明显,for update(为了修改)你都想修改了,肯定不允许其他人修改了,不然一起修改还不乱了套。

我们运行事务A 的上半部分,就是锁住了id=7的这条数据,事务A未结束。然后启动事务B,再次去查询李四(for update),我们会发现无法查询到李四的数据,事务一直在运行,也没有报错信息的,其实就是在等待,等待事务A结束(提交或者回滚)。当然如果事务B去查询id=8的数据肯定是没有问题的

其实这个时候对 id=7 这条数据加锁加的就是行锁。

我们来看下行锁的官方描述A record lock is a lock on an index record(行锁一定是作用在索引上的)。       

student表的id(建表的时候就加了聚簇索引),idx_card,idx_stage都是是加了索引的,如果以这些条件进行加锁操作,是会锁住这一条数据的。

行锁分为两种:


  • 共享锁(S) 共享锁也称为读锁,读锁允许多个连接可以同一时刻并发的读取同一资源,互不干扰;


  • 排他锁(X) 排他锁也称为写锁,一个写锁会阻塞其他的写锁或读锁,保证同一时刻只有一个连接可以写入数据,同时防止其他用户对这个数据的读写。


让然如果条件是name这种没有加索引的字段,想要锁住一条数据的话,那只能使用表锁了,这样的话就比较消耗资源,而且容易发生死锁的。

2.间隙锁 

我们刚刚看了以索引条件进行查询,会进行行锁那么,什么是间隙锁呢?我们来看下下面几条语句。

-- 开启事务A
START TRANSACTION;
-- id_card大于'110120200402023465',小于'110120200402023481'的数据
SELECT * FROM student where id_card>'110120200402023465' and id_card <'110120200402023481' for UPDATE
-- --上半部分
-- ----------
-- --下半部分
ROLLBACK;
-- -----------------------------------------------------------
-- 启动事务B
START TRANSACTION ;
-- 删除 id_card='110120200402023481' 的数据
DELETE FROM student where id_card='110120200402023481';
-- --上半部分
-- ----------
-- --下半部分
ROLLBACK;

运行事务A的上半部分,锁住了id_card大于'110120200402023465',小于'110120200402023481'的数据,这个时候id_card大于'110120200402023465',小于'110120200402023481',这个数据段是被锁住的,如果插入id_card=110120200402023469的数据,肯定是失败的。

但是如果运行事务B的上半部分呢?id_card小于'110120200402023481',是不是'110120200402023481'没有被锁住呢?当然不是,大家执行下SQL语句,会发现事务B一直在等待,直到事务A  rollback 之后,事务B才能够继续运行,很明显小id_card数据为'110120200402023481'的这条数据被锁住了。

查询相关概念,我们会发现,间隙锁锁住的策略是左开右闭,当你锁定一个区间时,左侧的数据没有被锁住,右侧的数据是被锁住的。所以事务A锁住的区间是('110120200402023465','110120200402023481'],大家可以试一下,事务B如果操作id_card=110120200402023465这条数据的话,是没有被锁住的。

有了间隙锁,锁住了某一段,这样进行一些控制,其实就能避免幻读的发生,同时有进行了最小范围的锁定,保证了数据库性能。

当然间隙锁针对的也是索引,没有索引的话也是直接锁表的。

3.意向锁

为了支持多粒度锁机制(multiple granularity locking),即允许行级锁与表级锁共存,而引入了意向锁(intention locks)。意向锁是指,未来的某个时刻,事务可能要加共享/排它锁了,先提前声明一个意向。

SELECT * FROM student where id_card>'110120200402023465' and id_card <'110120200402023481' for UPDATE

这个其实就是一个意向锁。


  1. 意向锁是一个表级别的锁(table-level locking);

  2. 意向锁又分为:

    • 意向共享锁(intention shared lock, IS),它预示着,事务有意向对表中的某些行加共享S锁

    • 意向排它锁(intention exclusive lock, IX),它预示着,事务有意向对表中的某些行加排它X锁


 语法:

select ... lock in share mode;  要设置IS锁;
       select ... for update;       要设置IX锁;

下面是意向锁与S锁与X锁的互斥关系 

 

 至于自增锁,我想大家都用过,每次建表的时候我们的id一般都是设置成自增的,这里其实就加了一个自增锁。

好了,数据库锁就简单的说到这里啦。

No sacrifice,no victory~


推荐阅读
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了logistic回归(线性和非线性)相关的知识,包括线性logistic回归的代码和数据集的分布情况。希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • mysql-cluster集群sql节点高可用keepalived的故障处理过程
    本文描述了mysql-cluster集群sql节点高可用keepalived的故障处理过程,包括故障发生时间、故障描述、故障分析等内容。根据keepalived的日志分析,发现bogus VRRP packet received on eth0 !!!等错误信息,进而导致vip地址失效,使得mysql-cluster的api无法访问。针对这个问题,本文提供了相应的解决方案。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • Python SQLAlchemy库的使用方法详解
    本文详细介绍了Python中使用SQLAlchemy库的方法。首先对SQLAlchemy进行了简介,包括其定义、适用的数据库类型等。然后讨论了SQLAlchemy提供的两种主要使用模式,即SQL表达式语言和ORM。针对不同的需求,给出了选择哪种模式的建议。最后,介绍了连接数据库的方法,包括创建SQLAlchemy引擎和执行SQL语句的接口。 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
author-avatar
962326154_5af7cb
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有