Skip to content
鼓励作者:欢迎打赏犒劳

数据库

事务的四大特性-ACID

事务(Transaction)是确保数据一致性和完整性的重要机制。事务的四大特性——原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),简称 ACID,是数据库事务处理的核心原则。

事务的隔离级别

隔离级别脏读不可重复读幻读
读未提交(Read uncommitted)×(未解决)××
读已提交(Read committed)√(解决)××
可重复读(Repeatable read)×
可串行化(Serializable)

脏读: 读取到未提交的事务数据

不可重复读: 同一事务内多次读取同一行数据时,因其他事务修改该行数据并提交,导致读取结果不一致。例如:事务A两次读取用户年龄,第一次为20岁,第二次因事务B修改变成22岁。‌

幻读‌:同一事务内多次执行相同范围查询时,因其他事务插入或删除符合条件的数据,导致结果集数量不一致。例如:事务A统计年龄>20的用户数,第一次为3条,第二次因事务B插入新用户变为4条。‌

MVCC

MVCC叫多版本并发控制,是一种通过保存数据的历史版本来实现并发访问的技术。它的核心目的是让读操作和写操作可以互不阻塞,从而提升数据库的并发性能

  • mvcc的核心是快照读
  • undo log 是历史版本的支撑(最主要的是里面会存储当前事务的xid和数据和上一个事务的xid,如果回滚的话就可以找到上一个事务的完整数据了)
  • readview 决定谁能看到那个版本 (readview里面存放的有正在进行的事务,还有已经执行的完的事务,这就可以解释,为啥同一个事务中可以获取到最新的未提交事务的数据)

数据库的log日志

1. Binlog (二进制日志)

  • 是什么? Binlog 是 MySQL Server 层 实现的、记录所有更改数据库数据的 DDL(如CREATE/ALTER)和 DML(如INSERT/UPDATE/DELETE)操作的逻辑日志
  • 特点:
    • 逻辑日志: 记录的是操作的逻辑内容,比如UPDATE user SET name = 'Alice' WHERE id = 1;,或者是行变更前后的完整镜像(取决于格式)。
    • 追加写入: 以“追加”的方式写入文件,不会覆盖以前的日志。
    • 用途广泛: 数据恢复

2. Undo Log (回滚日志)

  • 是什么? Undo Log 是 InnoDB 存储引擎层 独有的、用于保证事务原子性多版本并发控制(MVCC)物理逻辑日志
  • 特点:
    • 物理逻辑日志: 它记录的是数据被修改之前的旧版本状态(旧数据的拷贝)。例如,你更新了一行数据,Undo Log 会记录这行数据更新前的内容。
    • 循环使用: Undo Log 存储在特殊的回滚段(Rollback Segments)中,会被循环覆盖。当没有事务再需要读取某个旧版本数据时,对应的 Undo Log 空间就可以被回收。
    • 核心用途: 用于事务回滚 和实现 MVCC(实现读已提交和可重复读隔离级别)。

命中索引还是很慢

可能出现的原因:

  1. 索引建的有问题,索引字段区分不大。
  2. 虽然命中索引,但是回表很慢,可能字段太多或者数据量太大。解决: 覆盖索引,建立一个包含所有查询字段的复合索引
  3. 深度分页。解决: 利用 id 区间来查询分页

什么是聚簇索引和非聚簇索引

在InnoDB存储引擎中,根据索引的存储形式,又可以分为以下两种:

聚簇索引: 索引即数据,数据行直接存储在索引树的叶子节点上,主键就是聚簇索引,InnoDB中一个表只能有一个聚簇索引

非聚簇索引:索引是路由,叶子节点只存储指向数据行位置的指针。普通索引、组合索引、唯一索引都属于非聚簇索引

mysql什么情况下会发生死锁现象

什么是死锁?

死锁是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象。若无外力干涉,它们都将无法进行下去。

核心条件:双方都持有了对方需要的锁,同时又等待对方释放锁。

什么情况下会发生死锁?

死锁通常发生在并发事务对多个不相干的对象(行、表)以不同的顺序进行加锁时。

经典例子:并发转账

假设有一张账户表 accounts

idnamebalance
1Alice1000
2Bob500

两个并发事务执行如下操作:

事务 T1:Alice 给 Bob 转账 100

sql
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- 持有id=1的行锁
UPDATE accounts SET balance = balance + 100 WHERE id = 2; -- 请求id=2的行锁,等待T2释放

事务 T2:Bob 给 Alice 转账 50

sql
BEGIN;
UPDATE accounts SET balance = balance - 50 WHERE id = 2; -- 持有id=2的行锁
UPDATE accounts SET balance = balance + 50 WHERE id = 1; -- 请求id=1的行锁,等待T1释放

此时:

  • T1 持有 id=1 的锁,等待 id=2 的锁。
  • T2 持有 id=2 的锁,等待 id=1 的锁。

双方互相等待,死锁发生。MySQL的InnoDB引擎会自动检测到死锁,并强制回滚其中一个事务(通常是较小的事务,即影响行数较少的事务),让另一个事务成功执行。


如何排查死锁?

  1. 查看error日志。确认 innodb_print_all_deadlocks 配置是否为 ON(默认通常开启),可以直接在MySQL的错误日志文件中搜索 "deadlock" 关键字,查看历史死锁记录。
  2. 使用 SHOW ENGINE INNODB STATUS 命令,这是最直接、最常用的方法 重点看这部分内容
    • TRANSACTION 1TRANSACTION 2:两个事务的信息。
    • WAITING FOR THIS LOCK:某个事务在等待什么锁(锁的类型、锁在哪一行或哪个索引上)。
    • HOLDS THE LOCK:某个事务当前持有什么锁。
    • 根据这些信息,就能清晰地还原死锁现场。

如何解决和避免死锁?

  • 无需过多操作,InnoDB已自动回滚其中一个事务,另一个事务会继续执行
  • 保持事务小巧并尽快提交:缩短事务执行时间,减少锁的持有时间。
  • 注意加锁顺序
  • 为查询创建合适的索引,避免升级表锁,大大增加死锁概率。

如有转载或 CV 的请标注本站原文地址