mysql基础架构
    mysql5.5.5版本以后用InnoDB作为存储引擎
    sql语句执行过程:客户端连接mysql经过连接器,执行select语句查询缓存(k-v形式,k=sql语句 v=查询结果。mysql8以后没有了查询缓存),缓存中没有则到分析器,进行词法解析(解析表名,字段名等,判断语句是否满足语法),然后到优化器(优化器决定使用那个索引,多表联查决定表的连接顺序),执行器(权限验证,执行sql)
    image.png
    mysql日志
    WAL:先写日志,在写磁盘。
    redo log:
    当有一条记录需要更新时,InnoDB会先把把记录(在某个数据页上做了什么修改)写到redo log里,并更新内存,然后在适当(系统空闲时)的时候把这个更新操作更新到磁盘中。
    InnoDB的大小是固定的,可以配置成4个文件为一组,呈环状,当第三组也写满的时候,会将第一组更新清理位置出来。
    redo log可以保证数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe。
    image.png
    bin log:
    binlog是Server层实现的,记录语句的原始逻辑(给 ID=2 这一行的 c 字段加 1 ),大小不是固定的,是可以“追加写”的不会覆盖以前的日志,bin log用于恢复历史数据。
    两阶段提交:

    执行 update T set c=c+1 where ID=2;
    image.png

    写入redo log的时候会挂起事务,在写入bin log后在提交事务,就是MySQL的两阶段提交。

    change buffer
    执行一条更新语句,要更新的数据没有在当前内存数据页中会先存到change buffer中,然后在放入redo log中。在查询时如果在change buffer中的话会直接从change buffer中取出,以减少读磁盘的次数。
    redo log 主要节省的是随机写磁盘的 IO 消耗(转成顺序写),而 change buffer 主要节省的则是随机读磁盘的 IO 消耗。

    MySQL事务
    事务的四大特性:
    原子性:
    一致性:
    隔离性:
    持久性:
    事务出现的问题:
    脏读,不可重复读,
    幻读:一个事务内执行多次同一条查询语句,后面的查询语句查到了前面的语句没有查到的数据,就是幻读,在可重复读的级别下,普通的查询是不会出现幻读,只有“当前读”才会出现幻读。幻读专指读到新插入行。
    如何解决幻读:
    幻读产生的原因是,行锁只能锁住行,但是新插入记录这个动作更新的是记录之间的间隙,为了解决幻读,innodb引入间隙锁 再给数据上锁时,也会给要锁的数据之间的间隙上锁,以在锁期间无法插入数据。 间隙锁的引入,可能会导致同样的语句锁住了更大的范围,影响了并发度。
    事务的隔离级别:
    读提交:一个事务提交之后,它的变更才会被其他事务看到。
    读未提交:一个事务还未提交,其他事务就可以看到它做的变更。
    可重复读:在事务启动时会创建一个视图,事务执行过程中看到的数据只会是和视图中一样的。
    串形化:顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行

    事务隔离的实现:
    在Mysql中,每条记录在更新的时候会记录一条回滚操作,记录上的最新值,通过回滚操作,都可以得到前一个状态的值,也就是MVCC多版本并发控制,不同时刻启动的事务会有不同的视图
    image.png
    快照:在可重复读的级别下,在事务启动时就拍了快照,在事务开始时会生成一个数组,用来保存当前正在活跃的事务id,当前系统已经提交过的事务id,组成了事务的一致性视图。
    InnoDB 利用了“所有数据都有多个版本”的这个特性,实现了“秒级创建快照”的能力。
    image.png
    索引
    InnoDB的索引模型为B+树

    主键索引和非主键索引
    Mysql进行查询时,会为索引生成索引树,主键索引的索引树 K为主键的值 V为一行的数据,非主键索引的索引树中 K为本索引的值 V为主键索引的值,查询命中非主键索引时,会先查询非主键索引树,找到对应的主键,在去查询主键索引树从而获取到数据,这个过程称为回表。
    避免回表,可以利用索引覆盖 ,联合索引,select后面跟着的字段已经在索引树中,不需要去通过非主键索引树的结果查询主键索引树,就是索引覆盖;将高频查询的字段设置为联合索引,可以避免回表,MySQL中的索引下推优化也会减少回表操作。
    页分裂
    如果新插入的值在数据页内(非尾部),根据B+树的算法会申请一个新的页,然后挪动部分数据过去,这个过程叫页分裂,采用无序的值作为主键会产生页分裂。
    最左前缀
    索引项是按照索引定义的字段顺序排序的,索引内的的定义也是按照最左前缀规则的,使用LIKE时”%X”是不会命中索引的。
    索引下推
    MySQL会对索引进行索引下推优化,会在遍历过程中,对索引包含的字段先做判断,直接过滤调不满足条件的记录,减少回表次数。
    索引失效 在使用sql函数时,可能会影响数据的顺序性从而导致索引失效。当优化器通过抽样认为不回表会更节省资源的时候也会不走索引。模糊查询 %XX 会遵守最左前缀原则也会导致索引失效。 使用!= 不会走索引,or前后同时用索引会走索引,如果不是同时不会走
    MySQL锁
    全局锁
    对整个数据库加锁,命令:Flush tables with read lock 会让整个库只读。
    表级锁
    表锁:需要显式使用,在没有释放前其他线程会被阻塞,断开连接也会释放锁
    元数据锁:在访问一个表的时候会被自动加上,对一个表数据操作时,会加MDL读锁,修改表结构时会加MDL写锁,MDL锁在语句执行时开始申请,事务提交后才会释放。

    • 读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查。
    • 读写锁之间、写锁之间是互斥的,用来保证变更表结构操作的安全性。因此,如果有两个线程要同时给一个表加字段,其中一个要等另一个执行完才能开始执行。

    行锁
    在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。
    死锁
    当A事务先修改1在修改2 B事务修改2在修改1 同时发生时会造成A等待B释放2 B事务等待A释放1,就造成死锁了。
    解决死锁:设置超时时间 参数 innodb_lock_wait_timeout 来设置
    发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数 innodb_deadlock_detect 设置为 on,表示开启这个逻辑。
    Online DDL
    Online DDL重建表,可以清理空洞(不按照主键自增顺序插入数据会产生空洞,因为delete只是标记了这个位置可以被重新使用,实际占用空间还是存在。)

    order by语句的执行流程
    1、初始化sort_buffer,放入查询的字段
    2、从索引中找到第一个满足条件的主键id
    3、到主键id索引中取出整行数据,存入sort_buffer中
    4、从索引中查找下一个数据
    5、重复2 3 4直到下一个数据不满足条件为止
    6、对sort_buffer中的数据排序
    7、返回排序后的结果
    sort_buffer放不下这么多数据的时候,会开辟一些临时文件,在多个临时文件中排序,在合并成一个大的有序文件,sort_buffer的空间越小,临时文件的数量越大。使用临时文件排序一般是归并排序算法。
    如果排序的字段很大可以通过参数(SET max_length_for_sort_data = 16)设置临界值,mysql会使用rowid 排序。