精华内容
下载资源
问答
  • buffer
    千次阅读
    2022-05-15 21:02:34

    一、缓冲池

    ​​​​​14.5.1 Buffer Pool

    缓冲池是主内存中的一个区域,InnoDB在访问表和索引数据时将其缓存。缓冲池允许直接从内存访问经常使用的数据,从而加快处理速度。在专用服务器上,高达80%的物理内存通常分配给缓冲池。

    为了提高大容量读取操作的效率,缓冲池被划分为可能容纳多行的页面。为了提高缓存管理的效率,缓冲池被实现为页面的链接列表;很少使用的数据会使用最不常用(LRU)算法的变体从缓存中过时。

    了解如何利用缓冲池将频繁访问的数据保存在内存中是MySQL调优的一个重要方面。

    二、innodb_buffer_pool_size

    14.8.3.1 Configuring InnoDB Buffer Pool Size

    innodb_buffer_pool_size=innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances. 

    innodb_buffer_pool_size默认是128M,

    缓冲池的大小(字节),InnoDB缓存表和索引数据的内存区域。默认值为134217728字节(128MB)。最大值取决于CPU架构;32位系统的最大值为4294967295(2**32-1),64位系统的最大值为18446744073709551615(2**64-1)。在32位系统上,CPU体系结构和操作系统的实际最大大小可能低于规定的最大大小。当缓冲池的大小大于1GB时,将innodb_buffer_pool_instances设置为大于1的值可以提高繁忙服务器上的可伸缩性。

    更大的缓冲池需要更少的磁盘I/O来多次访问相同的表数据。在专用数据库服务器上,可以将缓冲池大小设置为机器物理内存大小的80%。在配置缓冲池大小时,请注意以下潜在问题,并准备在必要时缩小缓冲池的大小。

    对物理内存的竞争可能会导致操作系统中的分页。

    InnoDB为缓冲区和控制结构保留额外的内存,因此总分配空间比指定的缓冲池大小大约大10%。

    缓冲池的地址空间必须是连续的,这在具有在特定地址加载DLL的Windows系统上可能是一个问题。

    初始化缓冲池的时间大致与其大小成正比。在具有大型缓冲池的实例上,初始化时间可能很长。要缩短初始化周期,可以在服务器关闭时保存缓冲池状态,并在服务器启动时恢复。参见第14.8.3.6节“保存和恢复缓冲池状态”。

    当增加或减少缓冲池大小时,该操作将分块执行。区块大小由innodb_buffer_pool_chunk_size变量定义,该变量的默认值为128 MB。

    缓冲池大小必须始终等于或是innodb_buffer_pool_chunk_size*innodb_buffer_pool_instances的倍数。如果将缓冲池大小更改为不等于innodb_buffer_pool_chunk_size*innodb_buffer_pool_instances的值或其倍数,缓冲池大小将自动调整为等于或其倍数的值。

    innodb_buffer_pool_size可以动态设置,这允许您在不重新启动服务器的情况下调整缓冲池的大小。Innodb_buffer_pool_resize_status变量报告在线缓冲池大小调整操作的状态。有关更多信息,请参阅第14.8.3.1节“配置InnoDB缓冲池大小”。

    innodb_buffer_pool_chunk_size 默认是128M

    innodb_buffer_pool_instances默认是8(如果innodb_buffer_pool_size < 1GB,则是1)

    14.8.3.2 Configuring Multiple Buffer Pool Instances

    2.1查看现有配置

    mysql> show variables like 'innodb_buffer_pool%';
    +-------------------------------------+----------------+
    | Variable_name                       | Value          |
    +-------------------------------------+----------------+
    | innodb_buffer_pool_chunk_size       | 134217728      |
    | innodb_buffer_pool_dump_at_shutdown | ON             |
    | innodb_buffer_pool_dump_now         | OFF            |
    | innodb_buffer_pool_dump_pct         | 25             |
    | innodb_buffer_pool_filename         | ib_buffer_pool |
    | innodb_buffer_pool_instances        | 1              |
    | innodb_buffer_pool_load_abort       | OFF            |
    | innodb_buffer_pool_load_at_startup  | ON             |
    | innodb_buffer_pool_load_now         | OFF            |
    | innodb_buffer_pool_size             | 134217728      |
    +-------------------------------------+----------------+

    2.2简单优化

    把innodb_buffer_pool_size设置为1G。

    个人建议innodb_buffer_pool_size设置为系统内存的50%。

    最好设置为:innodb_buffer_pool_size=innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances.

    否则,innodb_buffer_pool_size自动调整可能是innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances的两倍。

    my.cnf

    # innodb缓冲池大小
    innodb_buffer_pool_size=1G
    
    # innodb缓冲池块大小
    innodb_buffer_pool_chunk_size=128M
    
    # innodb缓冲池实例数
    innodb_buffer_pool_instances=8

    重启数据库

    调整后:

    mysql> show variables like 'innodb_buffer_pool%';
    +-------------------------------------+----------------+
    | Variable_name                       | Value          |
    +-------------------------------------+----------------+
    | innodb_buffer_pool_chunk_size       | 134217728      |
    | innodb_buffer_pool_dump_at_shutdown | ON             |
    | innodb_buffer_pool_dump_now         | OFF            |
    | innodb_buffer_pool_dump_pct         | 25             |
    | innodb_buffer_pool_filename         | ib_buffer_pool |
    | innodb_buffer_pool_instances        | 8              |
    | innodb_buffer_pool_load_abort       | OFF            |
    | innodb_buffer_pool_load_at_startup  | ON             |
    | innodb_buffer_pool_load_now         | OFF            |
    | innodb_buffer_pool_size             | 1073741824     |
    +-------------------------------------+----------------+

    这些参数也支持在线调整,可考虑在业务低峰时调整。

    Configuring InnoDB Buffer Pool Size Online

    2.3配置是否合适

    5.1.3 Server Option, System Variable, and Status Variable Reference

    2.3.1查询缓存命中率:

    mysql> show status like 'Innodb_buffer_pool_read%';
    +---------------------------------------+--------------+
    | Variable_name                         | Value        |
    +---------------------------------------+--------------+
    | Innodb_buffer_pool_read_ahead_rnd     | 0            |
    | Innodb_buffer_pool_read_ahead         | 20294922     |
    | Innodb_buffer_pool_read_ahead_evicted | 1240192      |
    | Innodb_buffer_pool_read_requests      | 299216558100 |
    | Innodb_buffer_pool_reads              | 1167281260   |
    +---------------------------------------+--------------+

    Innodb_buffer_pool_read_requests:逻辑读取请求的数量。
    Innodb_buffer_pool_reads:InnoDB无法从缓冲池满足的逻辑读取数,必须直接从磁盘读取。
    percent = innodb_buffer_pool_read_requests / (innodb_buffer_pool_reads + innodb_buffer_pool_read_requests) * 100%
    上述的 percent>=99%,则表示当前的buffer pool满足当前的需求。否则需要考虑增加 innodb_buffer_pool_size的值。

    2.3.2缓存数据页占比:

    mysql> show status like 'Innodb_buffer_pool_pages%';
    +----------------------------------+----------+
    | Variable_name                    | Value    |
    +----------------------------------+----------+
    | Innodb_buffer_pool_pages_data    | 7003     |
    | Innodb_buffer_pool_pages_dirty   | 0        |
    | Innodb_buffer_pool_pages_flushed | 19906085 |
    | Innodb_buffer_pool_pages_free    | 1021     |
    | Innodb_buffer_pool_pages_misc    | 167      |
    | Innodb_buffer_pool_pages_total   | 8191     |
    +----------------------------------+----------+

    innodb_buffer_pool_pages_data:InnoDB缓冲池中包含数据的页数。这个数字包括脏页和干净页。(使用压缩表时,报告的Innodb_buffer_pool_pages_数据值可能大于)
    percent = Innodb_buffer_pool_pages_data / Innodb_buffer_pool_pages_total * 100%
    上述的 percent>=95% 则表示当前的innodb_buffer_pool_size满足当前的需求。否则可以考虑增加 innodb_buffer_pool_size的值。

    2.4如何判断MySQL使用内存会不会过高

    可能还有有一些担心,所有参数设置完毕后MySQL的占用会过高导致内存溢出,那么我们可以算一下他会不会太高。
    通过下面的SQL语句:
    SELECT ((@@key_buffer_size+@@innodb_buffer_pool_size+@@innodb_log_buffer_size)/1024/1024)+((@@read_rnd_buffer_size+@@read_buffer_size+@@myisam_sort_buffer_size+@@sort_buffer_size+@@join_buffer_size)/1024/1024*@@max_connections);
    最终单位为MB
    若该值不超过系统可用内存,说明还好(理论)

    2.5其他命令

    mysql> show status like 'Innodb_buffer_pool%';
    +---------------------------------------+--------------------------------------------------+
    | Variable_name                         | Value                                            |
    +---------------------------------------+--------------------------------------------------+
    | Innodb_buffer_pool_dump_status        | Dumping of buffer pool not started               |
    | Innodb_buffer_pool_load_status        | Buffer pool(s) load completed at 220313  7:31:02 |
    | Innodb_buffer_pool_resize_status      |                                                  |
    | Innodb_buffer_pool_pages_data         | 6999                                             |
    | Innodb_buffer_pool_bytes_data         | 114671616                                        |
    | Innodb_buffer_pool_pages_dirty        | 0                                                |
    | Innodb_buffer_pool_bytes_dirty        | 0                                                |
    | Innodb_buffer_pool_pages_flushed      | 19905034                                         |
    | Innodb_buffer_pool_pages_free         | 1024                                             |
    | Innodb_buffer_pool_pages_misc         | 168                                              |
    | Innodb_buffer_pool_pages_total        | 8191                                             |
    | Innodb_buffer_pool_read_ahead_rnd     | 0                                                |
    | Innodb_buffer_pool_read_ahead         | 20294410                                         |
    | Innodb_buffer_pool_read_ahead_evicted | 1240164                                          |
    | Innodb_buffer_pool_read_requests      | 299111990637                                     |
    | Innodb_buffer_pool_reads              | 1167212424                                       |
    | Innodb_buffer_pool_wait_free          | 1193110                                          |
    | Innodb_buffer_pool_write_requests     | 156029072                                        |
    +---------------------------------------+--------------------------------------------------+

    mysql> show engine innodb status \G
    mysql> SHOW GLOBAL STATUS \G 太多了。

    三、其他待优化:

    join_buffer_size = 128M
    sort_buffer_size = 2M
    read_rnd_buffer_size = 2M

    mysql> show variables like '%buffer_size%';
    +-------------------------+----------+
    | Variable_name           | Value    |
    +-------------------------+----------+
    | bulk_insert_buffer_size | 8388608  |
    | innodb_log_buffer_size  | 16777216 |
    | innodb_sort_buffer_size | 1048576  |
    | join_buffer_size        | 262144   |
    | key_buffer_size         | 8388608  |
    | myisam_sort_buffer_size | 8388608  |
    | preload_buffer_size     | 32768    |
    | read_buffer_size        | 131072   |
    | read_rnd_buffer_size    | 262144   |
    | sort_buffer_size        | 262144   |
    +-------------------------+----------+

    四、参考:

    Mysql优化之innodb_buffer_pool_size篇

    MySQL参数 之 innodb_buffer_pool_size

    MySQL中innodb_buffer_pool_size的配置

    MySQL基准测试innodb_buffer_pool_size对性能影响

    五、文档:

    Chapter 8 Optimization

    8.1 Optimization Overview

    8.2 Optimizing SQL Statements

    8.3 Optimization and Indexes

    8.4 Optimizing Database Structure

    8.5 Optimizing for InnoDB Tables

    8.6 Optimizing for MyISAM Tables

    8.7 Optimizing for MEMORY Tables

    8.8 Understanding the Query Execution Plan

    8.9 Controlling the Query Optimizer

    8.10 Buffering and Caching

    8.11 Optimizing Locking Operations

    8.12 Optimizing the MySQL Server

            8.12.4.1 How MySQL Uses Memory

    8.13 Measuring Performance (Benchmarking)

    8.14 Examining Server Thread (Process) Information

    8.5 Optimizing for InnoDB Tables

    8.5.1 Optimizing Storage Layout for InnoDB Tables

    8.5.2 Optimizing InnoDB Transaction Management

    8.5.3 Optimizing InnoDB Read-Only Transactions

    8.5.4 Optimizing InnoDB Redo Logging

    8.5.5 Bulk Data Loading for InnoDB Tables

    8.5.6 Optimizing InnoDB Queries

    8.5.7 Optimizing InnoDB DDL Operations

    8.5.8 Optimizing InnoDB Disk I/O

    8.5.9 Optimizing InnoDB Configuration Variables

    8.5.10 Optimizing InnoDB for Systems with Many Tables

    14.8.3 InnoDB Buffer Pool Configuration

    14.8.3.1 Configuring InnoDB Buffer Pool Size

    14.8.3.2 Configuring Multiple Buffer Pool Instances

    14.8.3.3 Making the Buffer Pool Scan Resistant

    14.8.3.4 Configuring InnoDB Buffer Pool Prefetching (Read-Ahead)

    14.8.3.5 Configuring Buffer Pool Flushing

    14.8.3.6 Saving and Restoring the Buffer Pool State

    更多相关内容
  • C语言头文件 BUFFER

    2022-06-11 10:43:12
    C语言头文件 BUFFERC语言头文件 BUFFERC语言头文件 BUFFERC语言头文件 BUFFERC语言头文件 BUFFERC语言头文件 BUFFERC语言头文件 BUFFERC语言头文件 BUFFERC语言头文件 BUFFERC语言头文件 BUFFERC语言头文件 BUFFERC...
  • 此模块可以检查如果一个对象是一个Buffer ,而不使用Buffer.isBuffer (其包括整个模块 )。 它是面向未来的,也适用于节点! 安装 npm install is-buffer 用法 var isBuffer = require ( 'is-buffer' ) isBuffer...
  • buffer:微型 C 字符串库

    2021-06-11 21:27:42
    缓冲 微型 C 字符串操作库。 安装 使用安装: $ clib install clibs/buffer 应用程序接口 buffer_t * buffer_new (); buffer_t * buffer_new_with_size ( size_t n);...buffer_t * ...buffer_free ( buffer_t
  • MySQL 的 Buffer Pool,终于被我搞懂了

    千次阅读 多人点赞 2022-03-25 22:15:26
    今天就聊 MySQL 的 Buffer Pool,发车! 为什么要有 Buffer Pool? 虽然说 MySQL 的数据是存储在磁盘里的,但是也不能每次都从磁盘里面读取数据,这样性能是极差的。 要想提升查询性能,加个缓存就行了嘛。所以,当...

    大家好,我是小林。

    今天就聊 MySQL 的 Buffer Pool,发车!

    在这里插入图片描述

    为什么要有 Buffer Pool?

    虽然说 MySQL 的数据是存储在磁盘里的,但是也不能每次都从磁盘里面读取数据,这样性能是极差的。

    要想提升查询性能,加个缓存就行了嘛。所以,当数据从磁盘中取出后,缓存内存中,下次查询同样的数据的时候,直接从内存中读取。

    为此,Innodb 存储引擎设计了一个缓冲池(Buffer Pool,来提高数据库的读写性能。

    有了缓冲池后:

    • 当读取数据时,如果数据存在于 Buffer Pool 中,客户端就会直接读取 Buffer Pool 中的数据,否则再去磁盘中读取。
    • 当修改数据时,首先是修改 Buffer Pool 中数据所在的页,然后将其页设置为脏页,最后由后台线程将脏页写入到磁盘。

    Buffer Pool 有多大?

    Buffer Pool 是在 MySQL 启动的时候,向操作系统申请的一片连续的内存空间,默认配置下 Buffer Pool 只有 128MB

    可以通过调整 innodb_buffer_pool_size 参数来设置 Buffer Pool 的大小,一般建议设置成可用物理内存的 60%~80%。

    Buffer Pool 缓存什么?

    InnoDB 会把存储的数据划分为若干个「页」,以页作为磁盘和内存交互的基本单位,一个页的默认大小为 16KB。因此,Buffer Pool 同样需要按「页」来划分。

    在 MySQL 启动的时候,InnoDB 会为 Buffer Pool 申请一片连续的内存空间,然后按照默认的16KB的大小划分出一个个的页, Buffer Pool 中的页就叫做缓存页。此时这些缓存页都是空闲的,之后随着程序的运行,才会有磁盘上的页被缓存到 Buffer Pool 中。

    所以,MySQL 刚启动的时候,你会观察到使用的虚拟内存空间很大,而使用到的物理内存空间却很小,这是因为只有这些虚拟内存被访问后,操作系统才会触发缺页中断,接着将虚拟地址和物理地址建立映射关系。

    Buffer Pool 除了缓存「索引页」和「数据页」,还包括了 undo 页,插入缓存、自适应哈希索引、锁信息等等。

    为了更好的管理这些在 Buffer Pool 中的缓存页,InnoDB 为每一个缓存页都创建了一个控制块,控制块信息包括「缓存页的表空间、页号、缓存页地址、链表节点」等等。

    控制块也是占有内存空间的,它是放在 Buffer Pool 的最前面,接着才是缓存页,如下图:

    上图中控制块和缓存页之间灰色部分称为碎片空间。

    为什么会有碎片空间呢?

    你想想啊,每一个控制块都对应一个缓存页,那在分配足够多的控制块和缓存页后,可能剩余的那点儿空间不够一对控制块和缓存页的大小,自然就用不到喽,这个用不到的那点儿内存空间就被称为碎片了。

    当然,如果你把 Buffer Pool 的大小设置的刚刚好的话,也可能不会产生碎片。

    查询一条记录,就只需要缓冲一条记录吗?

    不是的。

    当我们查询一条记录时,InnoDB 是会把整个页的数据加载到 Buffer Pool 中,因为,通过索引只能定位到磁盘中的页,而不能定位到页中的一条记录。将页加载到 Buffer Pool 后,再通过页里的页目录去定位到某条具体的记录。

    关于页结构长什么样和索引怎么查询数据的问题可以在这篇找到答案:换一个角度看 B+ 树

    如何管理 Buffer Pool?

    如何管理空闲页?

    Buffer Pool 是一片连续的内存空间,当 MySQL 运行一段时间后,这片连续的内存空间中的缓存页既有空闲的,也有被使用的。

    那当我们从磁盘读取数据的时候,总不能通过遍历这一片连续的内存空间来找到空闲的缓存页吧,这样效率太低了。

    所以,为了能够快速找到空闲的缓存页,可以使用链表结构,将空闲缓存页的「控制块」作为链表的节点,这个链表称为 Free 链表(空闲链表)。

    Free 链表上除了有控制块,还有一个头节点,该头节点包含链表的头节点地址,尾节点地址,以及当前链表中节点的数量等信息。

    Free 链表节点是一个一个的控制块,而每个控制块包含着对应缓存页的地址,所以相当于 Free 链表节点都对应一个空闲的缓存页。

    有了 Free 链表后,每当需要从磁盘中加载一个页到 Buffer Pool 中时,就从 Free链表中取一个空闲的缓存页,并且把该缓存页对应的控制块的信息填上,然后把该缓存页对应的控制块从 Free 链表中移除。

    如何管理脏页?

    设计 Buffer Pool 除了能提高读性能,还能提高写性能,也就是更新数据的时候,不需要每次都要写入磁盘,而是将 Buffer Pool 对应的缓存页标记为脏页,然后再由后台线程将脏页写入到磁盘。

    那为了能快速知道哪些缓存页是脏的,于是就设计出 Flush 链表,它跟 Free 链表类似的,链表的节点也是控制块,区别在于 Flush 链表的元素都是脏页。

    有了 Flush 链表后,后台线程就可以遍历 Flush 链表,将脏页写入到磁盘。

    如何提高缓存命中率?

    Buffer Pool 的大小是有限的,对于一些频繁访问的数据我们希望可以一直留在 Buffer Pool 中,而一些很少访问的数据希望可以在某些时机可以淘汰掉,从而保证 Buffer Pool 不会因为满了而导致无法再缓存新的数据,同时还能保证常用数据留在 Buffer Pool 中。

    要实现这个,最容易想到的就是 LRU(Least recently used)算法。

    该算法的思路是,链表头部的节点是最近使用的,而链表末尾的节点是最久没被使用的。那么,当空间不够了,就淘汰最久没被使用的节点,从而腾出空间。

    简单的 LRU 算法的实现思路是这样的:

    • 当访问的页在 Buffer Pool 里,就直接把该页对应的 LRU 链表节点移动到链表的头部。
    • 当访问的页不在 Buffer Pool 里,除了要把页放入到 LRU 链表的头部,还要淘汰 LRU 链表末尾的节点。

    比如下图,假设 LRU 链表长度为 5,LRU 链表从左到右有 1,2,3,4,5 的页。

    如果访问了 3 号的页,因为 3 号页在 Buffer Pool 里,所以把 3 号页移动到头部即可。

    而如果接下来,访问了 8 号页,因为 8 号页不在 Buffer Pool 里,所以需要先淘汰末尾的 5 号页,然后再将 8 号页加入到头部。

    到这里我们可以知道,Buffer Pool 里有三种页和链表来管理数据。

    图中:

    • Free Page(空闲页),表示此页未被使用,位于 Free 链表;
    • Clean Page(干净页),表示此页已被使用,但是页面未发生修改,位于LRU 链表。
    • Dirty Page(脏页),表示此页「已被使用」且「已经被修改」,其数据和磁盘上的数据已经不一致。当脏页上的数据写入磁盘后,内存数据和磁盘数据一致,那么该页就变成了干净页。脏页同时存在于 LRU 链表和 Flush 链表。

    简单的 LRU 算法并没有被 MySQL 使用,因为简单的 LRU 算法无法避免下面这两个问题:

    • 预读失效;
    • Buffer Pool 污染;

    什么是预读失效?

    先来说说 MySQL 的预读机制。程序是有空间局部性的,靠近当前被访问数据的数据,在未来很大概率会被访问到。

    所以,MySQL 在加载数据页时,会提前把它相邻的数据页一并加载进来,目的是为了减少磁盘 IO。

    但是可能这些被提前加载进来的数据页,并没有被访问,相当于这个预读是白做了,这个就是预读失效

    如果使用简单的 LRU 算法,就会把预读页放到 LRU 链表头部,而当 Buffer Pool空间不够的时候,还需要把末尾的页淘汰掉。

    如果这些预读页如果一直不会被访问到,就会出现一个很奇怪的问题,不会被访问的预读页却占用了 LRU 链表前排的位置,而末尾淘汰的页,可能是频繁访问的页,这样就大大降低了缓存命中率。

    怎么解决预读失效而导致缓存命中率降低的问题?

    我们不能因为害怕预读失效,而将预读机制去掉,大部分情况下,局部性原理还是成立的。

    要避免预读失效带来影响,最好就是让预读的页停留在 Buffer Pool 里的时间要尽可能的短,让真正被访问的页才移动到 LRU 链表的头部,从而保证真正被读取的热数据留在 Buffer Pool 里的时间尽可能长

    那到底怎么才能避免呢?

    MySQL 是这样做的,它改进了 LRU 算法,将 LRU 划分了 2 个区域:old 区域 和 young 区域

    young 区域在 LRU 链表的前半部分,old 区域则是在后半部分,如下图:

    old 区域占整个 LRU 链表长度的比例可以通过 innodb_old_blocks_pc 参数来设置,默认是 37,代表整个 LRU 链表中 young 区域与 old 区域比例是 63:37。

    划分这两个区域后,预读的页就只需要加入到 old 区域的头部,当页被真正访问的时候,才将页插入 young 区域的头部。如果预读的页一直没有被访问,就会从 old 区域移除,这样就不会影响 young 区域中的热点数据。

    接下来,给大家举个例子。

    假设有一个长度为 10 的 LRU 链表,其中 young 区域占比 70 %,old 区域占比 20 %。

    现在有个编号为 20 的页被预读了,这个页只会被插入到 old 区域头部,而 old 区域末尾的页(10号)会被淘汰掉。

    如果 20 号页一直不会被访问,它也没有占用到 young 区域的位置,而且还会比 young 区域的数据更早被淘汰出去。

    如果 20 号页被预读后,立刻被访问了,那么就会将它插入到 young 区域的头部,young 区域末尾的页(7号),会被挤到 old 区域,作为 old 区域的头部,这个过程并不会有页被淘汰。

    虽然通过划分 old 区域 和 young 区域避免了预读失效带来的影响,但是还有个问题无法解决,那就是 Buffer Pool 污染的问题。

    什么是 Buffer Pool 污染?

    当某一个 SQL 语句扫描了大量的数据时,在 Buffer Pool 空间比较有限的情况下,可能会将 Buffer Pool 里的所有页都替换出去,导致大量热数据被淘汰了,等这些热数据又被再次访问的时候,由于缓存未命中,就会产生大量的磁盘 IO,MySQL 性能就会急剧下降,这个过程被称为 Buffer Pool 污染

    注意, Buffer Pool 污染并不只是查询语句查询出了大量的数据才出现的问题,即使查询出来的结果集很小,也会造成 Buffer Pool 污染。

    比如,在一个数据量非常大的表,执行了这条语句:

    select * from t_user where name like "%xiaolin%";
    

    可能这个查询出来的结果就几条记录,但是由于这条语句会发生索引失效,所以这个查询过程是全表扫描的,接着会发生如下的过程:

    • 从磁盘读到的页加入到 LRU 链表的 old 区域头部;
    • 当从页里读取行记录时,也就是页被访问的时候,就要将该页放到 young 区域头部;
    • 接下来拿行记录的 name 字段和字符串 xiaolin 进行模糊匹配,如果符合条件,就加入到结果集里;
    • 如此往复,直到扫描完表中的所有记录。

    经过这一番折腾,原本 young 区域的热点数据都会被替换掉。

    举个例子,假设需要批量扫描:21,22,23,24,25 这五个页,这些页都会被逐一访问(读取页里的记录)。

    在批量访问这些数据的时候,会被逐一插入到 young 区域头部。

    可以看到,原本在 young 区域的热点数据 6 和 7 号页都被淘汰了,这就是 Buffer Pool 污染的问题。

    怎么解决出现 Buffer Pool 污染而导致缓存命中率下降的问题?

    像前面这种全表扫描的查询,很多缓冲页其实只会被访问一次,但是它却只因为被访问了一次而进入到 young 区域,从而导致热点数据被替换了。

    LRU 链表中 young 区域就是热点数据,只要我们提高进入到 young 区域的门槛,就能有效地保证 young 区域里的热点数据不会被替换掉。

    MySQL 是这样做的,进入到 young 区域条件增加了一个停留在 old 区域的时间判断

    具体是这样做的,在对某个处在 old 区域的缓存页进行第一次访问时,就在它对应的控制块中记录下来这个访问时间:

    • 如果后续的访问时间与第一次访问的时间在某个时间间隔内,那么该缓存页就不会被从 old 区域移动到 young 区域的头部
    • 如果后续的访问时间与第一次访问的时间不在某个时间间隔内,那么该缓存页移动到 young 区域的头部

    这个间隔时间是由 innodb_old_blocks_time 控制的,默认是 1000 ms。

    也就说,只有同时满足「被访问」与「在 old 区域停留时间超过 1 秒」两个条件,才会被插入到 young 区域头部,这样就解决了 Buffer Pool 污染的问题 。

    另外,MySQL 针对 young 区域其实做了一个优化,为了防止 young 区域节点频繁移动到头部。young 区域前面 1/4 被访问不会移动到链表头部,只有后面的 3/4被访问了才会。

    脏页什么时候会被刷入磁盘?

    引入了 Buffer Pool 后,当修改数据时,首先是修改 Buffer Pool 中数据所在的页,然后将其页设置为脏页,但是磁盘中还是原数据。

    因此,脏页需要被刷入磁盘,保证缓存和磁盘数据一致,但是若每次修改数据都刷入磁盘,则性能会很差,因此一般都会在一定时机进行批量刷盘。

    可能大家担心,如果在脏页还没有来得及刷入到磁盘时,MySQL 宕机了,不就丢失数据了吗?

    这个不用担心,InnoDB 的更新操作采用的是 Write Ahead Log 策略,即先写日志,再写入磁盘,通过 redo log 日志让 MySQL 拥有了崩溃恢复能力。

    下面几种情况会触发脏页的刷新:

    • 当 redo log 日志满了的情况下,会主动触发脏页刷新到磁盘;
    • Buffer Pool 空间不足时,需要将一部分数据页淘汰掉,如果淘汰的是脏页,需要先将脏页同步到磁盘;
    • MySQL 认为空闲时,后台线程回定期将适量的脏页刷入到磁盘;
    • MySQL 正常关闭之前,会把所有的脏页刷入到磁盘;

    在我们开启了慢 SQL 监控后,如果你发现**「偶尔」会出现一些用时稍长的 SQL**,这可能是因为脏页在刷新到磁盘时可能会给数据库带来性能开销,导致数据库操作抖动。

    如果间断出现这种现象,就需要调大 Buffer Pool 空间或 redo log 日志的大小。

    总结

    Innodb 存储引擎设计了一个缓冲池(Buffer Pool,来提高数据库的读写性能。

    Buffer Pool 以页为单位缓冲数据,可以通过 innodb_buffer_pool_size 参数调整缓冲池的大小,默认是 128 M。

    Innodb 通过三种链表来管理缓页:

    • Free List (空闲页链表),管理空闲页;
    • Flush List (脏页链表),管理脏页;
    • LRU List,管理脏页+干净页,将最近且经常查询的数据缓存在其中,而不常查询的数据就淘汰出去。;

    InnoDB 对 LRU 做了一些优化,我们熟悉的 LRU 算法通常是将最近查询的数据放到 LRU 链表的头部,而 InnoDB 做 2 点优化:

    • 将 LRU 链表 分为young 和 old 两个区域,加入缓冲池的页,优先插入 old 区域;页被访问时,才进入 young 区域,目的是为了解决预读失效的问题。
    • 当「页被访问」且「 old 区域停留时间超过 innodb_old_blocks_time 阈值(默认为1秒)」时,才会将页插入到 young 区域,否则还是插入到 old 区域,目的是为了解决批量数据访问,大量热数据淘汰的问题。

    可以通过调整 innodb_old_blocks_pc 参数,设置 young 区域和 old 区域比例。

    在开启了慢 SQL 监控后,如果你发现「偶尔」会出现一些用时稍长的 SQL,这可因为脏页在刷新到磁盘时导致数据库性能抖动。如果在很短的时间出现这种现象,就需要调大 Buffer Pool 空间或 redo log 日志的大小。

    展开全文
  • 探索node中buffer的用法

    千次阅读 多人点赞 2020-10-24 21:03:50
    Buffer Buffer 对象用于表示固定长度的字节序列,许多Node.js的API都支持Buffer Buffer 类是 JavaScript 的 Uint8Array 类的子类,且继承时带上了涵盖额外用例的方法。 只要支持 Buffer 的地方,Node.js API 都可以...

    Buffer

    Buffer 对象用于表示固定长度的字节序列,许多Node.js的API都支持Buffer

    Buffer 类是 JavaScript 的 Uint8Array 类的子类,且继承时带上了涵盖额外用例的方法。 只要支持 Buffer 的地方,Node.js API 都可以接受普通的 Uint8Array。

    Buffer 类在全局作用域中,因此无需使用 require(‘buffer’).Buffer。

    为何使用buffer?

    • js自身只有字符串数据类型,没有二进制数据类型.
    • 但在处理像TCP流或者文件流时必须室友二进制数据,所以定义了一个buffer类
    • buffer类用来创建一个专门存放二进制数据的缓存区

    创建buffer类

    buffer提供了以下几个API来创建Buffer类

    • Buffer.alloc(size[,fill[,encoding]]):返回一个指定大小的 Buffer 实例,如果没有设置 fill,则默认填满 0
    • Buffer.allocUnsafe(size): 返回一个指定大小的 Buffer 实例,但是它不会被初始化,所以它可能包含敏感的数据
    • Buffer.allocUnsafeSlow(size)
    • Buffer.from(array): 返回一个被 array 的值初始化的新的 Buffer 实例(传入的 array 的元素只能是数字,不然就会自动被 0 覆盖)
    • Buffer.from(arrayBuffer[, byteOffset[, length]]): 返回一个新建的与给定的 ArrayBuffer 共享同一内存的 Buffer。
    • Buffer.from(buffer): 复制传入的 Buffer 实例的数据,并返回一个新的 Buffer 实例
    • Buffer.from(string[, encoding]): 返回一个被 string 的值初始化的新的 Buffer 实例

    示例:

    // 创建一个长度为10,用0填充的Buffer
    const buf1 = Buffer.alloc(10);
    
    //创建一个长度为10,用0x1填充的buffer
    const buf2 = Buffer.alloc(10, 1);
    
    //创建一个长度为10,且未初始化的buffer 
    //!这个方法比调用Buffer.alloc()更快,但返回的buffer实例可能包含旧数据
    //所以需要fill或者write重写
    const buf3 = Buffer.allocUnsafe(10)
    
    //创建一个包含[0x1,0x2,0x3]的buffer
    const buf4 = Buffer.from([1, 2, 3]);
    
    // 创建一个包含 UTF-8 字节 [0x74, 0xc3, 0xa9, 0x73, 0x74] 的 Buffer。
    const buf5 = Buffer.from('tést');
    
    // 创建一个包含 Latin-1 字节 [0x74, 0xe9, 0x73, 0x74] 的 Buffer。
    const buf6 = Buffer.from('tést', 'latin1');
    
    //字符串转buffer
    const buffer7 = Buffer.from('angula','utf-8');
    console.log(buffer7)  //输出 <Buffer 61 6e 67 75 6c 61>
    

    缓冲区的长度

    语法:buf.length
    返回buffer对象所占据的内存长度
    

    示例:

    var e = Buffer.from('angula');
    var f = e.length;
    console.log('缓冲区e的长度:', f)   //输出:缓冲区e的长度: 6
    

    写入缓冲区

    语法:buf.write(string[,offset[,length]][,encoding])
    
    

    参数:

    • string:写入缓冲区的字符串
    • offset:缓冲区开始写入的索引值,默认为0
    • length:写入的字节数,默认为buffer.length
    • encoding:使用的编码,默认为utf-8

    根据encoding的字符编码写入string到buf中的offset位置.length参数是写入的字节数.如果buf没有足够的空间保存整个字符串,则只会写入string的一部分,只部分解码的字符不会被写入

    返回值: 返回实际写入的大小,如果buffer空间不足,则只会写入部分字符串

    buf = Buffer.alloc(256);
    len = buf.write("angula is the best");
    console.log("写入的字节数:", len);   //写入字节数:18
    
    

    从缓冲区读取数据

    语法:buf.toString([encoding[,start[,end]]])
    返回值: 解吗缓冲区数据并使用指定的编码返回字符串
    

    参数:

    • encoding:编码方式,默认为utf-8编码
    • start : 指定开始读取的索引位置
    • end:结束位置,默认为缓冲区的末尾
    
    let buf7 = Buffer.alloc(26);
    for (let i = 0; i < 26; i++) {
      buf7[i] = i + 97
    }
    console.log(buf7.toString('ascii'))
    console.log(buf7.toString('ascii', 0, 5));  // mascii编码,abcde
    console.log(buf7.toString('utf-8', 0, 5));  //utf-8   abcde
    console.log(buf7.toString(undefined, 0, 5));  //默认为utf-8编码
    

    将Buffer转换为JSON对象

    buf.toJSON()
    返回JSON对象
    

    当字符串化一个buffer实例时,JSON.stringify()会隐式的调用该toJSON();

    
    const buf8 = Buffer.from([0x1, 0x2, 0x3, 0x4]);
    const json = JSON.stringify(buf8);
    console.log(json);  //{"type":"Buffer","data":[1,2,3,4]}
    const copy = JSON.parse(json, (key, value) => {
      return value && value.type === 'Buffer' ? Buffer.from(value.data) : value
    })
    console.log(copy)  //<Buffer 01 02 03 04>
    

    缓冲区合并

    语法:Buffer.concat(list[, totalLength])
    返回一个多个成员合并的新 Buffer 对象。
    

    参数:

    • list - 用于合并的 Buffer 对象数组列表。
    • totalLength - 指定合并后Buffer对象的总长度。

    示例:

    
    //缓存区合并
    let buffer1 = Buffer.from('name:')
    let buffer2 = Buffer.from('angula')
    let buffer3 = Buffer.concat([buffer1, buffer2]);
    console.log('新对象buffer3:' + buffer3.toString());
    

    缓冲区比较

    语法:buf.compare(otherBuffer);
    返回一个数字,表示buf在otherbuffer之前之后或者相同
    

    参数:

    • otherBuffer:与buf对象比较的另外一个buffer对象
    let buff1 = Buffer.from('abc');
    let buff2 = Buffer.from('abcd');
    var result = buff1.compare(buff2);
    
    if (result < 0) {
      console.log(buff1 + '在' + buff2 + '之前');
    }
    else if (result == 0) {
      console.log(buff1 + '在' + buff2 + '相同');
    } else {
      console.log(buff1 + '在' + buff2 + '之后');
    }
    

    缓冲区的拷贝

    buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])
    没有返回值
    

    参数:

    • targetBuffer - 要拷贝的 Buffer 对象。
    • targetStart - 数字, 可选, 默认: 0
    • sourceStart - 数字, 可选, 默认: 0
    • sourceEnd - 数字, 可选, 默认: buffer.length

    示例:

    var a = Buffer.from('abcdefghjkl');
    var b = Buffer.from('angula');
    
    //从a的index=2开始,用b替换a之后的值,替换的长度为b的长度
    b.copy(a, 2);
    console.log(a.toString());   //输出abangulajkl
    

    缓冲区的裁剪

    语法:buf.slice([start[, end]])
    返回一个新的缓冲区,它和旧的缓冲区指向同一块内存,但是从索引start到end的位置剪切( >= start && < end)
    

    参数:

    • start - 数字, 可选, 默认: 0
    • end - 数字, 可选, 默认: buffer.length

    示例:

    
    let c = Buffer.from('angula');
    let d = c.slice(0, 3);
    console.log(d.toString())  //输出:ang
    

    buffer应用场景

    I/O操作

    关于 I/O 可以是文件或网络 I/O,以下为通过流的方式将 input.txt 的信息读取出来之后写入到 output.txt 文件

    const fs = require('fs');
    
    const inputStream = fs.createReadStream('input.txt'); // 创建可读流
    const outputStream = fs.createWriteStream('output.txt'); // 创建可写流
    
    inputStream.pipe(outputStream); // 管道读写
    
    

    在 Stream 中我们是不需要手动去创建自己的缓冲区,在 Node.js 的流中将会自动创建。

    加解密

    在一些加解密算法中会使用Buffer
    使用Buffer.alloc()初始化一个实例,然后使用fill方法作填充

    buf.fill(value[, offset[, end]][, encoding])

    • value: 第一个参数为要填充的内容
    • offset: 偏移量,填充的起始位置
    • end: 结束填充 buf 的偏移量
    • encoding: 编码集

    以下为 Cipher 的对称加密 示例:

    const crypto = require('crypto');
    const [key, iv, algorithm, encoding, cipherEncoding] = [
        'a123456789', '', 'aes-128-ecb', 'utf8', 'base64'
    ];
    
    const handleKey = key => {
        const bytes = Buffer.alloc(16); // 初始化一个 Buffer 实例,每一项都用 00 填充
        console.log(bytes); // <Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>
        bytes.fill(key, 0, 10) // 填充
        console.log(bytes); // <Buffer 61 31 32 33 34 35 36 37 38 39 00 00 00 00 00 00>
    
        return bytes;
    }
    
    let cipher = crypto.createCipheriv(algorithm, handleKey(key), iv);
    let crypted = cipher.update('angula', encoding, cipherEncoding);
        crypted += cipher.final(cipherEncoding);
    
    console.log(crypted) //yjOrnUKXqxMyQWuAUBcjyQ==
    
    
    展开全文
  • 缓存页对应的描述信息 4.Buffer Pool:初始化 5.Buffer Pool:free链表 6.Buffer Pool:flush链表 7.Buffer Pool:淘汰缓存页 7.1.缓存命中率 7.2.LRU链表 7.3.预读机制/全盘扫描 7.4.冷热分离LRU 7.5. 热数据LRU...

    MySQL性能优化(一)MySQL中SQL语句是如何执行的
    MySQL性能优化(二)InnoDB之日志文件

    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= 32423423
    

    3.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 Pool:free链表

    当你的数据库运行起来之后,你肯定会不停的执行增删改查的操作,此时就需要不停的从磁盘上读取一个一个的数据页放入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 Pool:flush链表

    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的预读机制呢?

    1. innodb_read_ahead_threshold

    有一个参数是innodb_read_ahead_threshold,他的默认值是56,意思就是如果顺序的访问了一个区里的多个数据页,访问的数据页的数量超过了这个阈值,此时就会触发预读机制,把下一个相邻区中的所有数据页都加载到缓存里去。

    1. innodb_random_read_ahead

    如果Buffer Pool里缓存了一个区里的13个连续的数据页,而且这些数据页都是比较频繁会被访问的,此时就会直接触发预读机制,把这个区里的其他的数据页都加载到缓存里去,这个机制是通过参数innodb_random_read_ahead来控制的,他默认是OFF,也就是这个规则是关闭的.

    1. 全表扫描
      是类似如下的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数量

    展开全文
  • InnoDB学习笔记二缓冲池(buffer pool)

    万次阅读 2022-05-09 17:35:34
    文章目录 一、buffer pool 1. 缓冲池(buffer pool) 2. Buffer Pool 高并发场景 3. 调整 Buffer Pool 修改配置调整 chunk 机制调整 4. Buffer Pool内存 如何设置 Buffer Pool 的总内存大小 如何设置 Buffer Pool 的...
  • Buffer API学习

    万次阅读 2021-06-20 11:31:37
    文章目录一、Buffer 读API二、字符串和Buffer的转换三、分散读取四、集中写五、黏包和半包现象出现原因1 黏包2 半包解决办法 一、Buffer 读API Buffer rewind() 不改变极限,把位置设置为0,表示从头开始读取。 ...
  • Triple Buffer介绍

    千次阅读 2022-02-11 15:23:40
    Triple Buffer介绍 Buffer Queue介绍 上图主要是 dequeue、queue、acquire、release ,在这个例子里面,App 是生产者,负责填充显示缓冲区(Buffer);SurfaceFlinger 是消费者,将各个进程的显示缓冲区做...
  • mysqld --innodb-buffer-pool-size=8G --innodb-buffer-pool-instances=16 如果 innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances的值已经大于innodb_buffer_pool_size的值,那么innodb_buffer_pool_...
  • Pytorch中的register_buffer()

    千次阅读 多人点赞 2021-08-08 18:18:45
    Pytorch中的register_buffer1.register_buffer( )的使用随着例子边看边讲例子1:使用类成员变量(类成员变量并不会在我们的model.state_dict(),即无法保存)例子2:使用类成员变量(类成员变量并不会随着model.cuda...
  • 环形缓冲区(ringbuffer

    千次阅读 多人点赞 2021-03-29 20:55:38
    环形缓冲区(ringbuffer) 环形缓冲区是嵌入式系统中十分重要的一种数据结构,比如在串口处理中,串口中断接收数据直接往环形缓冲区丢数据,而应用可以从环形缓冲区取数据进行处理,这样数据在读取和写入的时候都...
  • Xilinx FPGA资源解析与使用系列——Transceiver(八)TX buffer使用和旁路TX buffer vs 相位对齐电路TX Buffer Bypass总结 TX buffer vs 相位对齐电路 在GTX/GTH收发器 TX链路中有两个内部并行时钟作用于PCS:PMA...
  • Mysql 优化之 buffer pool 设置

    千次阅读 2020-08-13 15:45:45
    Buffer pool是内存中用来缓存数据和索引的存储区域,其是MySQL性能调优的重要一环。 理想情况下,设置的size越大,则缓存到内存的数据越多,InnoDB就越像是内存数据库。 以便于以后在查询的时候,万一你要是内存...
  • Node.js-Buffer对象

    千次阅读 2022-01-10 10:07:30
    注意Buffer中存储的都是二进制数据,但是在显示时以16进制显示Buffer.length表示占用内存的大小Buffer打印数字时会以十进制方式显示Buffer的创建方法通过Buffer的构造函数,但不推荐使用通过allocUnsafe方法通过...
  • Free Buffer waits

    千次阅读 2022-03-16 15:50:19
    问题 set line 120 select SQL_ID,event,count(*) from gv$...oracle等待事件之free buffer waits Free buffer waits 等待事件总结 free buffer waits 尝试解决方案 修改db_writer_processes参数为5,重启数据库
  • 配置InnoDB缓冲池(Buffer Pool)大小

    千次阅读 2021-02-02 15:10:52
    1.配置InnoDB缓冲池(Buffer Pool)大小当服务器正运行时,用户可以离线(启动时)或在线配置InnoDB缓冲池大小。这部分描述的行为适用这两种方法。当增加或减少innodb_buffer_pool_size时,该操作按照数据块(chunks)执行...
  • Ring Buffer介绍

    万次阅读 2019-09-09 16:29:14
    Ring Buffer的高级用法(类似内核KFIFO) 环形缓冲区(ring buffer),环形队列(ring queue) 多用于2个线程之间传递数据,是标准的先入先出(FIFO)模型。一般来说,对于多线程共享数据,需要使用mutex来同步,这样共享...
  • NodeJS —— Buffer 解读

    千次阅读 2018-10-12 00:15:07
    Buffer 概述 在 ES6 引入 TypedArray 之前,JavaScript 语言没有读取或操作二进制数据流的机制。 Buffer 类被引入作为 NodeJS API 的一部分,使其...Buffer 属于 Global 对象,使用时不需引入,且 Buffer 的大小...
  • Node.js的Buffer(缓冲区)和Stream

    千次阅读 2022-04-11 18:24:49
    Node.js的Buffer缓冲区和StreamBuffer 类8种编码方式实例:创建 BufferBuffer 实例写入缓冲区从缓冲区读取数据语法将 Buffer 转换为 JSON 对象缓冲区合并 Buffer 类 JavaScript 语言自身只有字符串数据类型,...
  • y = buffer(x,n) y = buffer(x,n,p) y = buffer(x,n,p,opt) [y,z] = buffer(___) [y,z,opt] = buffer(___) y = buffer(x,n) y = buffer(x,n) 将长度为L的信号向量x划分为长度为n的非重叠数据段(帧)。以下代码创建...
  • 这里只讨论数字电路里的双反相器式buffer,模拟运放型的Buffer不懂。 以前学的只记得buffer链用来插在长wire中以减小delay,还有放在大扇出逻辑中形成buffer tree来减小扇出,保证摆幅,减小延迟,也就是保证驱动...
  • 上一篇文章分析了dequeueBuffer函数的过程,本篇接着分析queueBuffer函数,当我们需要绘制图像时,调用dequeueBuffer函数获取到可用的GraphicBuffer之后就可以开始绘制了,最常见的绘制操作就是Android上层View的...
  • 澄清一下innodb buffer pool 缓存命中率(cachehit ratio)指标的计算,这个计算涉及到两个状态变量innodb_buffer_pool_reads和innodb_buffer_pool_read_requests。官方文档对这两个变量的解释如下: Innodb_buffer...
  • nodejs中buffer的基本使用

    千次阅读 2020-08-12 16:34:50
    什么是Bufferbuffer是一个单独的内存区域。 Buffer是一个类。 提供了很多操作字节的方法。 Buffer不需要引入。 Buffer类的方法及属性 1.申请一块内存区域 Buffer.alloc(size) //申请字节长度为10的内存空间 let...
  • MySQL原理解读——Buffer Pool和Change Buffer 1、Buffer Pool(缓冲池) ###1.1、概念 Buffer Pool是InnoDB存储引擎层的缓冲池,不属于MySQL的Server层,注意跟8.0删掉的“查询缓存”功能区分 ###1.2、作用 内存中...
  • 内存屏障今生之Store Buffer, Invalid Queue

    千次阅读 多人点赞 2020-08-03 23:23:34
    本文章将介绍cpu的优化过程,你将了解到硬件层面的优化——Store Buffer, Invalid Queue,以及软件层面的优化——cpu内存屏障指令。最原始的架构性能优化之旅——Store Buffer当cpu需要的数据在其他cpu的cache内时,...
  • MySQL中innodb_buffer_pool_size的配置

    千次阅读 2020-11-09 22:25:07
    文章目录innodb_buffer_pool_size参数概念理解相关连的参数innodb_buffer_pool_chunk_sizeinnodb_buffer_pool_instances关联参数之间的关系设置innodb_buffer_pool_size时的注意事项占50%~75%的内存是否真合适判断...
  • 上一章我们介绍了iio子系统中的iio event模块,本章我们将介绍iio buffer模块,iio buffer主要用于连续数据采集与缓存功能。IIO buffer模块借助IIO DEVICE字符设备文件与应用程序通信,同时借助iio trigger模块与iio...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,279,834
精华内容 511,933
关键字:

buffer