太完整了!java编程思想第六版
前⾔
今年的⾦三银四已经过去⼀⼤半了,在这其中参与过不少⾯试,2021都说⼯作不好,这也是对开发⼈员的要求变向的提⾼了。
之前在Github上收获15K+star的Java核⼼神技(这参数,质量多⾼就不⽤我多说了吧)⾮常全⾯,包含基础知识、Java集合、JVM、多线程并发、spring原理、微服务、Netty 与RPC 、Kafka、⽇记、设计模式、Java算法、数据库、Zookeeper、分布式缓存、数据结构等等内容⾮常丰富,已经帮很多⼈拿下互联⽹⼀线公司的offer
InnoDB总体结构
⾸先我们来看官⽹的⼀张图(图⽚来源于MySQL官⽹):
从上图中可以看出其主要分为两部分结构,⼀部分为内存中的结构(上图左边),⼀部分为磁盘中的结构(上图右边)
内存结构
InnoDB内存中的结构主要分为:Buffer Pool,Change Buffer和Log Buffer三部分。
Buffer Pool
Buffer Pool是InnoDB缓存表和索引的⼀块主内存区域,Buffer Pool允许直接从内存中处理经常使⽤的数据,从⽽加快处理速度,带来⼀定的性能提升。 但是缓存总有放满的时候,当缓存满了新来的数据怎么处理呢?Bufer Pool中采⽤的是LRU(least recently used,最近最少使⽤)算法,LRU列表中最前⾯存的是⾼频使⽤页,尾部放的是最少使⽤的页。当有新数据过来⽽缓存满了就会覆盖尾部数据。
假如我们有⼀条查询语句⾮常⼤,返回的结果集直接就超过了Buffer Pool的⼤⼩,⽽这种语句使⽤场景⼜是极少的,可能查询这⼀次之后很久不会查询,⽽这⼀次就将缓存占满了,将⼀些热点数据全部覆盖了。为了避免这种情况发⽣,InnoDB对传统的LRU算法⼜做了改进,将LRU列表分拆分为2个,如下图(图⽚来源于MySQL官⽹):
该算法在new⼦列表中保留⼤量页⾯(5/8),old⼦列表包含较少使⽤的页⾯(3/8);old⼦列表中数据可能会被覆盖,该算法具体操作如下:3/8的Buffer Pool空间⽤于old⼦列表
列表的中点是new⼦列表的尾部与old⼦列表的头部之间的边界
当InnoDB将⼀个页⾯读⼊缓冲池时,它⾸先将它插⼊到中间点(old⼦列表的头)。读取的页⾯是由⽤户发起的操作(⽐如SQL查询)或InnoDB⾃动执⾏的预读操作
访问old⼦列表中的页⾯使其“young”,并将其移动到new⼦列表的头部。如果读取的页是由⽤户发起的操作,那么就会⽴即进⾏第⼀次访问,并使页⾯处于young状态;如果读取的页是由预读发起的操作,那么第⼀次访问不会⽴即发⽣,⽽且可能直到覆盖都不会发⽣。
操作数据时,Buffer Pool中未被访问的页会逐渐移到尾部,最终会被覆盖。
默认情况下,查询读取的页⾯会⽴即移动到新的⼦列表中,这意味着它们在缓冲池中停留的时间更长。
Change Buffer
Change Buffer是⼀种特殊的缓存结构,⽤来缓存不在Buffer Pool中的辅助索引页, ⽀持insert, updat
e,delete(DML)操作的缓存(注意,这个在MySQL5.5之前叫做Insert Buffer,仅⽀持insert操作的缓存)。当这些数据页被其他查询加载到Buffer Pool后,则会将数据进⾏merge到索引数据叶中。
InnoDB在进⾏DML操作⾮聚集⾮唯⼀索引时,会先判断要操作的数据页是不是在Buffer Pool中,如果不在就会先放到Change Buffer进⾏操作,然后再以⼀定的频率将数据和辅助索引数据页进⾏merge。这时候通常都能将多个操作合并到⼀次操作,减少了IO操作,尤其是辅助索引的操作⼤部分都是IO操作,可以⼤⼤提⾼DML性能。
如果Change Buffer中存储了⼤量的数据,那么可能merge操作会需要消耗⼤量时间。
为什么Change Buffer只能针对⾮聚集⾮唯⼀索引
因为如果是主键索引或者唯⼀索引,需要判断数据是否唯⼀,这时候就需要去索引页中加载数据判断⽽不能仅仅只操作缓存。Change Buffer什么时候会merge
总体来说,Change Buffer的merge操作发⽣在以下三种情况:
辅助索引页被读取到Buffer Pool时。 当执⾏⼀条select语句时,会去检查当前数据页是否在Change Buffer中,如果在,就会把数据merge到索引页
该辅助索引页没有可⽤空间时。 InnoDB内部会检测辅助索引页是否还有可⽤空间(⾄少有1/32页),如果检测到当前操作之后,当前索引页剩余空间不⾜1/32时,会进⾏⼀次强制merge操作
后台线程Master Thread定时merge。 Master Thread是⼀个⾮常核⼼的后台线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的⼀致性。
Adaptive Hash Index
Adaptive Hash Index,⾃适应哈希索引。InnoDB引擎会监控对索引页的查询,如果发现建⽴哈希索引可以带来性能上的提升,就会建⽴哈希索引,这种称之为⾃适应哈希索引,InnoDB引擎不⽀持⼿动创建哈希索引。
Log Buffer
⽇志缓冲区是存储要写⼊磁盘⽇志⽂件的⼀块数据内存区域,⼤⼩由变量innodb_log_buffer_size 控制,默认⼤⼩为16MB(5.6版本是
8MB):
SHOW VARIABLES LIKE 'innodb_log_buffer_size';-- global级别,⽆session级别
上⽂讲述update语句更新流程⼀⽂中,我们只提到了Buffer Pool⽤来代替缓存区,通过本⽂对内存结构的分析,实际上Buffer Pool中严格来说还有Change Buffer,Log Buffer和Adaptive Hash Index三个部分,DML操作会缓存在Change Buffer区域,⽽写redo log之前会先写⼊Log Buffer,所以Log Buffer⼜可以称之为redo Log Buffer。
Log Buffer什么时候写⼊redo log
⼀个⼤的Log Buffer空间⼤允许运⾏⼤型事务,⽽⽆需在事务提交之前将redo log数据写⼊磁盘。Log Buffer中的数据会定期刷新到磁盘,那么Log Buffer的数据⼜是如何写⼊磁盘的呢?Log Buffer数据flush到磁盘有三种⽅式,通过变量
innodb_flush_log_at_trx_commit 控制,默认为1。 |value|描述|
当设置为0时,由于数据还在内存,所以崩溃后数据基本会被丢失
当设置为2时,由于数据已经实时写到redo log了,如果磁盘⽂件没有被损坏,还是可以恢复的
另外,Mast Thread默认1s进⾏⼀次刷盘操作,这个可以通过变量innodb_flush_log_at_timeout控制,默认1s。
SHOW VARIABLES LIKE 'innodb_flush_log_at_timeout';-- global级别,⽆session级别
磁盘结构
InnoDB引擎的磁盘结构,从⼤的⽅⾯来说可以分为Tablespace和redo log两部分
Tablespace
Tablespace可以分为4⼤类,分别是:System Tablespace,File-Per-Table Tablespaces,General Tablespaces,Undo Tablespaces
System Tablespace
系统表空间中包括了 InnoDB data dictionary,doublewrite buffer, change buffer, undo logs 4个部分,默认情况下InnoDB存储引擎有⼀个共享表空间ibdata1,如果我们创建表没有指定表空间,则表和索引数据也会存储在这个⽂件当中,可以通过⼀个变量控制(后⾯会介绍)。
ibdata1⽂件默认⼤⼩为12MB,可以通过变量innodb_data_file_path来控制,改变其⼤⼩的最好⽅式就是设置为⾃动扩展。
innodb_data_file_path=ibdata1:12M:autoextend
上⾯表⽰默认表空间ibdata1⼤⼩为12MB,⽀持⾃动扩展⼤⼩。
redis是nosql数据库吗当我们的⽂件达到⼀定的⼤⼩之后,⽐如达到了998MB,我们就可以另外开启⼀个表空间⽂件:
innodb_data_home_dir=
innodb_data_file_path=/ibdata/ibdata1:988M;/disk2/ibdata2:50M:autoextend
关于上⾯的设置有3点需要注意:
innodb_data_home_dir如果不设置的话,那么就默认所有的表空间⽂件都在datadir⽬录下,⽽我们上⾯指定了2个不同路径,所以需要把innodb_data_home_dir设为空
autoextend这个属性,只能放在最后⼀个⽂件
指定新的表空间⽂件名的时候,不能和现有表空间⽂件名⼀致,否则启动MySQL时会报错
当然,表空间可以增⼤,⾃然也可以减少,但是⼀般我们都不会去设置减少,⽽且减少表空间也相对⿇烦,在这⾥就不展开叙述了。
InnoDB Data Dictionary
InnoDB数据字典由内部系统表组成,其中包含⽤于跟踪对象(如表、索引和表列)的元数据。元数据在物理上位于InnoDB系统表空间中。由于历史原因,数据字典元数据在某种程度上与存储在InnoDB表元数据⽂件(.frm⽂件)中的信息重叠。
Doublewrite Buffer
Doublewrite Buffer,双写缓冲区,这个是InnoDB为了实现double write⽽设置的⼀块缓冲区,double write和上⾯的change buffer⼀个确保了可靠性,⼀个确保了性能的提升,是InnoDB中⾮常重要的两⼤特性。
我们先来看下⾯⼀张图:
InnoDB默认页的⼤⼩是16KB,⽽操作系统是4KB,如果存储引擎正在写⼊页的数据到磁盘时发⽣了宕机,可能出现页只写了⼀部分的情况,⽐如只写了 4K,这种情况叫做部分写失效(partial page write),可能会导致数据丢失。
可能有⼈会说,可以通过redo log来恢复,但是注意,redo log恢复数据有⼀个前提,那就是页没有损
坏,如果页本⾝已经被损坏了,那么是没办法恢复的,所以为了确保万⽆⼀失,我们需要先保存⼀个页的副本,如果出现了上⾯的极端情况,可以⽤页的副本结合redo log来恢复数据,这就是double write技术。
double write也是由两部分组成,⼀部分是内存中的double write buffer,⼤⼩为2MB,另⼀部分是物理磁盘上的共享表空间中的连续128个页,⼤⼩也是2MB,写⼊流程如下图(图⽚来源于《MySQL技术内幕 InnoDB存储引擎》):
double write机制会使得数据写⼊两次磁盘,但是其并不需要两倍的I/O开销或两倍的I/O操作。通过对
操作系统的单个fsync()调⽤,数据以⼀个⼤的顺序块的形式写⼊到双写⼊缓冲区。
在⼤多数情况下默认启⽤了doublewrite缓冲区。要禁⽤doublewrite缓冲区,可通过将变量innodb_doublewrite设置为0即可。
完结
Redis基于内存,常⽤作于缓存的⼀种技术,并且Redis存储的⽅式是以key-value的形式。Redis是如今互联⽹技术架构中,使⽤最⼴泛的缓存,在⼯作中常常会使⽤到。Redis也是中⾼级后端⼯程师技术⾯试中,⾯试官最喜欢问的问题之⼀,因此作为Java开发者,Redis是我们必须要掌握的。
Redis 是 NoSQL 数据库领域的佼佼者,如果你需要了解 Redis 是如何实现⾼并发、海量数据存储的,那么这份腾讯专家⼿敲《Redis源码⽇志笔记》将会是你的最佳选择。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。