两个事务并发写,能保证数据唯一吗?

news2024/10/7 7:33:40

哟,又是我小白。最近有点高产了。

连我自己都害怕了。

直接进入正题吧。

两个事务并发写,能保证数据唯一吗?

我先来解释下标题讲的是个啥。

我们假设有这么一个用户注册的场景。用户并发请求注册新用户。

你有一张数据库表,也就是下面的user表。

产品经理要求用户和用户之间,电话号码不能重复,为了保证这一点。我们想到了先查一下数据库,再判断一下,如果存在,就退出,否则插入一条数据。类似下面这样的伪代码。

select user where phone_no =2;  // 查询sql
if (user 存在) {
		return 
} else {
  insert user;   // 插入sql
}
复制代码

但这是两条sql语句,先执行查询sql,判断后再决定要不要执行插入sql。每次用户注册的时候都会执行这么一段逻辑。

那如果,此时有多个用户在做操作,就会并发执行这段逻辑。

如果都并发执行,第一条sql语句执行完之后,都会发现没有用户存在。此时都执行了插入,这样就出现了两条一样的数据才对。

所以,有人就想了,这两条sql语句逻辑应该是一个整体,不应该拆开,于是就想到了事务,通过事务把这两个sql作为一个整体,要么一起执行,要么都回滚。

这正是数据库ACID里的A(Atomicity),原子性的完美体现啊。

伪代码类似下面这样。

begin;
select user where phone_no =2;  // 查询sql
if (user 存在) {
		return 
} else {
  insert user;   // 插入sql
}
commit;
复制代码

那么问题来了,这段逻辑,并发执行,能保证数据唯一?

当然是不能。

事务內的多条sql语句,确实是原子的,要么一起成功,要么一起失败,这没错,但跟这个场景没什么太大关系。事务是并发执行的,第一个事务执行查询用户,并不会阻塞另一个事务查询用户,所以都有可能查到用户不存在,此时两个事务逻辑都判断为用户不存在,然后插入数据库。事务内两条sql都执行成功了,于是就插入了两条一样的数据。

怎么保证数据唯一?

那么我们接下来聊聊,怎么保证上面这种场景下,插入的数据是唯一的。方法有很多种,但我们今天只讨论mysql内部的做法,不考虑其他外部中间件(比如redis分布式锁这些)。

唯一索引

通过下面的命令,可以为数据库user表的phone_no字段加入唯一索引。

ALTER TABLE `user` ADD unique(`phone_no`);
复制代码

我们执行一条写操作时,比如下面这句,

INSERT INTO `user` (`user_name`, `phone_no`) VALUES('小红', 2);
复制代码

第一次会插入成功,第二次再执行插入,则会出现报错。

Duplicate entry '2' for key 'phone_no'
复制代码

含义是phone_no这个字段是唯一的,加两次phone_no=2会导致重复。

于是乎回到我们文章开头的场景里,就完美解决了重复插入的问题了。

那么问题来了。

为什么唯一索引能保证数据唯一?

我们看看一句写操作,会经历什么。

首先,mysql作为一个数据库,内部主要分为两层,一层是server层,一层是存储引擎层(一般是innodb)。

server层主要管的是数据库链接,权限校验,以及sql语句校验和优化之类的工作。请求打到存储引擎层,才是真正的查询和更新数据的操作。

大家都知道数据库是持久化存储,且最后都是把数据存到磁盘上的。

那数据库读写是直接读写磁盘数据吗?

不是,如果直接读写磁盘的话,那就太慢了,为了提升速度。

它在磁盘前面加了一层内存,叫buffer pool。它里面有很多细节,但最主要的就是个双向链表,里面放的是一个个数据页,每个数据页的大小默认是 16kb,数据页里面放的就是磁盘的数据。

于是有了这层buffer pool内存,mysql的读和写操作都可以先操作这部分内存,如果想要读写的数据页不在buffer pool里,再跑到磁盘里去捞。由于读写内存的速度比读写磁盘快得多

所以引擎读写都快多了。

但这还不够,很多时候写操作,我的诉求就是把xx更新为xx,或插入xx,数据库光知道这一点就够了,我根本不需要知道数据页原来长什么样子。

有点抽象?举个例子吧。

比方说我想要把id=1的这条数据的phone_no字段更新为100,数据库知道这一点就够了,至于这条数据原来phone_no究竟是等于20,还是30,这根本不重要,反正最后都会变成我想要的phone_no=100。

也就是说,如果有那么一块内存,记录下我准备把数据改成什么样子,然后后续异步慢慢更新到磁盘数据上。那我甚至到不需要在一开始就把这块数据从磁盘读到buffer pool中,按照这个思路,change buffer就来了。

于是乎,写加了普通索引的数据,它只要把想要写的内容写到change buffer上,就立马结束返回了。后面innodb引擎拿着这个change buffer,再异步读入磁盘数据到内存,将change buffer的数据修改到数据页中,再写回磁盘,这速度就上来了,秒啊。

但这个change buffer,放在唯一索引这里就不管用了,毕竟,它得保证数据真的只有一条,那就得去看下数据库里,是不是真的有这条数据。

所以,对于insert场景,普通索引把需求扔到change buffer就完事返回了,而唯一索引需要真的把数据从磁盘读到内存来,看下是不是有重复的,没重复的再插入数据。

这唯一索引,在性能上就输了一截了。

所以回到唯一索引为什么能保证数据唯一的问题上,一句话概括就是,唯一索引会绕过change buffer,确保把磁盘数据读到内存后再判断数据是否存在,不存在才能插入数据,否则报错,以此来保证数据是唯一的。

总结

  • 加唯一索引可以保证数据并发写入时数据唯一,而且最省事省心。
  • 数据库通过引入一层buffer pool内存来提升读写速度,普通索引可以利用change buffer提高数据插入的性能。
  • 唯一索引会绕过change buffer,确保把磁盘数据读到内存后再判断数据是否存在,不存在才能插入数据,否则报错,以此来保证数据是唯一的。

给大家留个问题呗,前面也提到了,innodb中,利用了change buffer,为普通索引做了加速有没有哪些场景下,change buffer不仅不能给普通索引加速,还起到反作用的呢?

最后

大家也别笑,文章开头提到的通过开事务来保证数据唯一性的错误操作,其实很容易犯,而且我曾经也遇到过不止一次这样的事情。

做这个操作的人,还会信誓旦旦,言之凿凿的说出他的理解,在我解释了几遍发现无果之后,我选择低头假装思考,然后说:"你说的有点道理,我再回去好好想想",然后默默的为数据表加上唯一索引......

我相信对方肯定已经理解了。那一刻,我感觉我写的不是代码,我写的是人情世故。

如果文章对你有帮助,欢迎.....

算了。

别说了,一起在知识的海洋里呛水吧

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/25320.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据可视化】第二章——基于matplotlib的数据可视化

目录系列课程学习目标1. 基于matplotlib的数据可视化2 matplotlib.pyplot函数库简介3 matplotlib.pyplot相关函数简介3.1 plt.plot(x, y, format_string, **kwargs)3.2 plt.title()3.3 plt.xlabel()/ylabel()3.4 plt.legend()3.5 pyplot的中文显示3.5.1 第一种方法3.5.2 第二种…

C#上位机系列(2)—常用控件的介绍:文本标签,按键,输入框

本文是讲解C#.net平台的Winform框架下的第二个内容,手把手介绍项目的创建方式以及一些写软件时常用的功能,讲解从零开始的每一个步骤。 本次介绍三个最常用控件的使用 1.文本控件Label 功能:显示一个文本 2.按键控件Button 功能&#x…

考研 | 组成原理【第二章】数据的表示和运算

考研 | 组成原理【第二章】数据的表示和运算 文章目录考研 | 组成原理【第二章】数据的表示和运算I. 数制与编码a. 进位计数制及其相互转换b. BCD码1. 8421码2. 余3码3. 2421码c. 无符号整数表示和运算1. 表示2. 运算d. 带符号整数的表示和运算1. 原码2. 反码3. 补码4. 移码5. …

多肽标签VSV-G Tag、YTDIEMNRLGK

VSV-G肽是一种源自水泡性口腔炎病毒糖蛋白的11个氨基酸的肽。编号: 191225 中文名称: 多肽标签VSV-G Tag 英文名: VSV-G Tag 英文同义词: VSV tag 单字母: H2N-YTDIEMNRLGK-OH 三字母: H2N-Tyr-Thr-Asp-Ile-Glu-Met-Asn-Arg-Leu-Gly-Lys-COOH 氨基酸个数: 11 分子式: C57H94N16…

【HMS Core】ScanPlus和Push推送库出现冲突该如何解决?

1、问题描述 项目中之前是同时集成了华为统一扫码服务SDK和华为推送SDK,这两天升级华为推送版本至6.3.0.304时发现这两个库出现了冲突, 这两个库的依赖版本情况如下所示: implementation com.huawei.hms:scanplus:2.7.0.301 implementatio…

[附源码]java毕业设计小型医院药品及门诊管理

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

《垃圾回收算法手册 自动内存管理的艺术》——内存分配(笔记)

文章目录七、内存分配7.1 顺序分配7.2 空闲链表分配7.2.1 首次适应分配7.2.2 循环首次适应分配7.2.3 最佳适应分配7.2.4空闲链表分配的加速快速适应分配(fast-fit allocation)位图适应分配(bitmapped-fits allocation)7.3 内存碎片化7.4 分区适应分配7.4.1 内存碎片7.4.2 空间大…

LINUX SECCOMP模块介绍

SECCOMP介绍 Seccomp是 "secure computing" 的 缩写。Linux内核2.6.12版本(2005年3月8日)引入。是linux一个安全模块,用于限制程序系统调用;当时如果使用了SECCOMP只允许4个系统调用: read,wri…

Flutter组件--Padding和AnimatedPadding

示意图: Padding介绍 在应用程序中有许多widget 时,这个时候画面常常会变得很拥挤,这个时候如果想要在widget之间来保留一些间距,那就用 Padding 为什么使用 Padding 而不使用 Container.padding 属性的 Container? Container 是将许多更…

RabbitMQ-08 不公平分发与预取值

不公平分发 我们之前演示的所有示例都遵循轮询分发,也就是多个消费者时交替消费队列消息。可以想一下我们之前的一个例子。Worker02处理消息只需要1s但是Worker03处理一个消息却用30s的情况。那么这个时候我们就要使用到不公平分发。简而言之就是能者多劳。 需要在消…

亚马逊刷单测评的正确方法是什么样的?

做亚马逊刷单测评时想要有效果,测评方法很重要,不能直接通过链接进行下单,这样显的很不真实,如果是要找人帮忙测评,可以通过以下方式进行: 1、通过关键词搜索进入 选择自己想刷的关键词,直接搜…

物联网安全年报小结

小结 不仅仅是 LockerGoga,其他勒索软件也对工业系统造成了重大损失,如全球第二大听力集团 Demant 被勒索造成损失达 9500 万美元 [21];世界上最大的飞机零部件供给商之一 ASCO,因其位于 比利时扎芬特姆的工厂系统遭勒索病毒传染…

目标检测算法——YOLOv5/YOLOv7改进之结合​RepVGG(速度飙升)

>>>深度学习Tricks&#xff0c;第一时间送达<<< 目录 RepVGG——极简架构&#xff0c;SOTA性能&#xff01;&#xff01;&#xff01; &#xff08;一&#xff09;前沿介绍 1.RepVGGBlock模块 2.相关实验结果 &#xff08;二&#xff09;YOLOv5/YOLOv7改…

Alibaba官方上线,SpringBoot+SpringCloud全彩指南(第五版)

Alibaba作为国内一线互联网大厂&#xff0c;其中springcloudAlibaba更是阿里微服务最具代表性的技术之一&#xff0c;很多人只知道springcloudAlibaba其实面向微服务技术基本上都有的下面就给大家推荐一份Alibaba官网最新版&#xff1a;SpringBootSpringCloud微服务全栈开发小册…

基于STM32G431嵌入式学习笔记——三、KEY按键入门

一、按键在CubeXM里的配置 可以打开先前配置过LED的CubeMX&#xff0c;在其基础上进行按键的配置。 个人建议在这里先将原环境拷贝 更改副本文件夹名为LED_KEY_LCD 打开文件夹中的.ioc文件进行配置 在配置之前&#xff0c;我们先查阅产品手册了解按键的电路图&#xff0c;以…

07-Nginx 日志管理及自动切割

Nginx 日志管理及自动切割 对于程序员、运维来说&#xff0c;日志非常得重要。通过日志可以查看到很多请求访问信息&#xff0c;及异常信息。Nginx 也提供了对日志的强大支持。 日志管理范围 首先&#xff0c;下面要讲的这些日志相关属性可以配置在任意模块。在不同的模块&…

如果我在初用tomcat时,是看到这篇tomcat架构解析,是不是就不会被说菜鸡了!

写在前面 tomcat作为一个中间件&#xff0c;相信绝大多数java程序员&#xff0c;多多少少都应该用到过吧。尤其是在springboot还未流行的时候&#xff0c;本地环境&#xff0c;没少跑tomcat服务吧&#xff1f; 我们一般都是直接使用&#xff0c;将我们的web服务&#xff0c;直…

疫情下跨越一万公里的友情:熊超与飒特电子哨兵的故事

在熊超&#xff08;化名&#xff09;家中&#xff0c;有一面照片墙&#xff0c;上面贴满了他在非洲工作十多年的剪影。这些照片整齐地摆放成一个心形&#xff0c;挂在客厅最显眼的位置。每当亲戚朋友来访&#xff0c;熊超都会为他们讲起自己援助非洲十余年发生的故事。 十多年…

【无标题】EXCEL实现刷题

实现的思路&#xff1a; 导入题库word文件导入到excel–>绑定随机事件选定考题。 word题库导入Excel表的代码如下&#xff1a; 整理题库结构&#xff08;添加题号&#xff0c;分离答案&#xff09; Public Sub numAdd() Dim rng As Range, RNG1 As Range With Sheet1 .[b1]…

linuex服务器中如何安装mysql数据库(一次性完成,包含远程连接)

大家好。我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂 今天给大家介绍一下&#xff0c;如何在新服务器中安装mysql数据库&#xff0c;总是捣鼓服务器&#xff0c;每次都去网上查&#xff0c;次次都不一样&#xff0c;浪费好多时间&#xff0c;这次自己记录…