目录
01 什么是事务?
02 事务隔离的实现
2.3.2.1 快照读和当前读
2.3.2.2 select 当前读
2.3.2.3 事务 C 不马上提交
03巨人的肩膀
04 总结
首页 数据库 mysql教程 一文详解MySQL中的事务和 MVCC 原理

一文详解MySQL中的事务和 MVCC 原理

Mar 09, 2022 am 11:05 AM
mysql 事务

本篇文章带大家了解一下MySQL中的事务,并介绍一下MVCC 原理,希望能够给大家提供帮助!

一文详解MySQL中的事务和 MVCC 原理

01 什么是事务?

数据库事务指的是一组数据操作,事务内的操作要么就是全部成功,要么就是全部失败,什么都不做,其实不是没做,是可能做了一部分但是只要有一步失败,就要回滚所有操作,有点一不做二不休的意思。

在 MySQL 中,事务支持是在引擎层实现的。MySQL 是一个支持多引擎的系统,但并不是所有的引擎都支持事务。比如 MySQL 原生的 MyISAM 引擎就不支持事务,这也是 MyISAM 被 InnoDB 取代的重要原因之一

1.1 四大特性

  • 原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。
  • 一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如 A 向 B 转账,不可能 A 扣了钱,B 却没收到。
  • 隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如 A 正在从一张银行卡中取钱,在 A 取钱的过程结束前,B 不能向这张卡转账。
  • 持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。

1.2 隔离级别

SQL 事务的四大特性中原子性、一致性、持久性都比较好理解。但事务的隔离级别确实比较难的,今天主要聊聊 MySQL 事务的隔离性。

SQL 标准的事务隔离从低到高级别依次是:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(serializable )。级别越高,效率越低

  • 读未提交:一个事务还没提交时,它做的变更就能被别的事务看到。
  • 读提交:一个事务提交之后,它做的变更才会被其他事务看到。
  • 可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
  • 串行化:顾名思义是对于同一行记录,“写” 会加 “写锁”,“读” 会加 “读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。所以种隔离级别下所有的数据是最稳定的,但是性能也是最差的

1.3 解决的并发问题

SQL 事务隔离级别的设计就是为了能最大限度的解决并发问题:

  • 脏读:事务 A 读取了事务 B 更新的数据,然后 B 回滚操作,那么 A 读取到的数据是脏数据
  • 不可重复读:事务 A 多次读取同一数据,事务 B 在事务 A 多次读取的过程中,对数据作了更新并提交,导致事务 A 多次读取同一数据时,结果不一致。
  • 幻读:系统管理员 A 将数据库中所有学生的成绩从具体分数改为 ABCDE 等级,但是系统管理员 B 就在这个时候插入了一条具体分数的记录,当系统管理员 A 改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

SQL 不同的事务隔离级别能解决的并发问题也不一样,如下表所示:只有串行化的隔离级别解决了全部这 3 个问题,其他的 3 个隔离级别都有缺陷

事务隔离级别 脏读 不可重复读 幻读
读未提交 可能 可能 可能
读已提交 不可能 可能 可能
可重复读 不可能 不可能 可能
串行化 不可能 不可能 不可能

PS:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

1.4 举个栗子

这么说可能有点难以理解,举个栗子。还是之前的表结构以及表数据

CREATE TABLE `student`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `age` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 66 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
登录后复制

表数据

假设现在,我要同时启动两个食物,一个事务 A 查询 id = 2 的学生的 age,一个事务 B 更新 id = 2 的学生的 age。流程如下,在四种隔离级别下的 X1、X2、X3 的值分别是怎样的呢?

隔离级别举例

  • 读未提交:X1 的值是 23,因为事务 B 虽然没提交但它的更改已被 A 看到。(如果 B 后面又回滚了 X1 的值就是脏的)。X2、X3 的值也是 23,这无可厚非。
  • 读已提交:X1 的值是 22,因为 B 虽然改了,但 A 看不到。(如果 B 后面回滚了,X1 的值不变,解决了脏读),X2、X3 的值是 23,没毛病,B 提交了,A 才能看到。
  • 可重复读:X1、X2 都是 22,A 开启的时刻值是 22,那么在 A 的整个过程中,它的值都是 22。(不管 B 在这期间怎么修改,只要 A 还没提交,都是看不见的,解决了不可重复读),而 X3 的值是 23,因为 A 提交了,能看到 B 修改的值了。
  • 串行化:B 在执行更改期间会被锁住,直至 A 提交。B 才能继续执行。(A 在读期间,B 不能写。得保证此时数据是最新的。解决了幻读)所以 X1、X2 都是 22,而最后的 X3 在 B 提交之后执行,它的值就是 23。

那为什么会出现这样的结果呢?事务隔离级别到底是怎么实现的呢?

事务隔离级别是怎么是实现的呢?我在极客时间丁奇老师的课上找到了答案:

实际上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。在 “可重复读” 隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图。在 “读提交” 隔离级别下,这个视图是在每个 SQL 语句开始执行的时候创建的。这里需要注意的是,“读未提交” 隔离级别下直接返回记录上的最新值,没有视图概念;而 “串行化” 隔离级别下直接用加锁的方式来避免并行访问

1.5 设置事务隔离级别

不同的数据库默认设置的事务隔离级别也大不一样,Oracle 数据库的默认隔离级别是读提交,而 MySQL 是可重复读。所以,当你的系统需要把数据库从 Oracle 迁移到 MySQL 时,请把级别设置成与搬迁之前的(读提交)一致,避免出现不可预测的问题

1.5.1 查看事务隔离级别

# 查看事务隔离级别
5.7.20 之前
SELECT @@transaction_isolation
show variables like 'transaction_isolation';

# 5.7.20 以及之后
SELECT @@tx_isolation
show variables like 'tx_isolation'

+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |
+---------------+-----------------+
登录后复制

1.5.2 设置隔离级别

修改隔离级别语句格式是:set [作用域] transaction isolation level [事务隔离级别]

其中作用域可选:SESSION(会话)、GLOBAL(全局);隔离级别就是上面提到的 4 种,不区分大小写。

例如:设置全局隔离级别为读提交

set global transaction isolation level read committed;
登录后复制

1.6 事务的启动

MySQL 的事务启动有以下几种方式:

  • 显式启动事务语句, begin 或 start transaction。配套的提交语句是 commit,或者回滚语句是 rollback。
# 更新学生名字
START TRANSACTION;
update student set name = '张三' where id = 2;
commit;
登录后复制
  • set autocommit = 0,这个命令会将线程的自动提交关掉。意味着如果你只执行一个 select 语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动执行 commit 或 rollback 语句,或者断开连接。
  • set autocommit = 1,表示 MySQL 自动开启和提交事务。 比如执行一个 update 语句,语句只完成后就自动提交了。不需要显示的使用 begin、commit 来开启和提交事务。所以当我们执行多个语句的时候,就需要手动的用 begin、commit 来开启和提交事务。
  • start transaction with consistent snapshot;上面提到的 begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个操作 InnoDB 表的语句,事务才真正启动。如果你想要马上启动一个事务,可以使用 start transaction with consistent snapshot 命令。 第一种启动方式,一致性视图是在执行第一个快照读语句时创建的; 第二种启动方式,一致性视图是在执行 start transaction with consistent snapshot 时创建的

02 事务隔离的实现

理解了隔离级别,那事务的隔离是怎么实现的呢?要想理解事务隔离,先得了解 MVCC 多版本的并发控制这个概念。而 MVCC 又依赖于 undo log 和 read view 实现。

2.1 什么是 MVCC?

百度上的解释是这样的:

MVCC,全称 Multi-Version Concurrency Control,即多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。

MVCC 使得数据库读不会对数据加锁,普通的 SELECT 请求不会加锁,提高了数据库的并发处理能力;数据库写才会加锁。 借助 MVCC,数据库可以实现 READ COMMITTED,REPEATABLE READ 等隔离级别,用户可以查看当前数据的前一个或者前几个历史版本,保证了 ACID 中的 I 特性(隔离性)。

MVCC 只在 REPEATABLE READ 和 READ COMMITIED 两个隔离级别下工作。其他两个隔离级别都和 MVCC 不兼容 ,因为 READ UNCOMMITIED 总是读取最新的数据行,而不是符合当前事务版本的数据行。而 SERIALIZABLE 则会对所有读取的行都加锁。

2.1.1 InnDB 中的 MVCC

InnDB 中每个事务都有一个唯一的事务 ID,记为 transaction_id。它在事务开始时向 InnDB 申请,按照时间先后严格递增。

而每行数据其实都有多个版本,这就依赖 undo log 来实现了。每次事务更新数据就会生成一个新的数据版本,并把  transaction_id 记为 row trx_id。同时旧的数据版本会保留在 undo log 中,而且新的版本会记录旧版本的回滚指针,通过它直接拿到上一个版本。

所以,InnDB 中的 MVCC 其实是通过在每行记录后面保存两个隐藏的列来实现的。一列是事务 ID:trx_id;另一列是回滚指针:roll_pt。

2.2 undo log

回滚日志保存了事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读。

根据操作的不同,undo log 分为两种: insert undo log 和 update undo log。

2.2.1 insert undo log

insert 操作产生的 undo log,因为 insert 操作记录没有历史版本只对当前事务本身可见,对于其他事务此记录不可见,所以 insert undo log 可以在事务提交后直接删除而不需要进行 purge 操作。

purge 的主要任务是将数据库中已经 mark del 的数据删除,另外也会批量回收 undo pages

所以,插入数据时。它的初始状态是这样的:

insert undo log

2.2.2 update undo log

UPDATE 和 DELETE 操作产生的 Undo log 都属于同一类型:update_undo。(update 可以视为 insert 新数据到原位置,delete 旧数据,undo log 暂时保留旧数据)。

事务提交时放到 history list 上,没有事务要用到这些回滚日志,即系统中没有比这个回滚日志更早的版本时,purge 线程将进行最后的删除操作。

一个事务修改当前数据:

第二次事务

另一个事务修改数据:

第三次事务

这样的同一条记录在数据库中存在多个版本,就是上面提到的多版本并发控制 MVCC。

另外,借助 undo log 通过回滚可以回到上一个版本状态。比如要回到 V1 只需要顺序执行两次回滚即可。

2.3 read-view

read view 是 InnDB 在实现 MVCC 时用到的一致性读视图,用于支持 RC(读提交)以及 RR(可重复读)隔离级别的实现

read view 不是真实存在的,只是一个概念,undo log 才是它的体现。它主要是通过版本和 undolog 计算出来的。作用是决定事务能看到哪些数据

每个事务或者语句有自己的一致性视图。普通查询语句是一致性读,一致性读会根据 row trx_id 和一致性视图确定数据版本的可见性

2.3.1 数据版本的可见性规则

read view 中主要包含当前系统中还有哪些活跃的读写事务,在实现上 InnDB 为每个事务构造了一个数组,用来保存这个事务启动瞬间,当前正活跃(还未提交)的事务

前面说了事务 ID 随时间严格递增的,把系统中已提交的事务 ID 的最大值记为数组的低水位,已创建过的事务 ID + 1记为高水位

这个视图数组和高水位就组成了当前事务的一致性视图(read view)

这个数组画个图,长这样:

数据版本的可见性规则

规则如下:

  • 1 如果 trx_id 在灰色区域,表明被访问版本的 trx_id 小于数组中低水位的 id 值,也即生成该版本的事务在生成 read view 前已经提交,所以该版本可见,可以被当前事务访问。
  • 2 如果 trx_id 在橙色区域,表明被访问版本的 trx_id 大于数组中高水位的 id 值,也即生成该版本的事务在生成 read view 后才生成,所以该版本不可见,不能被当前事务访问。
  • 3 如果在绿色区域,就会有两种情况:

    • a) trx_id 在数组中,证明这个版本是由还未提交的事务生成的,不可见
    • b) trx_id 不在数组中,证明这个版本是由已提交的事务生成的,可见

第三点我在看教程的时候也有点疑惑,好在有热心网友解答:

落在绿色区域意味着是事务 ID 在低水位和高水位这个范围里面,而真正是否可见,看绿色区域是否有这个值。如果绿色区域没有这个事务 ID,则可见,如果有,则不可见。在这个范围里面并不意味着这个范围就有这个值,比如 [1,2,3,5],4 在这个数组 1-5 的范围里,却没在这个数组里面。

这样说可能有点难以理解,我假设一个场景:三个事务对同一条数据进行查询更新等操作,为此画了张图以方便理解:

三个事务操作

原始数据还是下图这样的,对 id = 2 的张三进行信息的更新:

表数据

针对上图,我想提个问题。分别在 RC(读提交)以及 RR(可重复读)隔离级别下,T4 和 T5 时间点的查询 age 值分别是多少呢?T4 更新的值又是多少呢?思考片刻,相信大家都有自己的答案。答案在文末,希望大家能带着自己的疑问继续读下去。

2.3.2 RR(可重复读)下的结果

RR 级别下,查询只承认在事务启动前就已经提交完成的数据,一旦启动事务就会建视图。所以使用 start transaction with consistent snapshot 命令,马上就会建视图

现在假设:

  • 事务 A 开始前,只有一个活跃的事务,ID = 2,
  • 已提交的事务也就是插入数据的事务 ID = 1
  • 事务 A、B、C 的事务 ID 分别是 3、4、5

在这种隔离级别下,他们创建视图的时刻如下:

RR级别结果

根据上图得,事务 A 的视图数组是[2,3];事务 B 的视图数组是 [2,3,4];事务 C 的视图数组是[2,3,4,5]。分析一波:

  • T4 时刻,B 读数据都是从当前版本读起,过程是这样的:

    • 读到当前版本的 trx_id = 4,刚好是自己,可见
    • 所以 age = 24
  • T5 时刻,A 读数据都是从当前版本读起,过程是这样的:

    • 读到当前版本的 trx_id = 4,比自己视图数组的高水位大,不可见
    • 再往上读到 trx_id = 5,比自己视图数组高水位大,不可见
    • 再往上读到 trx_id = 1,比自己视图数组低水位小,可见
    • 所以 age = 22

这样执行下来,虽然期间这一行数据被修改过,但是事务 A 不论在什么时候查询,看到这行数据的结果都是一致的,所以我们称之为一致性读

其实视图是否可见主要看创建视图和提交的时机,总结下规律:

  • 版本未提交,不可见
  • 版本已提交,但在视图创建后提交,不可见
  • 版本已提交,但在视图创建前提交,可见

2.3.2.1 快照读和当前读

事务 B 的 update 语句,如果按照上图的一致性读,好像结果不大对?

如下图周明,B 的视图数组是先生成的,之后事务 C 才提交。那就应该看不见 C 修改的 age = 23 呀?最后 B 怎么得出 24 了?

更新逻辑

没错,如果 B 在更新之前执行查询语句,那返回的结果肯定是 age = 22。问题是更新就不能在历史版本更新了呀,否则 C 的更新不就丢失了?

所以,更新有个规则:更新数据都是先读后写(读是更新语句执行,不是我们手动执行),读的就是当前版本的值,叫当前读;而我们普通的查询语句就叫快照读

因此,在更新时,当前读读到的是 age = 23,更新之后就成 24 啦。

2.3.2.2 select 当前读

除了更新语句,查询语句如果加锁也是当前读。如果把事务 A 的查询语句 select age from t where id = 2 改一下,加上锁(lock in mode 或者 for update),也都可以得到当前版本 4 返回的 age = 24

下面就是加了锁的 select 语句:

select age from t where id = 2 lock in mode;
 select age from t where id = 2 for update;
登录后复制

2.3.2.3 事务 C 不马上提交

假设事务 C 不马上提交,但是 age = 23 版本已生成。事务 B 的更新将会怎么走呢?

事务 C 不马上提交

事务 C 还没提交,写锁还没释放,但是事务 B 的更新必须要当前读且必须加锁。所以事务 B 就阻塞了,必须等到事务 C 提交,释放锁才能继续当前的读。

被事务 C 锁住

2.3.3 RC(读提交)下的结果

在读提交隔离级别下,查询只承认在语句启动前就已经提交完成的数据;每一个语句执行之前都会重新算出一个新的视图

注意:在上图的表格中用于启动事务的是 start transaction with consistent snapshot 命令,它会创建一个持续整个事务的视图。所以,在  RC 级别下,这命令其实不起作用。等效于普通的 start transaction(在执行 sql 语句之前才算是启动了事务)。所以,事务 B 的更新其实是在事务 C 之后的,它还没真正启动事务,而 C 已提交

现在假设:

  • 事务 A 开始前,只有一个活跃的事务,ID = 2,
  • 已提交的事务也就是插入数据的事务 ID = 1
  • 事务 A、B、C 的事务 ID 分别是 3、4、5

在这种隔离级别下,他们创建视图的时刻如下:

RC级别结果

根据上图得,事务 A 的视图数组是[2,3,4],但它的高水位是 6或者更大(已创建事务 ID + 1);事务 B 的视图数组是 [2,4];事务 C 的视图数组是 [2,5]。分析一波:

  • T4 时刻,B 读数据都是从当前版本读起,过程是这样的:

    • 读到当前版本的 trx_id = 4,刚好是自己,可见
    • 所以 age = 24
  • T5 时刻,A 读数据都是从当前版本读起,过程是这样的:

    • 读到当前版本的 trx_id = 4,在自己一致性视图范围内但包含 4,不可见
    • 再往上读到 trx_id = 5,在自己一致性视图范围内但不包含 5,可见
    • 所以 age = 23

03巨人的肩膀

  • cnblogs.com/wyaokai/p/10921323.html
  • time.geekbang.org/column/article/70562
  • zhuanlan.zhihu.com/p/117476959
  • cnblogs.com/xd502djj/p/6668632.html
  • blog.csdn.net/article/details/109044141
  • blog.csdn.net/u014078930/article/details/99659272

04 总结

本文详细聊了事务的方方面面,比如:四大特性、隔离级别、解决的并发问题、如何设置、查看隔离级别、如何启动事务等。除此以外,还深入了解了 RR 和 RC 两个级别的隔离是怎么实现的?包括详解 MVCC、undo log 和 read view 是怎么配合实现 MVCC 的。最后还聊了快照读、当前读等等。可以说,事务相关的知识点都在这了。看完这一篇还不懂的话,你来捶我呀!

【相关推荐:mysql视频教程

以上是一文详解MySQL中的事务和 MVCC 原理的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它们
1 个月前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

mysql:简单的概念,用于轻松学习 mysql:简单的概念,用于轻松学习 Apr 10, 2025 am 09:29 AM

MySQL是一个开源的关系型数据库管理系统。1)创建数据库和表:使用CREATEDATABASE和CREATETABLE命令。2)基本操作:INSERT、UPDATE、DELETE和SELECT。3)高级操作:JOIN、子查询和事务处理。4)调试技巧:检查语法、数据类型和权限。5)优化建议:使用索引、避免SELECT*和使用事务。

phpmyadmin怎么打开 phpmyadmin怎么打开 Apr 10, 2025 pm 10:51 PM

可以通过以下步骤打开 phpMyAdmin:1. 登录网站控制面板;2. 找到并点击 phpMyAdmin 图标;3. 输入 MySQL 凭据;4. 点击 "登录"。

navicat premium怎么创建 navicat premium怎么创建 Apr 09, 2025 am 07:09 AM

使用 Navicat Premium 创建数据库:连接到数据库服务器并输入连接参数。右键单击服务器并选择“创建数据库”。输入新数据库的名称和指定字符集和排序规则。连接到新数据库并在“对象浏览器”中创建表。右键单击表并选择“插入数据”来插入数据。

navicat怎么新建连接mysql navicat怎么新建连接mysql Apr 09, 2025 am 07:21 AM

可在 Navicat 中通过以下步骤新建 MySQL 连接:打开应用程序并选择“新建连接”(Ctrl N)。选择“MySQL”作为连接类型。输入主机名/IP 地址、端口、用户名和密码。(可选)配置高级选项。保存连接并输入连接名称。

MySQL:世界上最受欢迎的数据库的简介 MySQL:世界上最受欢迎的数据库的简介 Apr 12, 2025 am 12:18 AM

MySQL是一种开源的关系型数据库管理系统,主要用于快速、可靠地存储和检索数据。其工作原理包括客户端请求、查询解析、执行查询和返回结果。使用示例包括创建表、插入和查询数据,以及高级功能如JOIN操作。常见错误涉及SQL语法、数据类型和权限问题,优化建议包括使用索引、优化查询和分表分区。

MySQL和SQL:开发人员的基本技能 MySQL和SQL:开发人员的基本技能 Apr 10, 2025 am 09:30 AM

MySQL和SQL是开发者必备技能。1.MySQL是开源的关系型数据库管理系统,SQL是用于管理和操作数据库的标准语言。2.MySQL通过高效的数据存储和检索功能支持多种存储引擎,SQL通过简单语句完成复杂数据操作。3.使用示例包括基本查询和高级查询,如按条件过滤和排序。4.常见错误包括语法错误和性能问题,可通过检查SQL语句和使用EXPLAIN命令优化。5.性能优化技巧包括使用索引、避免全表扫描、优化JOIN操作和提升代码可读性。

redis怎么使用单线程 redis怎么使用单线程 Apr 10, 2025 pm 07:12 PM

Redis 使用单线程架构,以提供高性能、简单性和一致性。它利用 I/O 多路复用、事件循环、非阻塞 I/O 和共享内存来提高并发性,但同时存在并发性受限、单点故障和不适合写密集型工作负载的局限性。

SQL删除行后如何恢复数据 SQL删除行后如何恢复数据 Apr 09, 2025 pm 12:21 PM

直接从数据库中恢复被删除的行通常是不可能的,除非有备份或事务回滚机制。关键点:事务回滚:在事务未提交前执行ROLLBACK可恢复数据。备份:定期备份数据库可用于快速恢复数据。数据库快照:可创建数据库只读副本,在数据误删后恢复数据。慎用DELETE语句:仔细检查条件,避免误删数据。使用WHERE子句:明确指定要删除的数据。使用测试环境:在执行DELETE操作前进行测试。

See all articles