MySQL
MySQL的四种隔离级别
- 读未提交的(read uncommited):一个事务(session1)会读到另一个事务(session2)未提交的数据,如果session2进行事务回滚,会导致session1脏读
- 读已提交的(read commited):只能读到已提交的数据
- 可重复读(repeated read):一个事务第一次读取到的结果多次读都一样,不会受其他事务的影响,除非是在当前事务进行update
- 可传行化(serilizeble):采用表级共享锁,代价最高,事务都要按照顺序执行。
mysql默认事务是可重复读

并发事务带来的问题
- 脏读:一个事务正在做数据修改但是还没有提交却被别的事务读取到了
- 丢失修改:两个事务同时读取一条数据并且同时对该条数据做修改,后面的一个覆盖了前一个的修改
- 不可重复读:一个事务1在读取的时候,另一个事务2做了修改,导致事务1前后两个读取数据不一致
- 幻读:事务1在读取多条数据库数据时候,事务2进行插入或者删除,导致事务1发现了原本不存在的数据
表级锁/行级锁
表级锁
- 锁住整张表
- 开销小,加锁快
- 不会死锁
- 锁粒度大,发生锁冲突大,并发效率低
行级锁
- 锁住某条记录
- 开销大,加锁慢
- 会存在死锁
- 锁粒度小,发生锁冲突小,并发效率高
innoDB支持表锁和行锁,默认行锁
共享锁/排他锁
共享锁
- 读锁
- 对共享资源进行加锁,其他人可以读到该资源,但是不能修改该资源
- 语法:select * from tableName in share mode
- 多个共享锁可以共存,但是共享锁与排他锁不能共存
排他锁
- 写锁
- 对当前资源进行加锁,其他人不能对该资源进行读和写
- 语法:select * from tableName for update;
- 排他锁是独占,不与其他锁共存
乐观锁/悲观锁
乐观锁
- 乐观认为,并发的事情很难发生
- 乐观锁采用数据库增加version字段,写的时候对比version是否相等,相等的话修改的时候version进行自增,不相等认为发生并发,进行事务回滚
- 实现方式:版本号、CAS
悲观锁
- 悲观认为并发随时发生
- 无论每次进行数据的读写操作都要进行加锁,防止被其他人修改
- 实现方式:数据库的行锁、读锁、写锁
索引类型
- 主键索引
- 普通索引
- 空间索引
- 唯一索引
- 全文索引:搜索很长的文章可以用
数据结构
二叉树
- 二叉树、红黑树查询数据快,但插入数据时维持困难,树高度不可控,回旋查找;
- B树,查询快,根节点和叶子节点都有完整的数据,一个节点可以同时存储多个值,让树高变矮,减少磁盘IO次数,数据顺序存储,回旋查找;
- B+树,查询快,根节点只存储子节点指针和索引数据(查询性能稳定),一个节点也可以同时存储多个值,根节点才存储数据并且数据是顺序存储双向指针(利于范围查找);为什么快?利用了磁盘的预读和顺序读写,每次读取一个节点,磁盘不需要寻道时间只需要旋转时间;
hash表(哈希值是一个无序值,作为索引无法应对范围查找、顺序查找等问题)
- 不支持范围查找,分组,条件统计,排序等;
- 要有复杂的哈希算法,哈希冲突对大数据不友好;
- 吃内存;
B+ tree :B+树详解
引擎
InnoDB
- 支持外键
- 支持事务
- 锁支持行锁,并发度高
- 支持全文索引
- 支持查询缓存
- 聚族索引;
MyISAM
- 不支持事务
- 支持全文索引
- 不支持MVCC
- 支持标记所,不支持行级锁,并发差
- 非聚族索引;
Memory
- 表结构在磁盘,数据在内存,数据库重启和崩溃数据丢失
- 不支持事务,支持表锁
- 字段类型也少
索引优缺点
优点
- 提高数据检索效率,减少IO次数
- 加快分组和排序,降低CPU消耗
- 将随机IO变成顺序IO,以空间换时间
缺点
- 创建、维护索引要耗费时间、空间,索引不宜过多
重要概念
- 回表:从普通索引获取聚族索引id值,在根据id值去获取完成数据
- 覆盖索引:从索引的叶子结点就能获取到全量查询数据,或者索引出就能得到需要的查询列
- 最左匹配:根绝索引顺序判断是否走索引
- 索引下推:ICP优化本应在server层过滤的数据下沉到存储引擎层
索引失效场景
- 范围查询以后的所以字段失效 如:in ,not in
- 使用!= ,<, >, or,in ,not in,is null,is not null,like % 失效
- 索引上做函数运算或者类型转换
问题
哪些列需要索引?
- 主键、外键、区分度高的列
- 经常需要索索的列
- 查询中作为排序、分组的字段
哪些列不适合创建索引?
- 经常更新的列
- 区分度低的列
- 数据量少
事务(ACID)
- 原子性
- 一致性
- 隔离性
- 持久性
原子性:要么全部执行成功,要么全部失败,通过undo log保证
一致性:AID都是为了保证一致性
隔离性:通过MVCC保证
持久性:通过redo log保证
undo log
作用:保存了事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读;
内容:逻辑格式的日志,在执行undo的时候,仅仅是将数据从逻辑上恢复至事务之前的状态,而不是从物理页面上操作实现的,这一点是不同于redo log的
什么时候产生:事务开始之前,将当前数据版本生成undo log,undo 也会产生 redo 来保证undo log的可靠性
什么时候释放:当事务提交之后,undo log并不能立马被删除,而是放入待清理的链表,由purge线程判断是否由其他事务在使用undo段中表的上一个事务之前的版本信息,决定是否可以清理undo log的日志空间
redo log
作用:确保事务的持久性,防止在发生故障的时间点,尚有数据未写入磁盘,在重启mysql服务的时候,根据redo log进行重做,从而达到事务的持久性这一特性。
内容:物理格式的日志,记录的是物理数据页面的修改的信息,其redo log是顺序写入redo log file的物理文件中去的
什么时候产生:事务开始之后就产生redo log,redo log的落盘并不是随着事务的提交才写入的,而是在事务的执行过程中,便开始写入redo log文件中
什么时候释放:当对应事务的数据写入到磁盘之后,redo log的使命也就完成了,redo log占用的空间就可以重用
对应的物理文件:
- 默认情况下,对应的物理文件位于数据库的data目录下的ib_logfile1&ib_logfile2
- innodb_log_group_home_dir 指定日志文件组所在的路径,默认./ ,表示在数据库的数据目录下。
- innodb_log_files_in_group 指定重做日志文件组中文件的数量,默认2
- 关于文件的大小和数量,由一下两个参数配置
- innodb_log_file_size 重做日志文件的大小。
- innodb_mirrored_log_groups 指定了日志镜像文件组的数量,默认1
其他:
很重要一点,redo log是什么时候写盘的?前面说了是在事物开始之后逐步写盘的
之所以说重做日志是在事务开始之后逐步写入重做日志文件,而不一定是事务提交才写入重做日志缓存,原因就是,重做日志有一个缓存区Innodb_log_buffer,Innodb_log_buffer的默认大小为8M,Innodb存储引擎先将重做日志写入innodb_log_buffer中。
bin log
作用:
- 用于复制,在主从复制中,从库利用主库上的binlog进行重播,实现主从同步
- 用于数据库的基于时间点的还原
内容:
逻辑格式的日志,可以简单认为就是执行过的事务中的sql语句,但又不完全是sql语句这么简单,而是包括了执行的sql语句(增删改)反向的信息,也就意味着delete对应着delete本身和其反向的insert;update对应着update执行前后的版本的信息;insert对应着delete和insert本身的信息。
什么时候产生:
事务提交的时候,一次性将事务中的sql语句(一个事务可能对应多个sql语句)按照一定的格式记录到binlog中。这里与redo log很明显的差异就是redo log并不一定是在事务提交的时候刷新到磁盘,redo log是在事务开始之后就开始逐步写入磁盘。因此对于事务的提交,即便是较大的事务,提交(commit)都是很快的,但是在开启了bin_log的情况下,对于较大事务的提交,可能会变得比较慢一些。这是因为binlog是在事务提交的时候一次性写入的造成的;
什么时候释放:
binlog的默认是保持时间由参数expire_logs_days配置,也就是说对于非活动的日志文件,在生成时间超过expire_logs_days配置的天数之后,会被自动删除
PS:物理格式、逻辑格式:物理格式是对数据页变更的描述、逻辑日志更倾向于表抽象上的描述,类似sql记录这样的
数据库语法
正文到此结束