MySQL基础
SQL名词
- DQL(Data Query Language):数据查询语言,指Select语句,通常会归类到DML中
- DDL(Data Definition Language):数据定义语言,通常指Create、Drop、Alter等对表操作的语句
- DML(Data Manipulation Language):数据操作语言,通常指Insert、Delete、Update等对数据操作的语句
- DCL(Data Control Language):数据控制语言,通常指Grant、Revoke等对数据库权限控制的语句
数据库三范式
- 原子性:数据不可再分
- 唯一性:所有数据都要和数据表中的键有完全依赖关系
- 冗余性:非键属性之间是完全无关的
数据完整性
实体完整性:每个实体都是独一无二的
- 主键(primary key)
- 唯一约束
- 唯一索引(unique)
引用完整性(参照完整性):关系中不允许引用不存在的实体
- 外键(foreign key)
域完整性:数据是有效的
- 数据类型及长度
- 非空约束(not null)
- 默认值约束(default)
- 检查约束(check)
- 说明:在MySQL数据库中,检查约束并不起作用。
数据一致性
实现原理:事务
数据库相关的命令
普通命令
SHOW ENGINES;
- 查看数据库支持的数据引擎begin/start transaction
- 开启事务commit/rollback
- 提交/回滚explain sql语句
- 查看执行计划savepoint 保存点名称
- 在事务中添加保存点rollback [work] to [savepoint] 保存点名称
- 事务中回滚到保存点release savepoint 保存点名称
- 删除保存点
变量命令
注意:变量的查看可以通过show variables like '%变量名%'
,也可以通过select @@变量名
,非特殊情况不做说明
show global variables;
- 查看所有全局变量show variables;
- 查看局部变量show variables like '%storage_engine%';
- 查看数据库的默认引擎,此处storage_engine不是完整的变量名show variables like '%datadir%';
- 查看当前数据保存位置show variables like "innodb_page_size";
- 查看innodb一个数据页的大小set session autocommit = on/off
- 设定事务是否自动开启select @@tx_isolation;
- 查看数据库的事务隔离级别(8.0.3之前版本)select @@transaction_isolation;
- 查看数据库的事务隔离级别(8.0.3之后版本)set session transaction isolation level 事务隔离名称
- 设置事务隔离级别
MySQL数据引擎
特性 | InnoDB | MRG_MYISAM | MEMORY | MyISAM |
---|---|---|---|---|
存储限制 | 有 | 没有 | 有 | 有 |
事务、集群索引、外键 | 支持 | |||
锁机制 | 行锁 | 表锁 | 表锁 | 表锁 |
B树索引、索引缓存 | 支持 | 支持 | 支持 | 支持 |
哈希索引 | 支持 | |||
全文检索 | 支持(5.6+) | 支持 | ||
数据缓存 | 支持 | 支持 | ||
数据可压缩 | 支持 | |||
内存使用 | 高 | 低 | 中 | 低 |
存储空间使用 | 高 | 低 | 低 | |
批量插入性能 | 低 | 高 | 高 | 高 |
MySQL从5.5开始默认使用InnoDB,InnoDB也是唯一一个支持外键、事务、行锁的数据引擎。
InnoDB 简介
支持事务安全表(ACID)
- A(Atomicity):原子性;事务,要么全成功,要么全失败
- C(Consistency):一致性;事务前后数据的完整性必须保持一致。
- I(Isolation):隔离性;多个事务之间相互隔离
- D(Durability):持久性;持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的
事务特性的实现
- 隔离性:读写锁 + MVCC
事务隔离级别:
- 无事务隔离级别:会出现第一类丢失更新、脏读、不可重复读、第二类丢失更新和幻读
- Read uncommitter(未提交读):会出现脏读、不可重复读、第二类丢失更新、幻读,解决了第一类丢失更新
- Read Committer(提交读):会出现不可重复读、第二类丢失更新、幻读,解决了脏读和第一类丢失更新
- Repeatable Read(可重复读):会出现幻读,解决了脏读、不可重复读和第二类丢失更新,MySQL默认的级别,InnoDB引擎下该级别也解决了幻读
- Serializable(串行化):最安全,速度最慢,所有问题都解决了
备注:
- 第一类丢失更新:撤销一个事务的时候,把其他事务已提交的更新覆盖了
- 脏读:一个事务修改了数据未提交,另一个事务读到了修改后的数据
- 不可重复读:第一个事务读取了数据后,第二个事务修改了该数据,第一个事务再次读该数据就会读到和第一次不同是数据
- 第二类丢失更新:一个事务提交更新的时候,将其他事务已提交的事务覆盖了,是不可重复读的特例
- 幻读:事务一操作了数据库的多条记录,事务二向数据库中插入一条满足事务一操作的数据。事务一中就会发现这条满足条件的数据。像是出现幻觉一样
第一类丢失更新 | 脏读 | 第二类丢失更新 | 不可重复读 | 幻读 | |
---|---|---|---|---|---|
Read uncommitted | ✅ | ❌ | ❌ | ❌ | ❌ |
Read Committed | ✅ | ✅ | ❌ | ❌ | ❌ |
Repeatable Read | ✅ | ✅ | ✅ | ✅ | ❌ |
Serializable | ✅ | ✅ | ✅ | ✅ | ✅ |
索引
MySQL中,所有数据类型的列都可以被索引,常用的存储引擎InnoDB和MyISAM能支持每个表创建16个索引。InnoDB和MyISAM使用的索引其底层算法是B-tree(B树),B-tree是一种自平衡的树,类似于平衡二叉排序树,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除的操作都在对数时间内完成。
B-tree:是一颗多路平衡查找树;
- 每个节点最多有m-1个关键字。
- 根节点最少可以只有1个关键字。
- 非根节点至少有m/2个关键字。
- 每个节点中的关键字都按照从小到大的顺序排列,每个关键字的左子树中的所有关键字都小于它,而右子树中的所有关键字都大于它。
- 所有叶子节点都位于同一层,或者说根节点到每个叶子节点的长度都相同。
- 每个节点都存有索引和数据,也就是对应的key和value。
B+tree:
- 根节点至少一个元素。
- 非根节点元素范围:m/2 <= k <= m-1。
- B+树有两种类型的节点:内部结点(也称索引结点)和叶子结点。内部节点就是非叶子节点,内部节点不存储数据,只存储索引,数据都存储在叶子节点。
- 内部结点中的key都按照从小到大的顺序排列,对于内部结点中的一个key,左树中的所有key都小于它,右子树中的key都大于等于它。叶子结点中的记录也按照key的大小排列。
- 每个叶子结点都存有相邻叶子结点的指针,叶子结点本身依关键字的大小自小而大顺序链接。
- 父节点存有右孩子的第一个元素的索引。
MVCC
MVCC:Multi Version Concurrency Control(多版本并发控制)
MVCC只在 Read Committed 和 Repeatable Read两个隔离级别下工作。Read Uncommitted总是读取最新的记录行,Serializable 会对所有读取的记录行都加锁。
MCVV 的实现:隐藏字段、Read View、Undo log
隐藏字段
- DB_TRX_ID(6字节):表示最近一次对本记录行作修改(insert|update)的事务ID。至于delete操作,InnoDB认为是一个update操作,不过会更新一个另外的删除位,将行表示为deleted。并非真正删除。
- DB_ROLL_PTR(7字节):回滚指针,指向当前记录行的undo log信息
- DB_ROW_ID(6字节):随着新行插入而单调递增的行ID。理解:当表没有主键或唯一非空索引时,innodb就会使用这个行ID自动产生聚簇索引。如果表有主键或唯一非空索引,聚簇索引就不会包含这个行ID了。这个DB_ROW_ID跟MVCC关系不大。
官方文档:15.3 InnoDB Multi-Versioning
Read View(读视图)结构
和快照、snapshot是类似的概念。主要是用来做可见性判断的, 里面保存了“对本事务不可见的其他活跃事务”。
Read View的定义源码,其中包括主要的几个变量
- low_limit_id:目前出现过的最大的事务ID+1,即下一个将被分配的事务ID。
- up_limit_id:活跃事务列表trx_ids中最小的事务ID,如果trx_ids为空,则up_limit_id 为 low_limit_id。
- trx_ids:Read View创建时其他未提交的活跃事务ID列表。意思就是创建Read View时,将当前未提交事务ID记录下来,后续即使它们修改了记录行的值,对于当前事务也是不可见的。Read View中trx_ids的活跃事务,不包括当前事务自己和已提交的事务(正在内存中)
- creator_trx_id:当前创建事务的ID,是一个递增的编号。(这个编号并不是DB_ROW_ID)
Undo log
Undo log中存储的是老版本数据,当一个事务需要读取记录行时,如果当前记录行不可见,可以顺着undo log链找到满足其可见性条件的记录行版本。
大多数对数据的变更操作包括 insert/update/delete,在InnoDB里,undo log分为如下两类:
- insert undo log : 事务对insert新记录时产生的undo log, 只在事务回滚时需要, 并且在事务提交后就可以立即丢弃。
- update undo log : 事务对记录进行delete和update操作时产生的undo log,不仅在事务回滚时需要,快照读也需要,只有当数据库所使用的快照中不涉及该日志记录,对应的回滚日志才会被purge线程删除。
Purge线程:为了实现InnoDB的MVCC机制,更新或者删除操作都只是设置一下旧记录的deleted_bit,并不真正将旧记录删除。
为了节省磁盘空间,InnoDB有专门的purge线程来清理deleted_bit为true的记录。purge线程自己也维护了一个read view,如果某个记录的deleted_bit为true,并且DB_TRX_ID相对于purge线程的read view可见,那么这条记录一定是可以被安全清除的。
锁
普通select
1.读未提交,读提交,可重复读 不加锁,快照读
2.串行化,普通select会升级为select … in share mode
加锁select
select … for update
select … in share mode
1.唯一索引,唯一条件 记录锁(Record Locks)
2.其他的查询条件和索引条件 间隙锁(Gap Locks)与临键锁(Next-Key Locks)
update与delete
1.唯一索引,唯一条件 记录锁(Record Locks)
2.其他, 排他临键锁(Next-Key Locks)
insert
排它锁、同时,会在插入区间加插入意向锁(insert intention lock)。