覆盖索引
对于非聚簇索引(二级索引)来说,如果索引字段没有包含所有select语句中查询的字段,在二级索引B+树中查到了对应的主键id之后,还需要利用这些主键id去聚簇索引的B+树中进行回表查询
覆盖索引不是一种独立的索引类型,而是一种查询优化的效果,指的就是二级索引中包含了所有select语句中查询的字段,如此,我们可以在二级索引的B+树中得到所有需要的字段,不用去回表查询,这就是所谓覆盖索引。可以用EXPALIN的Extra列出现Using index来确认
索引下推
索引下推是MySQL5.6之后为了减少回表次数所提出的一种技术
假设有一张用户表,一个联合索引(name,age),对于下列查询语句
select * from user where name like '张%' and age = 18;
在MySQL5.6之前,MySQL会先利用索引找到所有姓张的用户,因为这之后的age无法通过索引去继续查找,必须要回表,所以会拿到对应的所有姓张的用户的主键id,然后利用这些id去回表查询数据行中的数据。假设姓张的用户有1000个,就需要回表1000次
而在MySQL5.6之后,处理利用主键查询到所有姓张的用户,MySQL会直接在存储引擎层获取到姓张的用户的索引数据,并在这基础上找到age为18的用户,然后获取到这些用户的id,再用这些id去回表查询。这样一来,就能大大减少获取到的主键id数量,从而减少回表次数,优化查询性能。这就是索引下推。可以用EXPLAIN的的Extra显示Using index condition来确认索引下推生效
MVCC(多版本并发控制)
MVCC多版本并发控制,是InnoDB可以让快照读不加锁,与写操作互不阻塞的重要机制。它只作用于快照读(普通SELECT),当前读(SELECT … FOR UPDATE、UPDATE、DELETE)不依赖于ReadView,而是靠记录锁+间隙锁(临键锁)解决并发。严格来说MVCC+临键锁共同解决RR下的幻读
对于读已提交和可重复读这两个事务隔离级别,就是利用Read View来实现的,只不过它们的创建时机不同。Read View可以理解为是一种数据快照
-
读已提交是在每一条select语句执行前,都会生成一个ReadView,然后使用这个ReadView
-
可重复读隔离级别是在事务的第一条select语句执行前,生成一个ReadView,然后整个事务都使用这一个ReadView
ReadView有四个重要字段
-
m_ids:活跃事务id列表,指的是在ReadView创建时,所有活跃的事务的id。活跃事务指的是开启了但是还没有提交的事务
-
min_trx_id:min_trx_id指的是m_ids中活跃事务的最小id
-
max_trx_id:max_trx_id指的是创建ReadView时,数据库应该给下一个事务的id值,也就是全局事务中最大的id+1
-
creator_trx_id:creator_trx_id指的是创建ReadView的事务id
对于MySQL的每一条数据行,都有两个隐藏字段
-
trx_id:记录的是当前修改该条数据的事务id,当某一个事务对其进行改动时,就会将这个事务id记录下来
-
roll_pointer:存储指针,当有事务对数据行进行修改时,会产生一个undo_log,而这个指针指向的就是undo_log中该行的上一个版本,多个旧版本通过它传承版本链 在创建ReadView时,我们可以把trx_id分为以下三种情况
-
trx_id < min_trx_id:trx_id < min_trx_id,意味着这条数据行最后被修改时的事务已经提交,是在创建ReadView之前就已经提交的,所以这条记录对当前事务可见
-
trx_id > max_trx_id:trx_id > max_trx_id,意味着这条数据是在创建ReadView之后启动事务生成的,因此这条记录对当前事务不可见
-
min_trx_id <= trx_id < max_trx_id:这需要分为两种情况,如果trx_id在m_ids列表里,说明当前事务仍然活跃,事务还没提交,所以这条记录对当前事务不可见;如果trx_id不在m_ids里,说明修改这条记录的事务已经提交,所以这条记录就对当前事务可见 判断时从版本链最新版本开始,沿roll_pointer逐版本比对,找到第一个可见的旧版本
binlog
binlog:二进制日志,这是存储引擎层面的日志,是以二进制格式存储的。主要用于主从复制和数据备份
MySQL的数据更新之后,Server层还会生成一条binlog,在事务提交之后,会将事务中产生的所有binlog都写入到binlog文件中,binlog是Server层实现的日志,所有存储引擎都可以使用
binlog是追加写,写满一个文件,就会重新创建一个新的文件继续写,不会覆盖以前写入的内容,保留的是全量日志
binlog有3种类型格式,分别是STATEMENT、ROW、MIXED
-
STATEMENT:每一条修改数据的SQL都会记录到binlog中,主从复制时slave会根据记录的SQL语句重现。但是对于一些动态函数,如now()、uuid(),就会存在问题,从库中执行的结果和主库不同
-
ROW:记录行数据被修改成什么样了,这种格式的binlog不会出现STATEMENT格式下的问题。因为每次记录是行数据,主从复制时slave直接复制记录即可。但是对于批量更新,如果一次性更新了大量的数据,就会产生很多的行数据,导致binlog文件过大。而这种情况在STATEMENT格式下就只会记录一条update语句
-
MIXED:包含了STATEMENT和ROW两种模式,会根据不同的情况自动使用ROW或STATEMENT模式
UndoLog
UndoLog是一种用于撤销恢复的日志,保证了原子性
在事务提交之前,MySQL会先记录更新前的数据到undo log文件中,当事务需要回滚时,就会利用undo_log来进行回滚
-
当插入一条数据时,就会记录这条记录的主键id,回滚时根据主键id直接删除数据即可
-
当删除一条记录时,会记录这条行数据中的所有内容,回滚时再把这些数据插入到表中即可
-
当更新一条记录时,会把被更新的列的旧值记录下来,回滚时在把这些列更新成旧值
RedoLog
redo_log保证的是持久性
MySQL会利用Buffer Pool提升读写效率,每次更新数据时,都会先把数据记录到Buffer Pool中,但是由于Buffer Pool是基于内存的,因此如果出现了断点重启或者数据库宕机的情况,就会导致Buffer Pool中的数据丢失
而为了防止这类情况发生,当有一条记录需要更新时,InnoDB引擎会先更新内存,并将这一页标记为脏页,然后将对这个页的修改以redo_log的形式记录下来,然后就更新完成了
后续,InnoDB会利用后台线程在合适的时候,将Buffer Pool中缓存的脏页刷新到磁盘中,这就叫做WAL
WAL指的是,MySQL的写操作并不是立刻写到磁盘中,而是先写日志,然后在合适的时候再写到磁盘中
redo_log是物理日志,记录的是对某个数据页做了什么修改,每当执行一个事务就会产生一条或多条这样的日志
在事务提交时,只需要先将redo_log持久化到磁盘中即可,可以不需要直接将缓存中的数据持久化到磁盘。当系统崩溃时,如果还有数据没有从脏页中刷新到磁盘上,就会通过redo_log的内容,将所有数据恢复成最新的状态
既然在事务提交之后,redo_log也要持久化到磁盘中,那为什么不直接持久化缓存池中的数据
MySQL的redo_log采用的是追加写的方式写入到磁盘中,而持久化数据时,需要找到数据在磁盘中的位置,然后才能写入磁盘。redo_log是顺序写,而持久化数据是随机写。而顺序写的速度会比随机写更快,因此会选择先持久化redo_log,然后再利用WAL持久化Buffer Pool中的数据
redo log不是像binlog那样的追加写全量日志,而是固定大小,循环写的物理日志,写满后覆盖就记录,只保留最近一段时间的修改
SpringBoot 自动配置的原理
SpringBoot中的@SpringBootApplication注解中有一个@EnableAutoConfiguration注解,其中还有@Import(AutoConfigurationImportSelect.class)注解,会利用一个这个类,获取到classpath下所有jar中的META-INF文件夹,然后获取到spring.factories文件(SpringBoot2.7之后是org.springframework.boot.autoconfigure.AutoConfiguration.imports),扫描获取这个文件中所记录的所有全路径类名
找到指定的类,根据Condition注解选择性的加载这些类,例如@ConditionalOnBean只有在拥有某个Bean时才会加载这个类,@ConditinoalOnMissingBean,只有在没有这个Bean的时候才会加载类
加载类之后,会利用@ConfigurationProperties这类注解,去配置文件中加载需要的属性值,例如Redis就会从配置文件中获取以spring.data.redis开头的配置属性
然后再根据@Bean注解,将加载的类创建成Bean,并注入到IoC容器中
网硕互联帮助中心




评论前必须登录!
注册