原创

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记录这样的

数据库语法

正文到此结束