MySQL唯一索引失效的注意点


【1.实验环境】

Docker环境下的MySQL
MySQL版本: 5.7.37

【2.表结构】

CREATE TABLE `t_1` (`id` int(11) NOT NULL AUTO_INCREMENT,`student_id` char(120) DEFAULT NULL,`course_id` char(120) DEFAULT NULL,`is_delete` int(11) NOT NULL DEFAULT '0',PRIMARY KEY (`id`),KEY `idx_student_id_is_delete` (`student_id`,`is_delete`)UNIQUE KEY `uk_student_id_course_id` (`student_id`,`course_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

在 student_id 和 course_id 两个字段上创建了唯一索引

【3.my.cnf配置】

向 my.cnf 文件中添加以下4个配置项

[mysqld]innodb_buffer_pool_size = 64M
innodb_buffer_pool_load_at_startup = 0
innodb_buffer_pool_dump_at_shutdown = 0
innodb_buffer_pool_dump_pct = 0

MySQL 和 RDS MySQL 数据库的 innodb_buffer_pool_load_at_startup 和 innodb_buffer_pool_dump_at_shutdown 默认值都是1

MySQL 的 innodb_buffer_pool_load_at_startup
RDS MySQL 开放参数一览表

【4.初始化数据】

使用 Python 脚本向 t_1 表里初始化数据

#! /usr/bin/env python"""
pip install pymysql
"""import pymysql
import random
import osdef t():i = 0while i < 50000:try:id = random.randint(1000,8000000)student_id = str(random.randint(1000000000000000,8000000000000000))course_id = str(random.randint(1000000000000000,8000000000000000))sql = "INSERT IGNORE INTO t_1(id,student_id,course_id) VALUES(%s,%s,%s) " % (id, student_id, course_id)os.system('mysql -uroot -p9527 -h172.31.3.199 -P3306 -e "use db0; %s"' % (sql))except:passi = i + 1if __name__ == '__main__':t()

保证聚簇索引中前边的数据与后边的数据所在叶子节点的页相差很远

我向表里初始化了 49826 条数据

mysql> SELECT count(1) FROM t_1;
+----------+
| count(1) |
+----------+
|    49826 |
+----------+
1 row in set (0.51 sec)

【5.操作】

首先, 查询前10条数据

mysql> SELECT * FROM t_1 ORDER BY id ASC LIMIT 10 ;
+------+------------------+------------------+-----------+
| id   | student_id       | course_id        | is_delete |
+------+------------------+------------------+-----------+
| 1146 | 1663872557190860 | 4217153589627926 |         0 |
| 1158 | 3252641372188845 | 1885989893713950 |         0 |
| 1170 | 3505508562693832 | 5842914532945726 |         0 |
| 1176 | 4735722558899119 | 6217057537289160 |         0 |
| 1915 | 1183711356591177 | 1086968443403080 |         0 |
| 1920 | 4745308528623498 | 6039228952996318 |         0 |
| 2129 | 2938861665097838 | 3951826741079136 |         0 |
| 2522 | 3131140464950062 | 2272868851197166 |         0 |
| 2803 | 3015035454377989 | 3334778163743394 |         0 |
| 2820 | 3768213727956738 | 1119168911648982 |         0 |
+------+------------------+------------------+-----------+

构造一个与 id = 2820 一样的数据

-- 本插入语句使用的 id = 1000000, 所以要保证 t_1 表里没有 id = 1000000的数据, 如果 t_1 表里有 id = 1000000的数据, 可以继续使用 id = 1000001依此类推.
INSERT INTO t_1(id,student_id,course_id,is_delete) VALUES(1000000,'3768213727956738','1119168911648982',0) ;

以上插入语句, 构造了一个 t_1 表里已经有 student_id = 3768213727956738 , course_id = 1119168911648982 的数据.
因为 在 student_id 和 course_id 两个字段上创建了唯一索引, 接下来我们看一下, id = 1000000的数据是否会插入成功.


重启MySQL服务

登录数据库, 依次执行 set unique_checks=0; use db0; 插入语句
mysql> set unique_checks=0;
Query OK, 0 rows affected (0.00 sec)mysql> use db0;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -ADatabase changed
mysql> INSERT INTO t_1(id,student_id,course_id,is_delete) VALUES(1000000,'3768213727956738','1119168911648982',0) ;
Query OK, 1 row affected (0.01 sec)

插入成功了

我们验证下

mysql> select * from t_1 where student_id='3768213727956738' and course_id='1119168911648982';
+------+------------------+------------------+-----------+
| id   | student_id       | course_id        | is_delete |
+------+------------------+------------------+-----------+
| 2820 | 3768213727956738 | 1119168911648982 |         0 |
+------+------------------+------------------+-----------+
1 row in set (0.00 sec)

哎, 这里只查询到了 id = 2820 的数据, 没有查询到我们新插入的 id = 1000000的数据.
看一下该 SQL 的执行计划

mysql> explain select * from t_1 where student_id='3768213727956738' and course_id='1119168911648982';
+----+-------------+-------+------------+-------+-------------------------+-------------------------+---------+-------------+------+----------+-------+
| id | select_type | table | partitions | type  | possible_keys           | key                     | key_len | ref         | rows | filtered | Extra |
+----+-------------+-------+------------+-------+-------------------------+-------------------------+---------+-------------+------+----------+-------+
|  1 | SIMPLE      | t_1   | NULL       | const | uk_student_id_course_id | uk_student_id_course_id | 962     | const,const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+-------+-------------------------+-------------------------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.01 sec)

以上 SQL 使用了 uk_student_id_course_id 索引, MySQL回表只会取一条数据

接下来强制使用主键索引 , 采用 force index(primary)

mysql> select * from t_1 force index(primary) where student_id='3768213727956738' and course_id='1119168911648982';
+---------+------------------+------------------+-----------+
| id      | student_id       | course_id        | is_delete |
+---------+------------------+------------------+-----------+
|    2820 | 3768213727956738 | 1119168911648982 |         0 |
| 1000000 | 3768213727956738 | 1119168911648982 |         0 |
+---------+------------------+------------------+-----------+
2 rows in set (0.44 sec)

查询到了2条数据, 它们的 student_id 和 course_id 一样. 唯一索引失效了 .

然而这并不是MySQL的bug, 出现这样的问题, 责任在于使用者自身 .


【6.解惑】

首先, 这样要说一下MySQL里的ChangeBuffer. 在我们的 t_1 表上有一个普通的二级索引 idx_student_id_is_delete, 当向 t_1 表里更新数据的时候( update t_1 set is_delete = 1 where student_id = ‘1663872557190860’ ), 除了要更新聚簇索引上的数据, 还要更新二级索引 idx_student_id_is_delete 上的数据, 为了提高更新二级索引的性能, 引入了ChangeBuffer. 在更新二级索引的时候, 不需要先从磁盘读取二级索引数据页( 读取二级索引是随机读,性能差 ), 而是先把数据放在ChangeBuffer里, MySQL会在合适的时机将ChangeBuffer里的数据更新到二级索引数据页上. 然而这里说的二级索引不包含唯一索引, 假如我们要更新唯一索引, 为了保证唯一性, 就不能把数据放在ChangeBuffer里, 必须要读取磁盘,进行唯一性判断, 这样就会导致更新性能差.

于是乎MySQL提供了一个配置项 unique_checks , 默认 unique_checks = 1, 也就是插入唯一索引时,必须进行唯一性校验, 需要读取磁盘. 当 unique_checks = 0, 在插入唯一索引时, 就会使用到 ChangeBuffer 了, 这样就会造成, 重复的数据会插入到聚簇索引的数据页上, 即便MySQL在合适的时机将ChangeBuffer里的数据插入到唯一索引的数据页上时, 发现重复了, 也于事无补了. 聚簇索引的数据页和唯一索引的数据页不是同一个数据页, 在 unique_checks = 0 时两个数据页互不影响.


当使用者将 unique_checks = 0 时, 需要使用者自己保证插入的数据没有重复的 .




[ 外链 ]

1.ChangBuffer
2.官方 unique_checks

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

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

相关文章

QuPath学习④ 脚本使用

目录 1&#xff0c;基础学习 脚本打印项目中所有图像 访问当前图像内容 访问图像元数据 访问像素 创建ROI 创建对象&#xff08;使 ROI 可见&#xff09; 多个ROI Working with BufferedImage 使用 ImageJ 命令示例 2 脚本导出数据&#xff08;重点&#xff09; …

React16源码: Hooks源码实现

Hooks 1 &#xff09;概述 Hooks 在 React16.7版本出现的新功能Hooks 改变了整体应用开发的模式&#xff0c;同时开发体验会和以前会变得不一样Hooks 让函数组件具有类组件的能力 在 function component 里面没有this无法保存 state通过 Hooks可以让 function component 代替…

PSoc62™开发板之串口通信

实验目的 1.使用串口和PC机通信&#xff1a;接收和发送信息 2.接收GNSS模块定位信息 实验准备 PSoc62™开发板CH340 USB转TTL模块ATGM332D GNSS模块公母头杜邦线x4 板载资源 板载有多少uart 创建工程例程&#xff0c;在libraries/HAL_Drivers/uart_config.h中查看BSP支持…

跨境电商如何通过API选品文章

跨境电商通过API选品是一个相对复杂的过程&#xff0c;涉及到多个环节和考虑因素。以下是一个简化的概述&#xff0c;介绍了如何通过API进行选品&#xff0c;由于字数限制&#xff0c;这里仅能展示部分内容&#xff0c;如需5000字的文章&#xff0c;可能需要根据这个提纲进一步…

凯越推出复古150踏板欧洲先上?DAE150/150亮相

今天临下班发现凯越在欧洲的官网上更新了一台复古踏板&#xff0c;外观别说还有点精致的意思&#xff0c;一共分为125和150两个配置&#xff0c;都是采用的水冷单缸发动机。 配置和参数等数据简单过一下&#xff0c;这种车型更多的是看外观了&#xff0c;仪表采用的LCD的显示屏…

MySQL之四大引擎、账号管理以及建库认识

目录 一、数据库存储引擎&#xff08;发动机&#xff09; 1.1、认识引擎 1.2、查看存储引擎 1.3、引擎常识 1.4、support字段说明 1.5、四大引擎 二、数据库管理 2.1、元数据库介绍&#xff1a; 2.2、分类&#xff1a; 2.3、增删改查以及使用操作 2.4、权限 三、数…

ArkTS语言应用开发入门指南与简单案例解析

文章目录 前言创建项目及其介绍简单案例学习本文总结问答回顾-学习前言 在前几节课中,我们已经了解了ArkTS语言的特点以及其基本语法。现在,我们将正式利用ArkTS来进行应用开发。本节课将通过一个快速入门案例,让大家熟悉开发工具的用法,并介绍UI的基础概念。 创建项目及…

Mnist手写体数字数据集介绍与在Pytorch中使用

1.介绍 MNIST&#xff08;Modified National Institute of Standards and Technology&#xff09;数据集是一个广泛用于机器学习和计算机视觉研究的常用数据集之一。它由手写数字图像组成&#xff0c;包括0到9的数字&#xff0c;每张图像都是28x28像素的灰度图像&#xff0c;图…

探索大模型语言(LLM)科技的革新

文章目录 一. 引言二. 了解大模型语言2.1 什么是LLM&#xff1f;2.2 大模型与大模型语言的区分 三. 机器学习3.1 AI开发3.2 机器学习服务 四. 大模型的应用场景五. 全篇总结 一. 引言 自然语言处理领域的发展取得了巨大的突破&#xff0c;其中广义语言模型&#xff08;LLM&…

pytorch学习笔记

torchvision处理图像的 pytorch官网上看数据集的包&#xff0c;COCO数据集目标检测、语义分割&#xff0c;cifar物体识别 预训练好的模型 这个模块是图片的处理 root-位置&#xff0c;train-创建的true是个训练集&#xff0c;transform 前面是输出图片的数据类型&#xff0c;“…

ByteTrack算法流程的简单示例

ByteTrack ByteTrack算法是将t帧检测出来的检测框集合 D t {\mathcal{D}_{t}} Dt​ 和t-1帧预测轨迹集合 T ~ t − 1 {\tilde{T}_{t-1}} T~t−1​ 进行匹配关联得到t帧的轨迹集合 T t {T_{t}} Tt​。 首先使用检测器检测t帧的图像得到检测框集合 D t {\mathcal{D}_{t}} …

md文件图片上传方案:Github+PicGo 搭建图床

文章目录 1. PicGo 下载2. 配置Github3. 配置PicGo4. PicGo集成Typora4.1 picGo监听端口设置 5. 测试 1. PicGo 下载 下载地址&#xff1a;https://molunerfinn.com/PicGo/ 尽量下载稳定版本 2. 配置Github 1. 创建一个新仓库&#xff0c;用于存放图片 2. 生成一个token&a…

【安卓的签名和权限】

Android 编译使用哪个key签名&#xff1f; 一看Android.mk 在我们内置某个apk的时候都会带有Android.mk&#xff0c;这里面就写明了该APK使用的是什么签名&#xff0c;如&#xff1a; LOCAL_CERTIFICATE : platform表明使用的是platform签名 LOCAL_CERTIFICATE : PRESIGNED…

Redis 生产环境查找无过期时间的 key

在项目中,Redis 不应该被当作传统数据库来使用;储存大量没有过期时间的数据。如果储存大量无过期时间,而且无效的key的话;再加上 Redis 本身的过期策略没有被正确设置,就会大量占用内存。这样就会导致再多的内存资源也不够用。 情况大致是这样,项目中采用 Redis 二级存储…

SpringBoot整合ElasticSearch实现CRUD操作

本文来说下SpringBoot整合ES实现CRUD操作 文章目录 概述项目搭建ES简单的crud操作保存数据修改数据查看数据删除数据 本文小结 概述 SpringBoot支持两种技术和es交互。一种的jest&#xff0c;还有一种就是SpringData-ElasticSearch。根据引入的依赖不同而选择不同的技术。反正作…

leetcode2967. 使数组成为等数数组的最小代价

文章目录 题目思路复杂度Code 题目 给你一个长度为 n 下标从 0 开始的整数数组 nums 。 你可以对 nums 执行特殊操作 任意次 &#xff08;也可以 0 次&#xff09;。每一次特殊操作中&#xff0c;你需要 按顺序 执行以下步骤&#xff1a; 从范围 [0, n - 1] 里选择一个下标 …

代码随想录算法训练营Day16 | 654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树

LeetCode 654 最大二叉树 本题思路&#xff1a;我们可以看到每次其实这个找最大值&#xff0c;然后创建节点的过程就是一个二叉树的前序遍历的过程。所以&#xff0c;我们可以递归来完成它。 先创找到数组中&#xff0c;最大的值的下标&#xff0c;然后创建根节点然后根据下标…

pytest装饰器:@pytest.mark.incremental

pytest.mark.incremental 是一个pytest中的装饰器&#xff0c;用于标记增量测试。增量测试是一种测试策略&#xff0c;它将测试分解成多个递增的步骤或阶段&#xff0c;并按顺序执行这些步骤。 pytest.mark.incremental 装饰器的作用是告诉pytest&#xff0c;该测试函数是一个…

c语言-整型在内存的存储

文章目录 前言一、整型数值在内存中的存储1.1 整型数值的表示形式1.2 二进制的表示形式1.3 整数在内存中存储 二、大端字节序存储和小端字节序存储2.1 大端字节序存储2.2 小端字节序存储2.3 练习 总结 前言 本篇文章叙述c语言中整型数据在内存中的存储方式。 一、整型数值在内…

Vue学习计划-Vue3--核心语法(一)OptionsAPI、CompositionAPI与setup

1. OptionsAPI与CompositionAPI Vue2的API设计是Options(配置)风格的Vue3的API设计是Composition(组合)风格的 Options API的弊端&#xff1a; Options类型的API&#xff0c;数据、方法、计算属性等&#xff0c;是分散在&#xff1a;data、methods、computed中的&#xff0c;若…