常见存储引擎
InnoDB
- 默认的存储引擎
- 支持事务、行锁、外键
MyISAM
- 不支持事务
- 插入和查询效率高
数据库设计三大范式
事务
特性 ACID
- 原子性Atomicity:一个事务中操作,要么全部成功,要么全部失败
- 一致性Consistency:应用系统从一个正确的状态到另一个正确的状态。即AID为了保证C
- 持久性Durability:事务一旦提交,不可回滚
- 隔离性Isolation:事务之间,相互隔离,互不干扰
隔离级别
概念解释
- 脏读 :一个事务处理过程里读取了另一个未提交的事务中的数据
- 不可重复读 :一个事务范围内多次查询一个数据,却返回了不同的数据值。由于其他事务修改了数据
- 幻读:如果执行了两次 SELECT,但第二次返回第一次未返回的行,则该行为“幻影”行。mysql 关于幻读的文档
READ UNCOMMITTED
- 读取其他事务未提交的执行结果
READ COMMITTED
- 事务可以读取另一个已提交的事务
- 解决脏读
REPEATABLE READ
- Mysql默认级别
- 在同一个事务里, select 的结果总是事务开始时时间点的状态
- 解决不可重复读问题
MySQL 在RR隔离级别下 解决幻读问题
MVCC 解决 快照读模式的幻读问题
Next-Key Lock 解决当前读模式的幻读问题
快照读
简单查询(不加锁的),都是快照读。
MVCC 会在事务第一次快照读时候生成read view,查询的是事务开始的时候的快照数据,所以就不存在不可重复读和幻读的问题。
MVCC 多版本并发控制
数据有多个版本,每次事务都会生成undo.log
InnoDB 会对每一条数据保存的事务id。
只在RC和RR隔离级别下存在。
- insert 数据 name = Jerry,age = 24;
- 事务1更新数据 name = tom;
- 事务2更新数据 age =30;
InnoDB只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。
当前读
lock in share mode(s锁)、for update(x锁)
这些需要加锁的查询就是当前读。
就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
Next-Key Lock
行锁 + 间隙锁的组合
- 行锁 Record Locks :下文的X锁和S锁。
-- 防止任何其他事务插入、更新或删除值为10的行。
SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;
- 间隙锁 Gap Locks :
-- 阻止其他事务将值15插入到列 t.c1中,无论列中是否已经有这样的值,
-- 因为范围中所有现有值之间的间隙都被锁定。
SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;
- Next-Key Lock
-- (-∞,15]
select * from t where id <= 15 for update;
SERIALIZABLE
- 所有读都会加共享锁,并发效率低
锁
行锁
- 共享锁(也称为 S 锁):允许事务读取一行数据
select * from tableName where... lock in share mode;
- 独占锁 (也称为 X 锁):允许事务删除或更新一行数据
select * from tableName where... for update;
表锁
为了实现多粒度的锁机制,InnoDB 还有两种内部使用的意向锁,由 InnoDB 自动添加,且都是表级别的锁
- 意向共享锁 (IS):事务即将给表中的各个行设置共享锁,事务给数据行加 S 锁前必须获得该表的 IS 锁
- 意向排他锁 (IX):事务即将给表中的各个行设置排他锁,事务给数据行加 X 锁前必须获得该表 IX 锁
锁之间关系
- 冲突:两种锁不能同时存在,需要等待锁释放
- 兼容:两种锁可以同时存在
锁类型 | X | IX | S | IS |
---|---|---|---|---|
X | 冲突 | 冲突 | 冲突 | 冲突 |
IX | 冲突 | 兼容 | 冲突 | 兼容 |
S | 冲突 | 冲突 | 兼容 | 兼容 |
IS | 冲突 | 兼容 | 兼容 | 兼容 |
InnoDB加锁
- 意向锁,为InnoDB引擎自动控制,用户不需要干预
- DML操作,自动给涉及的数据集加X锁
- 查询操作,默认不加任何锁 => 快照读
- 查询操作可以手动的显式加锁 => 当前读
索引
- 数据页:InnoDB存储的最小单位
- 回表:非主键索引根据获取到的主键值,再查找其他数据,图中红色部分
索引的本质
索引的本质是一种排好序的数据结构
B+树索引
- 只有叶子节点保存数据,非叶子节点只保存Key信息。这样可以大大降低B+树的高度
- B+ 树有利于全表扫描。只需要遍历所有叶子节点
- 每一个索引都会生成一个索引B+树
InnoDB索引类型
索引类型
- 主键索引:主键的索引
- 普通索引:最普通的索引
- 唯一索引:索引列的值必须唯一,但允许有空值
- 联合索引:如果是组合索引,则列值的组合必须唯一
- 全文索引:适合于模糊搜索
底层类型
- 主键索引,聚簇索引
- 叶子节点存储的是数据(数据页)
- 非主键索引,非聚簇索引
- 叶子节点存储的是索引字段和主键字段
- 查询时候可能会需要回表(仅查询索引列时,不需要回表)
InnoDB 索引
联合索引
ALTER TABLE mytable ADD INDEX name_city_age (name(10),city,age);
这个联合索引,其实会建出以下三个索引
- name,city,age
- name,city
- name
最左前缀匹配原则
mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配
where city= 'dalian' and age = 60
会让索引失效,缺失优先度最高的name
where name= 'dalian' and age = 60
会让索引失效,缺失中间字段city
where name= 'dalian' and city like '%lian' and age = 20
name
和city
会走索引,age
不会走索引
查询优化器
当对索引中所有列通过"=" 或 “IN” 进行精确匹配时,索引都可以被用到
- 所以 MySQL 不存在 where 子句的顺序问题而造成索引失效
where city= 'dalian' and name= 'dalian'
不会让索引失效,查询优化器会自动优化
注意事项
创建
- 尽量使用简单类型(数字之类的)
- 字段尽量为 NOT NULL
- 字符串索引尽量指定长度,不要对整个列进行索引
- 区分度越高越好,比如性别字段,区分度太低,不适合索引
- 索引太多会影响DML新能
使用
- LIKE 时要 userName%,不要全模糊
- 字符串要加
''
- 不要在列上计算,会让索引失效
- 尽量不要使用NOT IN、<>、!= 操作
- OR 条件
- 组合索引的使用要遵守
最左前缀
原则
编码
如果你在使用Mysql或者MariaDB,不要用utf-8,改用utf-8mb4
缓存
MySQL缓存机制简单的说就是缓存sql文本及查询结果,如果运行相同的SQL,服务器直接从缓存中取到结果,而不需要再去解析和执行SQL。如果表更改了,那么使用这个表的所有缓存查询将不再有效,查询缓存中值相关条目被清空。
- 查询必须是完全相同(逐字节相同)才能够被认为是相同的
- 更改指的是表中任何数据或是结构发生改变,包括INSERT、UPDATE、 DELETE、TRUNCATE、ALTER TABLE、DROP TABLE或DROP DATABASE等,也包括那些映射到改变了的表使用MERGE表的查询
- 对于一些不常改变数据且有大量相同SQL查询的表,查询缓存会节约很大的性能
- where条件中如包含任何一个不确定的函数将永远不会被cache, 比如current_date, now等
- 执行SQL里有触发器,自定义函数时,MySQL缓存也是不起作用的
参考文章
- https://developer.ibm.com/zh/technologies/databases/articles/os-mysql-transaction-isolation-levels-and-locks/
- https://juejin.cn/post/6844903666420285454
- https://juejin.cn/post/6931901822231642125
- https://blog.csdn.net/qq_39390545/article/details/108540362
- https://tech.meituan.com/2014/06/30/mysql-index.html
- https://juejin.cn/post/6844903885501530125