
数据库
事务的四大特性-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(实现读已提交和可重复读隔离级别)。
命中索引还是很慢
可能出现的原因:
- 索引建的有问题,索引字段区分不大。
- 虽然命中索引,但是回表很慢,可能字段太多或者数据量太大。解决: 覆盖索引,建立一个包含所有查询字段的复合索引
- 深度分页。解决: 利用 id 区间来查询分页
什么是聚簇索引和非聚簇索引
在InnoDB存储引擎中,根据索引的存储形式,又可以分为以下两种:
聚簇索引: 索引即数据,数据行直接存储在索引树的叶子节点上,主键就是聚簇索引,InnoDB中一个表只能有一个聚簇索引
非聚簇索引:索引是路由,叶子节点只存储指向数据行位置的指针。普通索引、组合索引、唯一索引都属于非聚簇索引
mysql什么情况下会发生死锁现象
什么是死锁?
死锁是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象。若无外力干涉,它们都将无法进行下去。
核心条件:双方都持有了对方需要的锁,同时又等待对方释放锁。
什么情况下会发生死锁?
死锁通常发生在并发事务对多个不相干的对象(行、表)以不同的顺序进行加锁时。
经典例子:并发转账
假设有一张账户表 accounts
:
id | name | balance |
---|---|---|
1 | Alice | 1000 |
2 | Bob | 500 |
两个并发事务执行如下操作:
事务 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引擎会自动检测到死锁,并强制回滚其中一个事务(通常是较小的事务,即影响行数较少的事务),让另一个事务成功执行。
如何排查死锁?
- 查看error日志。确认 innodb_print_all_deadlocks 配置是否为 ON(默认通常开启),可以直接在MySQL的错误日志文件中搜索 "deadlock" 关键字,查看历史死锁记录。
- 使用
SHOW ENGINE INNODB STATUS
命令,这是最直接、最常用的方法 重点看这部分内容:- TRANSACTION 1 和 TRANSACTION 2:两个事务的信息。
- WAITING FOR THIS LOCK:某个事务在等待什么锁(锁的类型、锁在哪一行或哪个索引上)。
- HOLDS THE LOCK:某个事务当前持有什么锁。
- 根据这些信息,就能清晰地还原死锁现场。
如何解决和避免死锁?
- 无需过多操作,InnoDB已自动回滚其中一个事务,另一个事务会继续执行
- 保持事务小巧并尽快提交:缩短事务执行时间,减少锁的持有时间。
- 注意加锁顺序
- 为查询创建合适的索引,避免升级表锁,大大增加死锁概率。