Skip to content

随笔 ​

  1. id 不重要: 查询的序列号
  2. select_type 不重要:查询类型:例子:SIMPLE:简单的select不使用union或子查询
  3. table 不重要:表明
  4. type 重要 :
    1. const :最多只有一行记录匹配,当联合主键或唯一索引的所有字段跟常量值比较时,join类型为const。其他数据库也叫做唯一索引扫描
    2. eq_ref / ref :相对于下面的ref区别就是它使用的唯一索引,即主键或唯一索引,而ref使用的是非唯一索引或者普通索引。
    3. fulltext: 理解为效率较差但优先级高,若全文索引和普通索引同时存在时,mysql不管代价,优先选择使用全文索引。全文索引的优先级很高
    4. range: 索引范围查询,常见于使用 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN()或者like等运算符的查询中。
    5. index: 索引全表扫描,把索引从头到尾扫一遍
    6. all : 全表扫描
  5. key:真正用到的索引
  6. key_len : 查询用到的索引长度(字节数),多列索引,留下这个列的值,算一下你的多列索引长度就知道有没有使用所有的列
    1. key_len只计算where条件用到的索引长度,而排序和分组就算用到了索引,也不会计算到key_len中。
  7. rows: 这是mysql估算的需要扫描的行数
  8. extra 重要:
    1. distinct: select 中使用了distinct关键字
    2. using filesort: 表示mysql需要额外的排序操作,不能通过索引顺序达到排序效果,需要将其优化
    3. using index : 覆盖索引扫描。查询在索引树中就可以查找到所需要的数据,性能正常
    4. using temporary : 查询时候具有临时表
  9. ref列 :如果是使用的常数等值查询,这里会显示const,如果是连接查询,被驱动表的执行计划这里会显示驱动表的关联字段

字节B: 位Bit: int 是 4个字节:32位 一次io操作是4k(字节) 为了将磁盘io控制在很小的数量级,选择一个高度可控的多路搜索树木 b+

b+树怎么优化查询 ​

  1. io的次数取决于树的高度,假设高度h
  2. 表数据大小:N,每个磁盘块能存储的大小: M, 公式:h = log(m+1)N (m越大h越小)
  3. 每个磁盘块m = 磁盘块大小(固定4k/8k)/ 数据,所以磁盘块中的数据越小,磁盘块M越大,索引树越低
  4. 结论:
    1. 这就是为什么每个数据项,即索引字段要尽量的小
    2. 这也是为什么b+树要求把真实的数据放到叶子节点而不是内层节点,一旦放到内层节点,磁盘块的数据项会大幅度下降,导致树增高

对于索引的优化 ​

  1. 最左前缀匹配。(会一直从左向右直到遇到范围查找则停止)
    1. 我的条件是 a =1,b=1,c>1,d=1
    2. 如果复合索引为(a,b,c,d),索引走到c就停止了,d不会走索引
    3. 如果我的复合索引为(a,b,d,c),所有的都会走索引,因为查询优化器会给我自动优化
  2. =和in可以乱序 ,如上
  3. 索引列不能参与计算,保持列“干净” : from_unixtime(create_time) = ’2014-05-29’ 改为 create_time = unix_timestamp(’2014-05-29’)。
  4. 对索进行拓展而不是进行新建
  5. (重要)尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*):举例子唯一索引 算出来值为1;若为性别,只有男女情况下,基数大的场景下,这个就无限趋于0;就没必要建立索引

索引 ​

物理存储角度:非聚簇索引,聚蔟索引 ​

逻辑角度 主键索引,非主键索引,复合索引,唯一索引 ​

查询回表怎么回事 ​

流程 ​

  1. mvcc作用于读已提交,可重复读 (undolog /版本链 /readview)
    1. 所有的undolog通过回滚指针相连,共同组成版本链
      1. 此处不完全正确
      2. 版本链 = undolog(包含回滚指针) + record(原始数据)+ 事务id
      3. 对数据进行修改时不会直接覆盖数据而是会通过增加undolog的方式方便回滚
    2. readview
      1. 一个快照,存储当前活动的事务id(即未提交的事务),排序生成一个数组
    3. 使用方式
      1. 找到一条记录,找到该记录中当前的事务id,对比readview中该id所处的位置,如果该id在readview的最左边,说明该事务已经提交可以读取,若在右边,说明未提交,根据undolog中的回滚指针(roll-pointer)找到上一个版本,继续比对版本中事务id所处readview中的位置
      2. 如果处于该事务ID在readview最小和最大事务id之间,如果该id在readview中则不可以访问(表示该事务还没有提交,此时要沿着版本连向上找),如果id不在版本链中则可以访问
      3. 举例子:
        1. 事务id为2;版本链子是(1.3.4);这个时候就可以访问
  2. mvcc只会在RC和RR级别使用
    1. RC中:每次执行读操作时都会创建一个新的ReadView;因为读取的数据是读已提交,所以需要知道当前已经提交的事务
    2. RR中:
      1. 为了保证在整个事务中保证事务的一致性,第一次执行事务时候会创建readview,在该事务的处理期间,使用同一个readview,这样可以保证规避不可重复读的场景
      2. 幻读:其他事务在我的事务执行期间对查询范围插入行,RR中readview是固定所以其他事务在事务执行期间不能在查询范围内插入新的行,从而避免了幻读的问题。
      3. 间隙锁(Gap Lock)是一种为了防止幻读而设计的特殊锁形式,它在RR隔离级别下使用,但它仅是MVCC机制的一部分。间隙锁用于锁定索引记录之间的间隙,以防止其他事务在这些间隙中插入新的行;实际上它同时锁定索引记录之间的间隙和索引记录本身

红色的是undolog日志,所有的记录(红色绿色)称为版本链

场景: 理解一下:当出现update以后,会进行readview的更新,所以B第二次读取不是沿用一开始的版本链,而A没有更新操作,读取一直读的是当前的版本链就实现了可重复读

当前读与快照读 ​

当前读:在当前读(current read)的情况下,当前读,读取的是记录的最新版本,并且会对当前记录加锁,防止其他事务发修改这条记录。MySQL通过next-key lock来避免幻读。 一种是要加锁的特殊的读操作,它读的是记录的最新版本。

  1. -- 共享读锁
    1. select * from table where ? lock in share mode;
  2. -- 共享写锁
    1. select * from table where ? for update;
  3. -- 增删改也属于当前读,因为要先看这条记录在不在
    1. insert into table values (…);
    2. update table set ? where ?;
    3. delete from table where ?;

快照读:MVCCMySQL通过MVCC(多版本并发控制)来避免幻读。读取的是记录的可见版本 (有可能是历史版本),不用加锁。

场景 ​

RR级别下;

  1. 场景一:
    1. T1: select * from where id >100 <200;
    2. T2 : insert 150;(T1属于快照读所以不影响T2的插入)
    3. T1 select
    4. T1两次查询不会变更,解决幻读,insert可以插入;因为mvcc,readview只有第一次select生成的
  2. 场景二:
    1. T1: select * from where id >100 <200 for update(重点)
      1. 出现了for update说明要快照读,这时候对结果进行间隙锁
    2. T2 : insert 150;
      1. 插入阻塞,事务1没完成不会插入
    3. T1 select 解决幻读
  3. 场景三
    1. T1: select * from where id >100 <200;
    2. T2 : insert 150;
    3. T1 update t1 set name = ’11‘;(出现当前读,更新readview)
    4. T1 select * from where id >100 <200;
    5. 此时出现了150,幻读了 原因:: RR级别下ReadView的生成时机是在事务中的第一次查询,事务结束前该ReadView复用。但是如果事务中进行了当前读的操作,比如例3事务一中的update,后续再查询就会重新生成ReadView。也就是说update操作产生了当前读,那当前读肯定可以读到事务2已经提了的数据,然后全部更新后再去读就又会产生一个readview,很明显之前的update操作对于这个readview是可见的,所以数据的条数变成最新的了 所以RR级别在快照读的场景下通过mvcc保持一个readview的场景下保证了不会出现幻读,而快照读通过加锁的方式保证了数据不会更改保证了幻读的产生;至于场景三属于异常场景由最初的事务产生当前读导致readview更新导致

慢sql优化 ​

  1. 看是否业务不可接受,设置sql——no——chache
  2. where条件单表查,看那个条件区分度最高
  3. explain看执行计划
  4. order by limit 形式的sql语句让排序的表优先查!!
  5. 了解业务方使用场景
  6. 加索引

四种事物隔离级别 ​

  1. 读未提交
    1. 未提交的事务也可以读取,基本没有数据库用
  2. 读已提交
    1. 已经提交的事务可以进行读取;一个事务要等到另一个事务提交后才能读取数据 -》oracle,sqlserver
  3. 可重复读
    1. 即开始读取数据(事务开启)时,不再允许修改操作 -》 mysql
  4. 串行读
    1. 脏,不可重复,幻读都不会出现

脏读:未提交的事务被读取,只有在读未提交时候才会出现 一个事务读取另一个未提交的数据。

不可重复读 一个事务范围内两个相同的查询却返回了不同数据。 主要影响的是update操作,只有等update完成以后才能进行读取,在读已经提交时候最后出现(再升级就不可见)

幻读 主要影响是insert操作,也是一个事务范围内,两次查询返回了不同的数据

什么是 redo 和 undo 日志 什么是binlog?? ​

binlog ​

  1. 二进制日志,用于主从数据同步,数据恢复
  2. Statement-Based Replication (SBR) - 基于语句的复制
  3. Row-Based Replication (RBR) - 基于行的复制
  4. Mixed-Based Replication (MBR) - 混合模式复制: 这是上述两种模式的混合形式。MySQL会根据实际情况自动选择使用SBR或RBR来记录Binlog。
  5. 在RBR模式下,因为它需要记录更多的详细数据,会占用较大内存

binlog如何刷盘 ​

  1. 对于InnoDB存储引擎而言,只有在事务提交时才会记录binlog,此时记录还在内存中,那么binlog是什么时候刷到磁盘中的呢?
    1. 通过sync_binlog参数控制binlog的刷盘时机,取值范围是0-1-N
    2. 0:不去强制要求,由系统自行判断何时写入磁盘
    3. 1:每次commit的时候都要将binlog写入磁盘;
      1. MySQL 5.7.7之后版本的默认值
    4. N:每N个事务,才会将binlog写入磁盘。

redolog ​

  1. inndb为了支持事务持久性,设计出的日志文件
  2. 解决了事务持久化和刷盘的频次问题
  3. 具体来说就是只记录事务对数据页做了哪些修改

redolog基本概念 ​

  1. redo log包括两部分:
    1. 一个是内存中的日志缓冲(redolog buffer),
    2. 另一个是磁盘上的日志文件(redolog file)
    3. mysql每执行一条DML语句,先将记录写入redolog buffer,后续某个时间点再一次性将多个操作记录写到redolog file。这种先写日志,再写磁盘的技术就是MySQL里经常说到的**WAL(Write-Ahead Logging)**技术。
    4. 在innodb中,既有redo log需要刷盘,还有数据页也需要刷盘,redolog存在的意义主要就是降低对数据页刷盘的要求
    5. 虽然他在内存中有一部分,在磁盘有一部分,即便在磁盘中的内容他还是会循环覆盖哦,

undolog ​

redolog持久,那么undolog就是保证原子性

  1. undolog主要记录数据的逻辑变化,每个UPDATE语句,对应一条相反的UPDATE的undolog,这样在发生错误时,就能回滚到事务之前的数据状态

存储引擎 ​

不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎,还可以 获得特定的功能 MyIsam , 2. InnoDB, 3. Memory, 4. Archive, 5. Federated

InnoDB 底层存储结构为B+树, B树的每个节点对应innodb的一个page,page大小是固定的,一般设为 16k。其中非叶子节点只有键值,叶子节点包含完成数据。

三范式 ​

  1. 如果每列都是不可再分的最小数据单元(也称为最小的原子单元),则满足第一范式(1NF)
  2. 首先满足第一范式,并且表中非主键列不存在对主键的部分依赖。 第二范式要求每个表只描述一件事情。
  3. 满足第二范式,并且表中的列不存在对非主键列的传递依赖。除了主键订单编号外,顾客姓名依赖于非主键顾客编号。

存储过程 ​

一组为了完成特定功能的 SQL 语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它

触发器 ​

触发器是一段能自动执行的程序,是一种特殊的存储过程,触发器和普通的存储过程的区别是: 触发器是当对某一个表进行操作时触发

分库分表,结合自己的业务理解 ​

垂直区分(商品,交易,报表),水平区分(pt)

数据主从库同步 ​

binary log 就是binlog

  1. 主数据库将数据写到binary log(二进制日志)基于语句或者基于行的二进制
    1. 用于记录对数据库进行的更改操作,它是 MySQL 的事务日志,记录了数据库的增删改操作,以二进制的形式存储在磁盘上。
  2. 从库的io线程读取主库的binary log
  3. 从库的io线程将数据读取到relay log(中继日志)
    1. 该日志不会持久化存储
  4. mysql线程将中继日志内容读取替换到本地数据库中

数据库锁 ​

innodb支持的三种锁

  1. 行锁
    1. 锁直接加在记录上
  2. 间隙锁
    1. 当使用间隙锁时,InnoDB会锁住一个索引范围,确保索引记录的间隙不变;间隙锁是针对事务隔离级别为可重复读或以上级别而已的。
  3. Next-Key Lock
    1. 属于特殊的间隙锁,主要是间隙锁不会对索引范围两边的record加锁,但是next-key会
    2. 如果事务 A 锁定了键值范围 (10, 20],那么 Next-Key Lock 不仅会锁定这个范围的间隙,还会锁定键值为 10 和 20 的记录本身(区别在此)。
  4. 间隙锁是锁定索引记录之间的间隙。
    1. 当InnoDB扫描索引记录的时候,会首先对索引记录加上行锁(Record Lock),再对索引记录两边的间隙加上间隙锁(Gap Lock)。加上间隙锁之后,其他事务就不能在这个间隙修改或者插入记录。
  5. 何时能使用到间隙锁
    1. RR隔离级别下
    2. 查询条件必须有索引
    3. 间隙锁通过两个方面进行幻读的避免
      1. 防止间隙内有数据进行插入
      2. 防止在间隙外的数据通过update的操作移动到间隙内

数据库死锁?? ​

mysql的流式查询 ​

https://blog.csdn.net/liuxiao723846/article/details/130726967

  1. 普通:OOM
  2. 流:需要设置statement.setFetchSize(Integer.MIN_VALUE);
    1.  Statement statement = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);    
      
    2. 过流式查询获取一个ResultSet后,通过next迭代出所有元素之前或者调用close关闭它之前,不能使用同一个数据库连接去发起另外一个查询,否者抛出异常
    3. 分批的从TCP通道中读取mysql服务返回的数据,每次读取的数据量并不是一行(通常是一个package大小)
  3. 游标查询:
    1. 在连接参数中需要拼接useCursorFetch=true;
    2. Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3307/test?useSSL=false&useCursorFetch=true", "root", "123456");
    3. ((JDBC4Connection) connection).setUseCursorFetch(true); //com.mysql.jdbc.JDBC4Connection
    4. 设置fetchSize控制每一次获取多少条数据
    5. 由于MySQL方不知道客户端什么时候将数据消费完,而自身的对应表可能会有DML写入操作,此时MySQL需要建立一个临时空间来存放需要拿走的数据。因此对于当你启用useCursorFetch读取大表的时候会看到MySQL上的几个现象:
      1. 磁盘空间飙升
      2. 在数据准备完成后,开始传输数据的阶段,网络响应开始飙升,IOPS由“读写”转变为“读取”。
      3. 客户端JDBC发起SQL后,长时间等待SQL响应数据,这段时间就是服务端在准备数

索引下推 ​

索引下推 (Index Condition Pushdown,索引条件下推,简称 ICP),是 MySQL5.6 版本的新特性 它可以在对联合索引遍历过程中,对索引中包含的所有字段先做判断,过滤掉不符合条件的记录之后再回表

数据库锁:

当前读:for update/insert/update/delete 都是要加锁的

  1. InnoDB 中的行锁的实现依赖于索引,一旦某个加锁操作没有使用到索引,那么该锁就会退化为表锁。
  2. 记录锁存在于包括主键索引在内的唯一索引中,锁定单条索引记录。
  3. 间隙锁存在于非唯一索引中,锁定开区间范围内的一段间隔,它是基于临键锁实现的。
  4. 临键锁存在于非唯一索引中,该类型的每条记录的索引上都存在这种锁,它是一种特殊的间隙锁,锁定一段左开右闭的索引区间。

基于 VitePress 构建