目录
1、MySQL是什么?
MySQL 是一种广泛使用的开源关系型数据库管理系统(RDBMS Relational Database Management System)。
2、MySQL 的默认存储引擎是什么?
MySQL 5.5 之前版本,默认存储引擎是 MyISAM。
MySQL 5.5 及之后版本,默认存储引擎是 InnoDB。
3、MySQL的索引是怎么实现的?
索引概念
索引在 MySQL 中类似于一本书的目录,它的主要目的是提高数据查询的速度。索引是一种数据结构,它存储了表中一列或多列的值以及这些值对应的行在表中的物理位置(通常是指向行数据的指针)。
索引结构
B-Tree多路平衡查找树,所有叶子节点在同一层,并且根节点到叶子节点的路径长度相同或相近,查找数据的复杂度通常为O(logn)。
B+Tree是B-Tree的优化,所有非叶子节点只存放关键字的副本,真正数据存放在叶子节点中,并且所有叶子节点之间通过链表相连接,更有利于范围查找。
Hash索引将列值映射为一个哈希码,查找数据根据哈希码来定位查找,在等值查询时间复杂度通常为O(1)。不支持范围查询,哈希的特性使它无法按照顺序遍历。
创建索引
创建表时创建索引,用 CREATE INDEX 索引名 ON 表名(表字段)来创建索引
已经建好表修改索引,用 ALTER TABLE 表名 ADD INDEX 索引名(索引字段)来添加索引
索引维护
使用 OPTIMIZE TABLE 语句来优化索引,减少内存碎片,内存碎片是索引在增删改产生的,InnoDB引擎会自动优化,数据量大频繁变更也可以手动执行。
使用 EXPLAIN 分析查询语句是否命中索引、命中哪个索引以及使用效率等信息,执行命令后会返回一个结果集,key判断是否命中以及效率问题;possible_keys查询中可能用到的索引;key实际使用的索引名称,如果是null则未使用索引。
4、聚簇索引与非聚簇索引
聚簇索引
聚簇索引是一种按照物理存储顺序来构建的索引结构,数据存储的顺序和索引的顺序是一致的,数据行本身就存储在对应的叶子节点上。InnoDB 引擎会默认根据主键自动创建聚簇索引,表中没有指定主键,InnoDB 引擎会自动选择一个唯一且非空的键作为聚簇索引,要是依然还没有,会隐式创建一个自增的主键作为聚簇索引。
特点:
数据存储和索引紧密结合,数组存储在叶子节点,通过聚簇索引查找数据时,能直接获取对应数据,通过聚簇索引范围查找一批数据可以快速定位到相应的物理存储位置并读取数据。
一个表只能有一个聚簇索引。
对插入顺序有一定影响,当按照聚簇索引列插入数据时,如果主键自增或有序的值,那么数据的插入相对比较规整。但如果时无序的值频繁插入,可能导致频繁的磁盘页分裂等情况,影响插入性能。
非聚簇索引
非聚簇索引也叫二级索引,它的索引结构与数据的物理存储顺序是分离的,非聚簇索引的叶子节点并不会存储实际数据行,而是存储索引列的值及对应主键值。查询根据非聚簇索引查询非存储列以外的数据需要回表。
特点:
可以创建多个非聚簇索引。
查询可能会涉及回表操作。
索引覆盖可优化查询。
5、磁盘页分裂问题
磁盘页分裂是指在基于B+树构建索引以及存储数据的过程中,当向已满的磁盘页插入新的数据记录时所发生的一种现象。
InnoDB 引擎中,数据是按照页为单位进行存储和管理的。B+树的叶子节点存放着数据记录,假设某个叶子节点所在的数据叶已经存满了数据记录,此时如果再有新的数据记录要插入这个页中,就没办法直接插入了。InnoDB会根据一定规则,将这个已满的数据页进行分裂操作,也就是把这个页中的部分数据记录挪到一个新创建的数据页中,同时B+树中的相关节点的指针等结构,以维持B+树索引结构的正确性和完整性,让数据依然能够按照既定的索引规则有序的被查找和访问。
6、回表查询
非聚簇索引它只存储索引列的值和相应指针,只有当查询的字段包含在非聚簇索引的列中,数据库就不需要回表。如果查询字段不在索引里,数据库就需要通过回表去获取完整的行数据。
7、MVCC
MVCC(Multi-Version Concurrency Control,多版本并发控制)是一种数据库管理技术,用于在多用户环境中有效管理并发访问,确保数据的一致性和隔离性。MVCC通过允许数据库在同一时刻存在多个版本的数据来避免锁的竞争,减少了数据库的阻塞和死锁问题,提升了并发性能。
MVCC的核心概念:
MVCC的核心思想是,通过对数据库中的数据进行版本化,使得每个事务能够看到数据库某一时刻的快照,而不需要直接阻塞其他事务。每当数据发生变化时,数据库会为该数据创建一个新的版本。不同的事务可以读取和修改各自独立版本的数据,而无需等待其他事务完成。
MVCC的工作原理:
事务隔离:每个事务都有自己独立的“视图”或快照,数据库中的每个数据项可以有多个版本。事务只能访问其事务开始时“可见”的数据版本。
数据版本管理:当数据被更新时,数据库不会直接修改旧数据,而是创建新的数据版本。每个数据版本会保存创建该版本的事务ID和过期的事务ID(即哪些事务可以访问该数据版本)。
可见性控制:MVCC使用事务的时间戳或事务ID来判断哪个版本的数据对当前事务可见。通常,较早开始的事务可以看到旧版本的数据,而较晚的事务只能看到新版本的数据。
MVCC的主要组件:
事务ID:每个事务都有一个唯一的ID,通常是由数据库自动分配的时间戳。
数据版本:每次更新数据时,数据库会创建一个新版本,并记录数据的创建时间(事务ID)和有效时间(例如过期时间或终止事务ID)。
事务快照:事务通过查看“快照”来判断哪些数据版本对其可见。快照通常是事务开始时的数据库状态。
MVCC的优势
提高并发性能:通过避免大部分的锁竞争,MVCC能够允许多个事务并发执行,减少了由于锁而导致的等待和死锁问题。
提高读性能:读取操作通常不需要加锁,因此能够高效执行,尤其是在读取操作远多于写入操作的场景下。
保证一致性:通过为每个事务提供一个一致的视图,MVCC能够避免脏读、不可重复读和幻读等问题,确保数据库的一致性和隔离性。
MVCC的缺点
磁盘空间开销:每次数据更新都会产生新版本,可能导致较多的旧版本数据存在,增加磁盘空间的需求。如果垃圾回收机制不及时清理过期版本,可能会导致数据库空间的浪费。
实现复杂度:MVCC机制需要管理多个数据版本的生命周期,增加了数据库管理的复杂性,尤其是在多版本数据的清理和回收方面。
8、WAL
WAL(Write-Ahead Logging,预写日志)是一种用于数据库系统中的日志记录机制,旨在确保事务的持久性和一致性。WAL的核心思想是,所有对数据库的修改操作必须先记录到日志文件中,然后才允许实际数据被修改。这种机制能够在发生系统崩溃或故障时,保证事务的原子性和持久性,从而实现数据库的恢复。
基本原理:
在执行数据库写操作时,首先将操作的细节(例如插入、更新、删除等)写入一个日志文件(通常称为WAL日志)。
日志写入是顺序的,不管数据写入是否成功,日志都会先写入磁盘。
数据页的实际修改(例如更新数据库表)只有在日志被成功写入后,才会应用到数据库文件中。
这样即使系统崩溃,事务的操作记录可以通过日志文件进行恢复,保证数据的完整性和一致性。
WAL的关键特性
预写日志:数据库在更新数据之前,先将修改操作记录到WAL日志中。这样做保证了在事务提交之前,数据库能保存完整的日志信息。
顺序写入:WAL日志是顺序写入的,这使得写入操作相对较快。顺序写入比随机写入磁盘更高效,减少了磁盘I/O操作的延迟。
持久性和恢复:在数据库崩溃的情况下,通过重放日志中的操作,可以恢复数据库到崩溃前的状态。因为日志记录了所有修改的数据操作,即使数据文件本身没有更新成功,也可以通过日志回滚或者重做恢复。
WAL的工作流程
事务开始:当一个事务开始时,数据库分配一个唯一的事务ID。
日志记录:事务进行的每一步操作,都会在WAL日志中记录。日志记录包含了数据修改的细节,比如数据页的改变、更新内容、修改时间戳等。
数据修改:在日志被写入后,数据库才会进行实际的数据修改。数据页的修改和日志记录是分开进行的,确保即使发生崩溃,日志已经保存下来,数据修改能够恢复。
事务提交:当事务提交时,系统确保日志中的所有操作都已经写入磁盘,并且确认数据页的实际修改完成。然后事务被标记为完成。
崩溃恢复:如果系统在事务提交前崩溃,数据库会通过重做日志中的操作来恢复事务。日志中的每个操作都会在恢复过程中被重新应用到数据库中。
WAL的优势
事务一致性和持久性:WAL保证了数据库即使在崩溃后,也能通过日志文件恢复未提交的事务或保证已提交事务的持久性。
高效的顺序写入:WAL通过顺序写入日志来减少磁盘I/O操作,提高了系统性能,特别是写操作频繁的场景。
并发性能:由于日志写入与实际数据更新分开,数据库可以通过更灵活的并发控制机制来处理多个事务,提高并发性能。
WAL的缺点
日志空间消耗:WAL需要维护大量的日志文件,这可能会占用大量磁盘空间,尤其是在长时间运行或有大量数据修改的情况下。
日志管理复杂性:随着时间的推移,日志文件可能会积累,需要定期清理和归档。否则可能会导致存储空间耗尽。
写入延迟:尽管WAL通过顺序写入减少了I/O延迟,但由于所有修改都必须先写入日志,这可能引入一定的写入延迟。
9、双重写入策略
InnoDB的双重写入策略(Doublewrite)是一个关键的机制,用于保证在磁盘写入过程中,即使出现崩溃或不完整写入的情况,也能确保数据的一致性和完整性。
双重写入的基本概念
InnoDB的双重写入策略涉及到两个阶段的写操作:
第一阶段:在磁盘上预写数据到一个特殊的区域(称为"Doublewrite Buffer")。
第二阶段:将数据从Doublewrite Buffer写到数据文件中。
具体而言,当InnoDB要写入数据页时,它不会直接将数据页写到数据库的实际数据文件(如.ibd
文件)。而是首先将数据页写入到一个专门的缓存区域,即Doublewrite Buffer。这时,数据页会被写入到一个位于ibdata1
文件的预定区域(或者对应的共享存储位置)。一旦这个过程完成,数据页才会被正式写入到数据库的实际数据文件中。
双重写入的工作流程
写入Doublewrite Buffer:
当InnoDB执行更新操作时,数据页首先被复制到Doublewrite Buffer。
这个缓存区的作用是为后续的磁盘写入提供一个备份,以防止磁盘写入过程中出现不完整的情况。
将数据写入数据文件:
一旦数据页已经成功写入Doublewrite Buffer,InnoDB将会把该数据页从Doublewrite Buffer写入到实际的数据库数据文件中。
如果在写入过程中发生崩溃(如系统断电、磁盘故障等),由于数据已经被预先写入Doublewrite Buffer,恢复时可以通过Doublewrite Buffer中的数据重新写入数据文件,避免了损坏。
崩溃恢复:
如果系统崩溃发生在数据页写入到数据库文件的过程中,InnoDB能够从Doublewrite Buffer中恢复出完整的数据页。
在数据库恢复时,InnoDB会检查数据库文件和Doublewrite Buffer的内容,确保数据的一致性,防止数据损坏。
双重写入的优势
数据完整性和一致性:双重写入确保了在写入数据文件时,如果发生崩溃或系统故障,InnoDB可以通过Doublewrite Buffer进行恢复,避免了部分写入的数据页损坏,保证了数据文件的一致性。
减少磁盘碎片:写入到Doublewrite Buffer是顺序的,可以减少磁盘上的随机写操作,优化磁盘I/O性能。
可靠性增强:在磁盘写入时,数据页不是直接写入数据文件,而是先通过Doublewrite Buffer“备份”,大大增加了系统崩溃后的数据恢复机会。
双重写入的缺点
性能开销:双重写入意味着数据需要被写入两次:一次是写入Doublewrite Buffer,另一次是写入数据文件。尽管Doublewrite Buffer的写入是顺序的,减少了I/O的随机性,但这一额外的写入操作仍然会带来一定的性能损耗,尤其是在磁盘I/O负载较高时。
空间开销:Doublewrite Buffer本身需要占用一定的磁盘空间。虽然这个空间开销相对较小,但在大规模数据操作时,仍然可能对存储造成一定的压力。
延迟增加:由于数据需要经过两次写入(先写入Doublewrite Buffer,再写入数据文件),这一过程会增加一定的写入延迟。在高并发环境下,这可能会影响数据库的响应时间和吞吐量。
如何配置和管理双重写入
InnoDB的双重写入策略是默认启用的,且通常不需要特别配置。不过,可以通过以下参数控制它:
-
innodb_doublewrite
:该参数可以控制是否启用双重写入。默认情况下是启用的(innodb_doublewrite=ON
)。如果关闭(innodb_doublewrite=OFF
),则InnoDB将不使用双重写入机制,这可能会降低数据的可靠性。 -
innodb_flush_log_at_trx_commit
:该参数控制事务日志的刷新策略,与双重写入机制协同工作。它可以影响事务的持久性和崩溃恢复过程。
双重写入与其他恢复机制的关系
WAL(Write-Ahead Logging):InnoDB采用的WAL(预写日志)机制记录了对数据库的所有修改,并确保事务的原子性和持久性。WAL和双重写入策略可以协同工作,进一步增强系统的可靠性。在崩溃恢复时,WAL可以帮助恢复未提交的事务,而双重写入则可以确保数据的一致性,防止部分写入的数据损坏。
日志刷写策略:双重写入机制和日志刷写策略(如innodb_flush_log_at_trx_commit
)共同决定了数据库系统的事务一致性、持久性以及恢复能力。它们通过不同的方式确保系统在崩溃后能够正确恢复。
10、CAS
CAS 通常是指 Compare-And-Swap(比较与交换)操作,是计算机科学和并发编程中的一种原子操作。它用于确保在并发环境中,多个线程/进程对共享资源的访问能够正确地进行同步,避免竞态条件和数据不一致。
CAS 的基本概念
CAS操作的核心思想是:
- 比较(Compare):首先,CAS操作检查目标位置(如内存中的一个值)是否为预期的值。
- 交换(Swap):如果目标值与预期值相等,则将其替换为新值;如果不相等,则什么也不做。
CAS操作保证了它是原子的,即该操作在执行时不会被中断,保证了数据的一致性。
CAS 操作的步骤
- 读取目标位置的当前值
V_current
。 - 比较
V_current
是否等于预期的值V_expected
。 - 如果相等,将目标位置的值设置为新值
V_new
。 - 如果不相等,CAS操作失败,并且目标位置的值不做任何更改。
CAS 的原子性
CAS 操作通过硬件支持来确保原子性,这意味着它在并发环境中不会被中断或干扰。大多数现代处理器(例如 x86、ARM 等)都提供了对 CAS 操作的支持,通常作为一条指令(如 CMPXCHG
指令),因此它被认为是一种非常高效的同步机制。
CAS 的应用场景
CAS 操作广泛用于并发编程中的锁-free(无锁)算法和数据结构中,常见的应用场景包括:
- 无锁队列:通过 CAS 来保证对队列的并发操作不会发生冲突。
- 无锁栈:使用 CAS 确保栈操作的原子性。
- 计数器、标志位:使用 CAS 操作对共享的计数器或状态标志进行修改。
- 实现高效的同步原语:比如原子变量、并发队列等。
CAS 的优缺点
优点:
- 高效:由于 CAS 操作是原子的,通常无需加锁,因此它比传统的锁机制(如互斥锁、信号量等)更高效,尤其是在多线程环境中,避免了线程间的竞争。
- 减少上下文切换:CAS 不涉及线程阻塞和唤醒,避免了因锁竞争引起的上下文切换,从而提高了程序的吞吐量。
缺点:
- ABA 问题:如果某个位置的值在读取后发生了变化,但最终又恢复成了原来的值(即从
A
变为B
,然后变回A
),CAS 可能会错误地认为值没有发生变化。这种问题被称为“ABA 问题”。为了解决这个问题,可以使用带版本号的 CAS(例如,增加版本号来标记值的修改)。 - 可能导致自旋:CAS 操作在发生失败时通常会重复尝试,这可能导致在高竞争情况下发生自旋,浪费 CPU 时间。
- 难度较大:实现基于 CAS 的无锁数据结构可能相对复杂,尤其是处理竞争和同步问题时,需要小心设计。