前言

作为后端猿的我们,不出意外每天都会和mysql打交道。除了每天写不腻的CURD语句,关于mysql我们应该要了解它的那些基础知识呢?

直入主题

简单画出mysql的逻辑架构图?

1
2
3
4
5
6
7
8
9
10
11
       接 入 层
-----------------------
⬆ ⬆
⬇ ⬇
缓 存 解 析 器


优 化 器
-----------------------

引 擎

mysql常见的引擎有哪些,区别是什么?

  • InnoDB
    • 支持事务
    • 行锁
    • 聚簇索引
      • 引申问题:什么是聚簇索引?索引存放实际数据
    • 辅助索引(二级索引)索引存放的是主键
  • MyISAM
    • 不支持事务
    • 表锁
    • 崩溃无法安全恢复
    • 非聚簇索引
      • 引申问题:什么是非聚簇索引?索引存放实际数据的地址
    • 辅助索引(二级索引)索引存放的还是实际数据的地址
  • Memory
    • 基于内存
    • 表锁
    • 字段长度固定,不支持blob,text, 即使指定vachar实际储存也会转为char
  • Archive
    • 只支持insert/select操作
    • 适合日志等

什么是事务的ACDI概念?

  • A: Atomicity, 原子性, 一个事务的所有操作视为一个整体,要不全部成功,要不全部失败。
  • C: Consistency, 一致性, 一个事务下的所有的数据状态变更,只有事务提交成功才全部变更。
  • D: Durability, 持久性, 一旦事务提交成功,产生的数据变更将永久保存在数据库中。
  • I: Isolation, 隔离性, 一个事务在提交之前对其他事务不可见。

什么是脏读,幻读,不可重复读?

  • 脏读:读取未提交事务的数据,数据可能被回滚,不符合隔离性的定义。
  • 幻读:一个事务批量读取了一批数据时,另一个事务提交了新的数据,当之前的事务再次读取时,会产生幻影行。
  • 不可重复读:执行两次相同的查询,可能得到不同的结果。

mysql事务的隔离级别有哪些?默认的隔离级别是什么?

  • 未提交读:一个事务还未提交,另一个事务就可以读取,这样导致的后果,脏读。
  • 提交读(又叫,不可重复读):一个事务未提交对其他事务不可见,但是会产生幻读和不可重复读。
  • 可重复读(mysql默认隔离级别):保证同一个事务下多次读取的结果一致,但是会产生幻读。
  • 可串行化:严格的串行阻塞,并发能力不好。
隔离级别 脏读 不可重复读 幻读
Read Uncommitted
Read Committed ×
Repeatable Read × ×
Serializable × × ×

什么是MVCC?简述MVCC的作用及原理?

MVCC:Multi Version Concurrency Control, 多版本并发控制,mysql防止幻读的一种技术手段。每行数据存在间隙行,间隙行存放该行数据的创建时间删除时间,这里的时间实际是事务的版本号。当,

  • select数据时:只查询创建时间小于等于当前事务版本号 -> 当前事务或当前事务之前插入的行,删除时间大于当前版本号的行 -> 当前事务版本前未被删除的行。
  • update数据时:在原有行a的基础上复制行a’,行a的删除时间设置为当前的事务版本号,行a’的创建时间设置为当前的事务版本号。
  • insert数据时:记录创建时间为当前事务版本号。
  • delete数据时:记录删除时间为当前事务版本号。

死锁是怎么出现的,并写出简单示例?

死锁产生的原因是两个事务互相等待对方释放,产生了循环依赖,mysql采用了死锁检测(检测到循环依赖返回错误)和死锁超时(超时回滚持有行锁最少的事务)的方式尽可能去避免死锁。例如:

1
2
3
4
5
6
7
行锁:

UPDATE `table_demo` SET `a` = 'test' WHERE `b` = 'lalala';
UPDATE `table_demo` SET `b` = 'test' WHERE `a` = 'lalala';

UPDATE `table_demo` SET `b` = 'test' WHERE `a` = 'lalala';
UPDATE `table_demo` SET `a` = 'test' WHERE `b` = 'lalala';

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

  • 聚簇索引:InnoDB, B+树的叶子节点存放实际数据
  • 非聚簇索引:MyISAM, B+树的叶子节点存放实际数据的地址

什么是覆盖索引?

覆盖索引:要查询的行被索引覆盖,从索引中可以直接读取,不需要回表查询。例如:

1
2
3
4
5
6
7
CREATE TABLE `demo_table`(
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`username` char(32) NOT NULL DEFAULT '' COMMENT '用户名',
`password` char(32) NOT NULL DEFAULT '' COMMENT '密码',
PRIMARY KEY (`id`),
KEY `idx_username` (`username`)
)ENGINE=InnoDB DEFAULT CHARSET=UTF8;
1
2
3
4
5
6
explain select `username` from `demo_table` where `username` = 'demo';
+----+-------------+------------+------+---------------+--------------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+------+---------------+--------------+---------+-------+------+--------------------------+
| 1 | SIMPLE | demo_table | ref | idx_username | idx_username | 96 | const | 1 | Using where; Using index |
+----+-------------+------------+------+---------------+--------------+---------+-------+------+--------------------------+

Extra里的Using index就是使用了覆盖索引的意思。

什么是索引的最左前缀匹配原则?

InnoDB为什么不采用红黑树而采用B+树作为索引存放数据结构,并简要画出B+树?(目前我个人的理解比较浅欢迎积极纠正~)

红黑树本质是二叉树,每个节点最多拥有两个子节点,所以红黑树的深度较深,而B树每个节点最多可以有n个子节点,假如每个节点拥有100个子节点,那百万级的数据我们基本上只需要深度是3就可以存下 => 100^3,这样就减少的io次数(一个节点的大小通常为磁盘一个页的大小)。又有“局部性原理”(一旦一个数据被查询,那么它附近的数据可能也会需要被查询),其次B+树的叶子节点构成一个链表,这样我们就可以很容易的查询出一段范围的数据,其次B+树的根节点和内部节点只存放该索引下一个子节点的位置的指针,数据只存放在叶子节点里,这样非叶子节点就可以有更多的空间存放索引的位置,索引的范围就可以尽可能的大,从而树的深度就可能的小。

结语

后续持续修正和补充,如果有什么写的不对的地方欢迎大家积极指正,谢谢大家。