网站建设怎么汇报,wordpress 改网址导航,安卓android软件,网页设计与制作的论文文章目录1.回顾缓冲池 Buffer Pool2.配置Buffer Pool的大小3.Buffer Pool#xff1a;数据结构3.1.磁盘数据结构#xff1a;数据页3.2.缓冲池数据结构#xff1a;数据页(缓存页)3.3.缓存页对应的描述信息4.Buffer Pool#xff1a;初始化5.Buffer Pool#xff1a;free链表6.…
文章目录1.回顾缓冲池 Buffer Pool2.配置Buffer Pool的大小3.Buffer Pool数据结构3.1.磁盘数据结构数据页3.2.缓冲池数据结构数据页(缓存页)3.3.缓存页对应的描述信息4.Buffer Pool初始化5.Buffer Poolfree链表6.Buffer Poolflush链表7.Buffer Pool淘汰缓存页7.1.缓存命中率7.2.LRU链表7.3.预读机制/全盘扫描7.4.冷热分离LRU7.5. 热数据LRU移动7.6. 冷数据LRU刷盘8.多个Buffer Pool优化并发能力8.1.chunk机制支持动态调整buffer pool8.2.Buffer pool总内存应该设置多少8.3.Buffer pool应该设置多少个1.回顾缓冲池 Buffer Pool
我们来看看下面这个更新SQL的流程图不管流程多么复杂有一点可以肯定的最终数据落地是落地在磁盘文件上。但是我们在对数据库执行增删改操作的时候不可能直接更新磁盘上的数据的因为如果你对磁盘进行随机读写操作那速度是相当的慢随便一个大磁盘文件的随机读写操作可能都要几百毫秒。如果要是那么搞的话可能你的数据库每秒也就只能处理几百个请求了 实际上我们对数据库执行增删改操作的时候实际上主要都是针对内存里的Buffer Pool中的数据进行的也就是你实际上主要是对数据库的内存里的数据结构进行了增删改。同时配合了后续的redo log、刷磁盘等机制和操作。所以Buffer Pool就是数据库的一个内存组件里面缓存了磁盘上的真实数据然后我们的Java系统对数据库执行的增删改操作其实主要就是对这个内存数据结构中的缓存数据执行的。
2.配置Buffer Pool的大小
因为Buffer Pool本质其实就是数据库的一个内存组件你可以理解为他就是一片内存数据结构所以这个内存数据结构肯定是有一定的大小的不可能是无限大的。这个Buffer Pool默认情况下是128MB还是有一点偏小了我们实际生产环境下完全可以对Buffer Pool进行调整
比如我们的数据库如果是16核32G的机器那么你就可以给Buffer Pool分配个2GB的内存使用下面的配置就可以了。
nnodb_buffer_pool_size 2147483648我们可以先查看下我们innodb_buffer_pool_size的大小执行如下SQL可以发现为134217728 134217728 /1024 / 1024 128M
show global variables like innodb_buffer_pool_size;设置大小
SET GLOBAL innodb_buffer_pool_size 324234233.Buffer Pool数据结构
3.1.磁盘数据结构数据页
现在我们知道了数据库中一定有一片内存区域是Buffer Pool了那么我们的数据是如何放在BufferPool中的 我们都知道数据库的核心数据模型就是表字段行的概念也就是说我们都知道数据库里有一个一个的表一个表有很多字段然后一个表里有很多行数据每行数据都有自己的字段值。所以大家觉得我们的数据是一行一行的放在Buffer Pool里面的吗 这就明显不是了实际上MySQL对数据抽象出来了一个数据页的概念他是把很多行数据放在了一个数据页里也就是说我们的磁盘文件中就是会有很多的数据页每一页数据里放了很多行数据如下图所示。
所以实际上假设我们要更新一行数据此时数据库会找到这行数据所在的数据页然后从磁盘文件里把这行数据所在的数据页直接给加载到Buffer Pool里去也就是说Buffer Pool中存放的是一个一个的数据页如下图。
3.2.缓冲池数据结构数据页(缓存页)
实际上默认情况下磁盘中存放的数据页的大小是16KB也就是说一页数据包含了16KB的内容。 而Buffer Pool中存放的一个一个的数据页我们通常叫做缓存页因为毕竟Buffer Pool是一个缓冲池里面的数据都是从磁盘缓存到内存去的。而Buffer Pool中默认情况下一个缓存页的大小和磁盘上的一个数据页的大小是一一对应起来的都是16KB。
3.3.缓存页对应的描述信息
对于每个缓存页他实际上都会有一个描述信息这个描述信息大体可以认为是用来描述这个缓存页的。比如包含如下的一些东西这个数据页所属的表空间、数据页的编号、这个缓存页在Buffer Pool中的地址以及别的一些杂七杂八的东西。每个缓存页都会对应一个描述信息这个描述信息本身也是一块数据在Buffer Pool中每个缓存页的描述数据放在最前面然后各个缓存页放在后面。 Buffer Pool中的描述数据大概相当于缓存页大小的5%左右也就是每个描述数据大概是800个字节左右的大小然后假设你设置的buffer pool大小是128MB实际上Buffer Pool真正的最终大小会超出一些可能有个130多MB的样子因为他里面还要存放每个缓存页的描述数据。
4.Buffer Pool初始化
数据库只要一启动就会按照你设置的Buffer Pool大小稍微再加大一点去找操作系统申请一块内存区域作为Buffer Pool的内存区域。然后当内存区域申请完毕之后数据库就会按照默认的缓存页的16KB的大小以及对应的800个字节左右的描述数据的大小在Buffer Pool中划分出来一个一个的缓存页和一个一个的他们对应的描述数据。然后当数据库把Buffer Pool划分完毕之后看起来就是之前我们看到的那张图了如下图所示 只不过这个时候Buffer Pool中的一个一个的缓存页都是空的里面什么都没有要等数据库运行起来之后当我们要对数据执行增删改查的操作的时候才会把数据对应的页从磁盘文件里读取出来放入Buffer Pool中的缓存页中。
5.Buffer Poolfree链表
当你的数据库运行起来之后你肯定会不停的执行增删改查的操作此时就需要不停的从磁盘上读取一个一个的数据页放入Buffer Pool中的对应的缓存页里去把数据缓存起来那么以后就可以对这个数据在内存里执行增删改查了。 但是此时在从磁盘上读取数据页放入Buffer Pool中的缓存页的时候必然涉及到一个问题那就是哪些缓存页是空闲的因为默认情况下磁盘上的数据页和缓存页是一 一对应起来的都是16KB一个数据页对应一个缓存页。所以我们必须要知道Buffer Pool中哪些缓存页是空闲的状态。
free链表他是一个双向链表数据结构这个free链表里每个节点就是一个空闲的缓存页的描述数据块的地址也就是说只要你一个缓存页是空闲的那么他的描述数据块就会被放入这个free链表中。 数据库启动的时候可能所有的缓存页都是空闲的因为此时可能是一个空的数据库一条数据都没有所以此时所有缓存页的描述数据块都会被放入这个free链表中。 个free链表里面就是各个缓存页的描述数据块只要缓存页是空闲的那么他们对应的描述数据块就会加入到这个free链表中每个节点都会双向链接自己的前后节点组成一个双向链表。
free链表他本身其实就是由Buffer Pool里的描述数据块组成的你可以认为是每个描述数据块里都有两个指针一个是free_pre一个是free_next分别指向自己的上一个free链表的节点。
对于free链表而言只有一个基础节点是不属于Buffer Pool的他是40字节大小的一个节点里面就存放了free链表的头节点的地址尾节点的地址还有free链表里当前有多少个节点。
那么磁盘数据页如何读取到缓冲池数据页的 首先我们需要从free链表里获取一个描述数据块然后就可以对应的获取到这个描述数据块对应的空闲缓存页我们看下图所示 接着我们就可以把磁盘上的数据页读取到对应的缓存页里去同时把相关的一些描述数据写入缓存页的描述数据块里去比如这个数据页所属的表空间之类的信息最后把那个描述数据块从free链表里去除就可以了如下图所示 我们在执行增删改查的时候肯定是先看看这个数据页有没有被缓存如果没被缓存就走上面的逻辑从free链表中找到一个空闲的缓存页从磁盘上读取数据页写入缓存页写入描述数据从free链表中移除这个描述数据块。但是如果数据页已经被缓存了那么就会直接使用了。
所以其实数据库还会有一个**哈希表数据结构他会用表空间号数据页号作为一个key然后缓存页的地址作为value。**读取一个数据页到缓存之后都会在这个哈希表中写入一个key-value对key就是表空间号数据页号value就是缓存页的地址那么下次如果你再使用这个数据页就可以从哈希表里直接读取出来他已经被放入一个缓存页了。
6.Buffer Poolflush链表
flush链表本质也是通过缓存页的描述数据块中的两个指针让被修改过的缓存页的描述数据块组成一个双向链表。凡是被修改过的缓存页都会把他的描述数据块加入到flush链表中去flush的意思就是这些都是脏页后续都是要flush刷新到磁盘上去的所以flush链表的结构如下图所示跟free链表几乎是一样的。
7.Buffer Pool淘汰缓存页
随着你不停的把磁盘上的数据页加载到空闲的缓存页里去free链表中的空闲缓存页是不是会越来越少因为只要你把一个数据页加载到一个空闲缓存页里去free链表中就会减少一个空闲缓存页。free链表中迟早有一天会没有空闲缓存页了。就像redis一样内存中存储的数据满了怎么淘汰数据
淘汰缓存页顾名思义你必须把一个缓存页里被修改过的数据给他刷到磁盘上的数据页里去然后这个缓存页就可以清空了让他重新变成一个空闲的缓存页。
7.1.缓存命中率
假设现在有两个缓存页一个缓存页的数据经常会被修改和查询比如在100次请求中有30次都是在查询和修改这个缓存页里的数据。那么此时我们可以说这种情况下缓存命中率很高。
另外一个缓存页里的数据就是刚从磁盘加载到缓存页之后被修改和查询过1次之后100次请求中没有一次是修改和查询这个缓存页的数据的那么此时我们就说缓存命中率有点低因为大部分请求可能还需要走磁查询数据他们要操作的数据不在缓存中。
因此在进行缓存页淘汰的时候命中率使我们考虑的一个原则。
7.2.LRU链表
的LRU就是Least Recently Used最近最少使用的意思。通过这个LRU链表我们可以知道哪些缓存页是最近最少被使用的那么当你缓存页需要腾出来一个刷入磁盘的时候不就可以选择那个LRU链表中最近最少被使用的缓存页了么
假设我们从磁盘加载一个数据页到缓存页的时候就把这个缓存页的描述数据块放到LRU链表头部去那么只要有数据的缓存页他都会在LRU里了而且最近被加载数据的缓存页都会放到LRU链表的头部去。
只要我们从磁盘加载一个数据页到缓存页的时候就把这个缓存页的描述数据块放到LRU链表头部去那么只要有数据的缓存页他都会在LRU里了而且最近被加载数据的缓存页都会放到LRU链表的头部去。因此在进行淘汰缓存页的时候直接在LRU链表的尾部找到一个缓存页然后你就把LRU链表尾部的那个缓存页刷入磁盘中然后把你需要的磁盘数据页加载到腾出来的空闲缓存页中就可以 了
7.3.预读机制/全盘扫描
谓预读机制说的就是当你从磁盘上加载一个数据页的时候他可能会连带着把这个数据页相邻的其他数据页也加载到缓存里去如下所示 那么这个时候你要是把LRU链表尾部的缓存页给刷入磁盘这是绝对不合理的最合理的反而是把上图中LRU链表的第二个通过预读机制加载进来的缓存页给刷入磁盘和清空毕竟他几乎是没什么人会访问的 到底哪些情况下会触发MySQL的预读机制呢
innodb_read_ahead_threshold
有一个参数是innodb_read_ahead_threshold他的默认值是56意思就是如果顺序的访问了一个区里的多个数据页访问的数据页的数量超过了这个阈值此时就会触发预读机制把下一个相邻区中的所有数据页都加载到缓存里去。
innodb_random_read_ahead
如果Buffer Pool里缓存了一个区里的13个连续的数据页而且这些数据页都是比较频繁会被访问的此时就会直接触发预读机制把这个区里的其他的数据页都加载到缓存里去这个机制是通过参数innodb_random_read_ahead来控制的他默认是OFF也就是这个规则是关闭的.
全表扫描 是类似如下的SQL语句SELECT * FROM USERS此时他没加任何一个where条件会导致他直接一下子把这个表里所有的数据页都从磁盘加载到Buffer Pool里去。
7.4.冷热分离LRU
上面所说的这些问题都是因为所有缓存页都混在一个LRU链表里才导致的。真正MySQL在设计LRU链表的时候采取的实际上是冷热数据分离的思想。 真正的LRU链表会被拆分为两个部分一部分是热数据一部分是冷数据这个冷热数据的比例是由innodb_old_blocks_pct参数控制的他默认是37也就是说冷数据占比37%。第一次被加载了数据的缓存页都会不停的移动到冷数据区域的链表头部。 MySQL设定了一个规则他设计了一个innodb_old_blocks_time参数默认值1000也就是1000毫秒。假设你加载了一个数据页到缓存去然后过了1s之后你还访问了这个缓存页说明你后续很可能会经常要访问它这个时间限制就是1s因此只有1s后你访问了这个缓存页他才会给你把缓存页放到热数据区域的链表头部去。 综上我们知道了在淘汰缓存的时候一定是优先淘汰冷数据区域几乎不怎么被访问的缓存页
7.5. 热数据LRU移动
接着我们来看看LRU链表的热数据区域的一个性能优化的点就是说在热数据区域中如果你访问了一个缓存页是不是应该要把他立马移动到热数据区域的链表头部去如果这样的话是不是这么频繁的进行移动是不是性能也并不是太好?
当并发量大的时候因为要加锁会存在锁竞争每次移动显然效率就会下降。因此 MySQL 针对这一点又做了优化如果一个缓存页处于热数据区域且在热数据区域的前 1/4 区域注意是热数据区域的 1/4不是整个链表的 1/4那么当访问这个缓存页的时候就不用把它移动到热数据区域的头部如果缓存页处于热数据的后 3/4 区域那么当访问这个缓存页的时候会把它移动到热数据区域的头部。
举个例子假设热数据区域的链表里有100个缓存页那么排在前面的25个缓存页他即使被访问了也不会移动到链表头部去的。但是对于排在后面的75个缓存页他只要被访问就会移动到链表头部去。这样的话他就可以尽可能的减少链表中的节点移动了
7.6. 冷数据LRU刷盘
首先第一个时机并不是在缓存页满的时候才会挑选LRU冷数据区域尾部的几个缓存页刷入磁盘而是有一个后台线程他会运行一个定时任务这个定时任务每隔一段时间就会把LRU链表的冷数据区域的尾部的一些缓存页刷入磁盘里去清空这几个缓存页把他们加入回free链表去所以实际上在缓存页没用完的时候可能就会清空一些缓存页了我们看下面的图示
8.多个Buffer Pool优化并发能力
MySQL同时接收到了多个请求他自然会用多个线程来处理这多个请求每个线程会负责处理一个请求。 现在多个线程来并发的访问这个Buffer Pool了此时他们都是在访问内存里的一些共享的数据结构比如说缓存页、各种链表之类的那么此时是不是必然要进行加锁对多线程并发访问一个Buffer Pool必然是要加锁的然后让一个线程先完成一系列的操作比如说加载数据页到缓存页更新free链表更新lru链表然后释放锁接着下一个线程再执行一系列的操作。
大部分情况下每个线程都是查询或者更新缓存页里的数据这个操作是发生在内存里的基本都是微秒级的很快很快包括更新free、flush、lru这些链表他因为都是基于链表进行一些指针操作性能也是极高的。
因此我们可以以给MySQL设置多个Buffer Pool来优化他的并发能力。 一般来说MySQL默认的规则是如果你给Buffer Pool分配的内存小于1GB那么最多就只会给你一个BufferPool。 但是如果你的机器内存很大那么你必然会给Buffer Pool分配较大的内存比如给他个8G内存那么此时你是同时可以设置多个Buffer Pool的比如说下面的MySQL服务器端的配置。
[server]
innodb_buffer_pool_size 8589934592
innodb_buffer_pool_instances 4我们给buffer pool设置了8GB的总内存然后设置了他应该有4个Buffer Pool此时就是说每个buffer pool的大小就是2GB这个时候MySQL在运行的时候就会有4个Buffer Pool了每个Buffer Pool负责管理一部分的缓存页和描述数据块有自己独立的free、flush、lru等链表。
8.1.chunk机制支持动态调整buffer pool
buffer pool是由很多chunk组成的他的大小是innodb_buffer_pool_chunk_size参数控制的默认值就是128MB。每个chunk就是一系列的描述数据块和缓存页这样的话就是把buffer pool按照chunk为单位拆分为了一系列的小数据块但是每个buffer pool是共用一套free、flush、lru的链表的 给大家讲解这个chunk机制倒不是让大家在数据库运行的时候动态调整buffer pool大小其实这不是重点重点是大家要了解数据库的buffer pool的真实的数据结构是可以由多个buffer pool组成的每个buffer pool是多个chunk组成的然后你只要知道他运行期间可以支持动态调整大小就可以了。
8.2.Buffer pool总内存应该设置多少
建议一个比较合理的、健康的比例是给buffer pool设置你的机器内存的50%~60%左右比如你有32GB的机器那么给buffer设置个20GB的内存剩下的留给OS和其他人来用这样比较合理一些。假设你的机器是128GB的内存那么buffer pool可以设置个80GB左右大概就是这样的一个规则。
8.3.Buffer pool应该设置多少个
接着确定了buffer pool的总大小之后就得考虑一下设置多少个buffer pool以及chunk的大小了 此时要记住有一个很关键的公式就是 buffer pool总大小 chunk大小 x chunk的个数 x buffer pool数量