数据库系列:MySQL慢查询分析和性能优化

数据库系列:MySQL索引优化总结(综合版)

数据库系列:高并发下的数据字段变更

数据库系列:覆盖索引和规避回表

数据库系列:数据库高可用及无损扩容

数据库系列:使用高区分度索引列提升性能

数据库系列:前缀索引和索引长度的取舍

数据库系列:MySQL引擎MyISAM和InnoDB的比较

1 介绍

并发控制是为了防止多用户并发使用数据库时造成数据错误和程序运行错误,保证数据的完整性。当多个事务并发地存取数据库时,就会产生同时读取和/或修改同一数据的情况。若对并发操作不加控制就可能会存取和存储不正确的数据,破坏数据库的一致性(Consistency)。因此,数据库中间件必须提供并发控制(Concurrency Control)机制能力,而MySQL的InnoDB引擎,很好的支持了这一块。

2 InnoDB并发控制

MySQL 是一个流行的关系型数据库管理系统,它支持多用户并发访问。并发控制是确保数据库一致性和完整性的重要机制。在 MySQL 中,有几种方法可以实施并发控制:

  • 读写锁(Read-Write Locks):MySQL 使用了读写锁来控制对数据的并发访问。读写锁是共享的,多个客户端可以同时持有读锁,但只有一个客户端可以持有写锁。当一个客户端获得写锁时,其他客户端无法获得读锁或写锁,直到写锁被释放。
  • 事务隔离级别 :MySQL 提供了不同的事务隔离级别,包括读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。较低的隔离级别允许更多的并发访问,但可能导致数据不一致;较高的隔离级别可以确保数据一致性,但会限制并发访问。
  • 锁等待和死锁:MySQL 提供了锁等待和死锁检测机制。当一个事务尝试获取一个已经被其他事务持有的锁时,MySQL 会阻塞等待,直到锁被释放。如果事务之间形成死锁,MySQL 会检测到并终止其中一个事务以解除死锁。
  • 分段锁定(Segmented Locking):MySQL 还支持分段锁定,它允许对数据库的特定部分进行锁定,而不是对整个数据库进行锁定。这对于处理大型数据集时非常有用,因为它可以减少锁定范围,提高并发性能。
  • 乐观并发控制(Optimistic Concurrency Control):MySQL 还支持乐观并发控制,它假设冲突不太可能发生,因此不会立即锁定数据。而是在更新时检查是否存在冲突,如果存在冲突,则进行适当的处理,比如回滚或重试。
  • 多版本并发控制(MVCC):MVCC允许在事务隔离级别下执行一致性读操作,以提高并发性能。

通过合理配置和使用上述机制,可以在 MySQL 中实现有效的并发控制,来保证在数据库中执行一致性和数据完整性。

下面详细来说说通过并发控制保证数据一致性的常见手段:

  • 锁(Locking)
  • 数据多版本(Multi Versioning)

3 锁的基本实现

MySQL 使用锁来保持数据的一致性。在并发控制中,锁是用来防止多个事务同时对同一数据进行修改或删除,以保持数据的一致性。MySQL 中的锁机制包括共享锁和排他锁。

锁的基本实现,一般是这样的:

  • 当用户对数据进行操作前,锁住,实施互斥,不允许其它任务的操作;
  • 当前操作完成后,释放锁之后,其他任务才可以执行;

但是存在一个问题,他的执行本质是串行的,无论读写,都无法并行,这样性能太差了,也不符合互联网高并发需求。

于是MySQL 中的锁机制实现了共享锁和排他锁:

  • 共享锁(Shared Lock):多个事务可以同时持有共享锁,用于读取数据,但不允许修改数据。共享锁允许并发读取,提高了并发性能。
  • 排他锁(Exclusive Lock):只有一个事务可以持有排他锁,用于修改数据,不允许其他事务同时修改。排他锁会阻塞其他事务的读写操作,降低了并发性能。

    简而言之就是:
  • 共享锁之间不互斥,即读读可以并行,这样提高了数据读取的并发能力
  • 排他锁与任何锁互斥,所以写读,写写不可以并行,其他线程的读写操作都在锁释放之后才能执行,这样对并发度是有较大影响的

所以说,单纯的锁机制,还是不满足需求,为了保证写任务没有完成之时,其他读的任务也可以并发执行,我们就需要使用另外一个能力来补充。

那就是数据多版本(Multi Versioning)

4 数据多版本的实现原理

MySQL的并发控制是通过多版本并发控制(MVCC)实现的。MVCC允许在事务隔离级别下执行一致性读操作,以提高并发性能。

在MySQL的MVCC中,每个数据行都有多个版本,每个版本都表示该行在不同时间点的状态。当一个事务读取数据时,它只看到该事务开始之前存在的数据版本,而不是当前最新的数据版本。这种方式允许并发读取多个数据版本,而不会相互阻塞,进一步提高并发的效果。

详细拆分开来,读写同步执行的原理是这样的:

  • 执行写任务发时,Clone一份数据,打上新的版本号,与原版本号区分
  • 写任务实际操作的是克隆那个版本的数据,直至操作并提交完成后
  • 读任务可以并发执行,持续读取,读的是原版本的数据,并不会造成阻塞

如图所示,可以分成这几个步骤去解读:

  1. 初始数据版本为V1.0
  2. T1发起了一个写任务,这时候把数据clone了一份,进行修改,版本变为V2.0,这时候修改进行中,任务还未完成
  3. T2并发了一个读任务,依然读的是V1.0版本的数据
  4. T3又并发了一个读任务,依然不会阻塞,读的还是V1.0的版本
  5. 这时候数据修改,V1.0的数据变为V2.0的数据
  6. T4这时候发起的度任务,读到的就是V2.0的数据了

从这边可以看出,数据多版本,读写之间不需要阻塞,能够极大提高任务的并发能力。

  • 普通意义上的锁机制,本质是串行执行,效率十分低下
  • 读写锁,可以实现读读并发,但是写读依然是互斥的,也不符合互联网的机制
  • 数据多版本(Multi Versioning),才是实现读写并发的要素

5 MySQL 数据多版本的相关实现

5.1 概念介绍

在MySQL的InnoDB存储引擎中,使用MVCC(多版本并发控制,Multiversion Concurrency Control)实现多版本控制。

MVCC的实现主要基于undo日志、redo日志、rollback segment 回滚存储区间、和read view。undo日志用于回滚操作,而read view用于生成数据行的历史版本。通过这种方式,InnoDB实现了非阻塞的一致性读操作。

  • redo日志

    数据库事务提交后,必须将更新的数据刷到磁盘上,以保证ACID特性。磁盘随机写性能较低,且过度频繁的刷盘,会极大影响数据库的吞吐量。

    优化方式是将修改行为先写到redo日志里,这样随机就变成了有序性,再按照时间周期将数据持久化到磁盘上,极大提高了性能。

    另外一方面,即使数据库崩溃,恢复之后也可以从redo日志里面获取操作Log,重新提交事务操作,然后刷盘,最终保证数据的一致性。

  • undo日志

    数据库事务未提交时,会将事务修改数据的Mirror Data(修改前的版本 )存放到 Undo Log中,它的主要作用是在事务执行过程中,如果发生错误或者需要回滚操作,可以通过Undo Log中的记录来撤销已经执行的操作,恢复数据到事务开始之前的状态。

    另外一方面,数据库奔溃时,也可以使用undo日志,撤销未提交事务,保证事务的ACID特性。

    • insert操作:undo日志存储新数据的PK(ROW_ID),回滚时执行删除即可。
    • delete/update操作:undo日志存储旧数据row(整行数据),回滚时直接恢复。
  • rollback segment

    Rollback Segment(回滚存储区)是数据库中的一部分存储空间,用于临时保存当数据库数据发生改变时的先前值。它主要有两个作用:

    • 通过Rollback操作来取消数据操作,使之恢复到改变之前的原始值,在transaction的过程才有效。如果执行了commit命令,那么Rollback Segment里面的值就会标识为失效,数据的改变将永久化。
    • select读取的同时另一个事务也在修改这个表的值,那么select出来的数据是修改前的值,因为修改之前的原数据存入到了Rollback Segment中,所以不会被阻塞到。

5.2 示例说明

5.2.1 初始数据

# 表结构
t_userinfo(id PK, name, sex, age); # 默认数据
1,Brand,0,22
2,Helenlyn,1,19
3,Sol,0,21

先初始化一个默认的表,里面模拟几条数据。此时没有任何的事务未提交操作,所以回滚段是空的,如上图。

5.2.2 事务操作示例

start transaction;
delete from t_userinfo where id = 1;
update t_userinfo set name = 'Helenlyn...' where id = 2;
insert into t_userinfo(name, sex, age) valus ('Lili', 1, 18);

还未执行commit 或者 rollback,所以事务处于未提交的状态

综上,我们可以看出Commit之前我们进行如下操作:

  • 正式提交删除前,id=1的数据作为旧版本的数据,进入了回滚存储区;
  • 正式提交修改前,id=2的数据作为旧版本的数据,进入了回滚存储区;
  • 新插入的数据 ('Lili', 1, 18), id= 4,在正式提交之前,也进入了回滚段;

我们上面说了,不是所有的操作最终都会commit,如果失败,事务rollback,就可以通过回滚存储区中的undo日志对操作进行回滚。

如果成功commit,则整体提交成功了



可以看到:

  • id=1 数据删除成功;
  • id=2 字段更新成功;
  • id=4 数据行插入成功;
  • 回滚存储区相关日志清掉

如果失败并执行rollback,则全部回滚

  • 数据删除的恢复了
  • 被修改的旧数据也恢复
  • 新增写入的数据删除
  • 回滚存储区相关日志清掉

6 总结

  • MySQL实现并发控制,保证数据一致性的方法有锁,数据多版本等
  • 普通锁串行,读写锁读读并行,Multi Versioning 读写并行;
  • redo日志保证已提交事务的ACID特性, undo日志用来回滚未提交的事务,rollback segment 为临时回滚存储区;
  • InnoDB是基于多版本并发控制的存储引擎;
  • InnoDB用的多版本是快照读不加锁,所有select都是快照读,这些数据不会被修改,并发性能特别高;

数据库系列:InnoDB下实现高并发控制的更多相关文章

  1. (转载)MySQL数据库的几种常见高可用方案

    转自: https://yq.aliyun.com/articles/74454   随着人们对数据一致性的要求不断的提高,越来越多的方法被尝试用来解决分布式数据一致性的问题,如MySQL自身的优化. ...

  2. [数据库系列之MySQL]Mysql优化笔记

    大型网站提速之MySql优化 数据库优化包括的方面 数据库优化是一个综合性的技术,并不是通过某一种方式让数据库效率提高很多,而是通过多方面的提高,从而使得数据库提高很多. 主要包括: 1.表的设计合理 ...

  3. FAQ | 是什么导致MySQL数据库服务器磁盘I/O高(本文章来自知数堂)

    FAQ | 是什么导致MySQL数据库服务器磁盘I/O高 2016-12-26 叶金荣 老叶茶馆 0.导读 有个MySQL服务器的磁盘I/O总有过高报警,怎么回事? 本文约1500字,阅读时间约10分 ...

  4. MySQL数据库和InnoDB存储引擎文件

    参数文件 当MySQL示例启动时,数据库会先去读一个配置参数文件,用来寻找数据库的各种文件所在位置以及指定某些初始化参数,这些参数通常定义了某种内存结构有多大等.在默认情况下,MySQL实例会按照一定 ...

  5. NHibernate系列文章十一:NHibernate并发控制

    摘要 在同一时刻数据访问量和更新次数比较大的系统中,产生了数据的并发访问问题.并发访问使得在这样的环境中,所有用户(程序.实际用户.进程.线程等)的操作不产生负面问题. 如果不使用并发,在两个用户同时 ...

  6. MySQL数据表修复, 如何修复MySQL数据库(MyISAM / InnoDB)

    常用的Mysql数据库修复方法有下面3种: 1. mysql原生SQL命令: repair 即执行REPAIR TABLE SQL语句 语法:REPAIR TABLE tablename[,table ...

  7. CYQ.Data 支持分布式数据库(主从备)高可用及负载调试

    前言: 继上一篇,介绍 CYQ.Data 在分布式缓存上支持高可用,详见:CYQ.Data 对于分布式缓存Redis.MemCache高可用的改进及性能测试 本篇介绍 CYQ.Data 在对数据库层面 ...

  8. MySQL InnoDB下关于MVCC的一个问题的分析

      这个是网友++C++在群里问的一个关于MySQL的问题,本篇文章实验测试环境为MySQL 5.6.20,事务隔离级别为REPEATABLE-READ ,在演示问题前,我们先准备测试环境.准备一个测 ...

  9. Mysql加锁过程详解(9)-innodb下的记录锁,间隙锁,next-key锁

    Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...

  10. centos下搭建高可用redis

    Linux下搭建高可用Redis缓存 Redis是一个高性能的key-value数据库,现时越来越多企业与应用使用Redis作为缓存服务器.楼主是一枚JAVA后端程序员,也算是半个运维工程师了.在Li ...

随机推荐

  1. MyBatis(log4j)

    log4j介绍 Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台.文件.GUI组件,甚至是套接口服务器.NT的事件记录器.UNIX Syslog守护 ...

  2. 模拟ArrayList(顺序表)的底层实现

    模拟ArrayLIst的底层实现 package com.tedu.api04.list; import java.util.Objects; /** * @author LIGENSEN * Dat ...

  3. Linux 问题:网络相关

    防火墙 同网段双网卡 双网关 看服务日志

  4. 【VS Code 与 Qt6】QAction 类的一些事

    QAction 类表示用户命令的一种抽象,包括命令文本.图标.命令触发后要执行的代码.菜单.工具栏按钮往往存在相同的功能,将这些命令独立抽出来,放到 QAction 以象上,可避免编写重复的代码.比如 ...

  5. 关于bzoj3306(树)的一些反思

    1.加零大法好,用好没烦恼 2.不要瞎开long long 3.万物皆可变成wa 4.如果超时,试图把循环中中的东西拉到外面来

  6. Java的readBytes是怎么实现的?

    1.前言 众所周知,Java是一门跨平台语言,针对不同的操作系统有不同的实现.本文从一个非常简单的api调用来看看Java具体是怎么做的. 2.源码分析 从FileInputStream.java中看 ...

  7. Oracle11gR2单实例的安装与部署

    1   安装目标与规划 交易系统1台HP DL580 G9服务器将安装Oracle11gR2 . 1.1  安装介质版本 主机名 his1 IP地址 192.168.40.102(非生产环境ip) O ...

  8. 基于CUBEMX的STM32F4 Hal库,配置LVGL(无操作系统版)

    本篇文章移植思路适用于所有嵌入式MCU,包括Arm,STM32,NXP,乐鑫,Nuvoton,Arduino,RT-Thread,Zephyr,NuttX,Adafruit等等. 为什么要写这一篇移植 ...

  9. php批量同步数据

    php批量同步流程 首先分页获取数据 创建临时表 批量添加数据 备份原表 删除原表 修改临时表表名改为原表 代码 1 <?php 2 3 class Stock{ 4 5 private $da ...

  10. [数据分析与可视化] Python绘制数据地图5-MovingPandas绘图实例

    MovingPandas是一个基于Python和GeoPandas的开源地理时空数据处理库,用于处理移动物体的轨迹数据.关于MovingPandas的使用见文章:MovingPandas入门指北,本文 ...