三、Mysql
1.事务的特征ACID和保证
原子性:事务内的操作统一成功或者失败
由undolog日志保证,他记录了需要回滚的日志信息,回滚时撤销已执行的sql
一致性:事务执行前后数据总量不变
由其他三大特性共同保证,是事务的目的
隔离性:事务之间互不影响
由MVCC保证
持久性:事务一旦提交就不可逆
由redolog日志和内存保证,mysql修改数据时内存和redolog会记录操作,宕机时可恢复
3.事务的隔离级别
高并发情况下,并发事务会发生脏读、不可重复读、幻读问题,这时需要用隔离界别来控制
读未提交:允许事务读取另一个事务未提交的数据,有脏读问题(未提交的数据回滚导致读取的数据无效)
读已提交:只允许事务读取到其他事务已提交的数据,有不可重复读问题(多次读取结果不一致)
可重复读:多次读取结果一致,有幻读问题(b事务对a事务操作的数据做了修改,并且b事务先提交,a感觉自己的操作不起作用,十分魔幻)
可串行化:所有事务依次执行,没有并发问题
Inno DB 默认隔离级别为可重复读级别,分为快照度和当前读,并且通过间隙锁解决了幻读问题
5.MVCC原理
MVCC多版本并发控制,为每次事务生成一个新版本数据,每个事务都有自己的版本,从而不加锁拒绝读写冲突,这种读叫做快照读,只在读已提交和可重复读中生效,实现原理由四个东西保证,分别是
undolog日志:记录了数据历史版本
redaView:事务进行快照读生成的视图,记录了当前系统中活跃的事务id,控制哪个历史版本对当前事务可见
隐藏字段DB_TRC_ID:最近修改记录的事务id
隐藏字段DB_Roll_PTR:回滚指针,配合undolog指向数据的上一个版本
6. 索引分类与数据结构
- 数据结构分类:B+树,Hash索引,Full-text索引
- 物理存储分类:聚集索引、非聚集索引
- 字段特性分类:主键索引(PRIMARY KEY)、唯一索引(UNIQUE)、普通索引(INDEX)、全文索引(FULLTEXT)
- 字段个数分类:单列索引、联合索引(也叫复合索引、组合索引)
如果一个表没有主键索引依旧会创建B+树
在InnoDB中,会为每一张表创建一个主键索引,如果没有明确的主键索引,会使用一个隐藏(ROW ID)的、自动生成的主键来创建索引。建议每个表都添加主键索引。
HASH索引
在InnoDB中不支持mysql,即使选择了hash索引,但是使用的依旧是B+,InnoDB只支持自适应的HASH索引,手动选择无效。
memory支持hash索引,存在hash冲突,使用链表解决
聚簇索引和非聚簇索引
聚集索引将索引和数据放在一起,非聚集索引分离开存储,所以需要二次查找。
在MySQL中二级索引使用非聚集索引
,除了主键索引都是二级索引。二级索引叶子节点存储主键id,根据id再次通过主键索引查找,这叫回表
覆盖索引
查询的字段都在索引列中叫覆盖索引。
索引下推
二级索引中
的优化手段,在范围查询中
减少回表次数,没有索引下推的时候,一个范围查询使用二级索引,第一次查询了一个范围的主键,假设查询到10条数据,那么需要回表十次,如果使用索引下推,那么只需要回表一次,就可以拿到十条数据。
单列索引
索引只有一个字段,即使是中文,构成的B+树也是有序的
联合索引
构成索引的是多个字段。如何保证数据有序?
假设有三个字段 name、age、id
那就按照name、age、id的先后顺序排序,最后这些字段都在B+树的叶子节点上。
最左前缀原则
当我创建n个字段的联合索引时,若想使用索引查询,查询条件必须使用第一个字段,因为联合索引是按照字段顺序排序的,没有第一个字段就是乱序。
创建A B C的联合索引,AB、AC、ABC都是可以走联合索引的,但是BC不可以
切记合理使用单列索引和联合索引,不可盲目添加索引
索引优缺点
优点:提高检索效率;减低排序成本,默认asc。
缺点:创建和维护索引需要时间;占用物理空间;降低表的增删改效率。
7.聚簇索引和非聚簇索引的区别
聚簇索引:聚簇索引的叶子节点存放的是主键值和数据行;辅助索引(在聚簇索引上创建的其它索引)的叶子节点存放的是主键值或指向数据行的指针。
优点:根据索引可以直接获取值,所以他获取数据更快;对于主键的排序查找和范围查找效率更高;
缺点:如果主键值很大的话,辅助索引也会变得很大;如果用uuid作为主键,数据存储会很稀疏;修改主键或乱序插入会让数据行移动导致页分裂;所以一般我们定义主键时尽量让主键值小,并且定义为自增和不可修改。
非聚簇索引(辅助索引):叶子节点存放的是数据行地址,先根据索引找到数据地址,再根据地址去找数据,需要回表二次查找
他们都是b+数结构
8.MySQL如何做sql优化
可以查看执行计划分析数据的扫描类型、索引是否生效,常见的慢优化有:
- 减少select的数据列,使用覆盖索引
- order by使用索引排序,否则的话会回表查询
- group by同样使用索引,避免使用临时表
- 分页查询如果limit后面的数字很大,使用子查询查出主键,再limit后主键就能走覆盖索引
- 使用复杂查询的时候,使用关联查询来代替子查询,最好使用内连接
- 使用count函数的时候count(*)效率最高,count(字段)不会统计null
- update的时候,where要加索引,否则会从行锁升级到表锁
- 表中数据是否太大,是不是要分库分表
9.为什么要用内连接而不用外连接?
用外连接的话连接顺序是固定死的,比如left join,他必须先对左表进行全表扫描,然后一条条到右表去匹配;而内连接的话mysql会自己根据查询优化器去判断用哪个表做驱动。
子查询的话同样也会对驱动表进行全表扫描,所以尽量用小表做驱动表。
10.MySQL整个查询的过程
(1)客户端向 MySQL 服务器发送一条查询请求
(2)服务器首先检查查询缓存,如果命中缓存,则返回存储在缓存中的结果。否则进入下一阶段
(3)服务器进行 SQL 解析、预处理、再由优化器生成对应的执行计划
(4)MySQL 根据执行计划,调用存储引擎的 API 来执行查询
(5)将结果返回给客户端,同时缓存查询结果
注意:只有在8.0之前才有查询缓存,8.0之后查询缓存被去掉了
11.B和B+数的区别,为什么使用B+数
二叉树:索引字段有序,极端情况会变成链表形式
AVL数:树的高度不可控
B数:控制了树的高度,但是索引值和data都分布在每个具体的节点当中,若要进行范围查询,要进行多次回溯,IO开销大
B+树:非叶子节点只存储索引值,叶子节点再存储索引+具体数据,从小到大用链表连接在一起,范围查询可直接遍历不需要回溯
12.MySQL有哪些锁
基于粒度:
表级锁:对整张表加锁,粒度大并发小
行级锁:对行加锁,粒度小并发大
间隙锁:间隙锁,锁住表的一个区间,间隙锁之间不会冲突只在可重复读下才生效,解决了幻读
基于属性:
共享锁:又称读锁,一个事务为表加了读锁,其它事务只能加读锁,不能加写锁
排他锁:又称写锁,一个事务加写锁之后,其他事务不能再加任何锁,避免脏读
13. redo log 重做日志
保证事务的持久性,Innodb才有的日志
直接刷入磁盘有两个问题:Innodb以页为单位,每个事务可能只修改几个字节,浪费资源;一个事务可能涉及多个页,物理上不连续,随机IO性能太差
redo log写入机制
redo log包括两个部分:一个是内存中的日志缓冲( redo log buffer ),另一个是磁盘上的日志文件( redo logfile)。
先写入buffer中,通过innodb_flush_log_at_trx_commit
字段的规则写入磁盘
0 每次事务提交都只把redo log写入buffer
1 每次提交持久化到磁盘
2 每次提交只写入到page cache
14. bin log 归档日志
记录数据的写入性操作,二进制的形势保存到磁盘,
使用场景
主从复制:通过bin log实现数据一致性
数据恢复:使用mysql binlog恢复数据
bin log输盘时机
sync_binlog
参数控制刷盘时机,范围是0-N
0 系统判断何时刷入
1 每次commit都写入磁盘
2 每N个事务写入磁盘
15. undo log日志
保证事务原子性
记录数据的逻辑变化,例如insert对应一条delete,发生错误的时候可以回滚,是实现MVCC的关键
16. 索引失效的场景
like左%、or关键字、字段转换、字段计算、使用函数、字段比较、order by
17. 两阶段提交
首先再事务中,redolog会一边执行一边写入,binlog只有再事务提交之后才会写入,导致两份日志逻辑不一致,这个时候有其他写操作就会抛出异常。为了解决这个问题,InnoDB使用两阶段提交。
在写入redolog的时候也就是prepare阶段,可以写入redolog日志,当事务提交之后再写入binlog,写完之后redolog设置为commit阶段,这个时候其他线程来写,抛出异常也不影响。