预读从字面意义就能理解,就是通过一系列指标,判断先读取某些数据 加载到内存中,从而减少实时IO请求。数据库发展到目前,IO交互的代价还是最高的,特别是传统的机械硬盘下预读能力确实能提供性能。但前提是内存充裕,比如刚预读,数据还没有读取,内存容量不够,就得立即从缓存中淘汰掉,那就等于做了无用的事情。
了解预读能力之前,先回顾一下InnoDB逻辑存储单元主要分为表空间、段、区和页。层级关系为tablespace -> segment -> extent(64个page,1M) -> page(默认16kb)。
innodb引擎对于数据页变更的操作是异步进行的,但对于读请求来说可以使read-time读之外,通过预读方式进行先加载策略。
1.Ahead-read
预读请求是指预取缓冲池中的多个页面的异步I/O请求,以预测这些页面即将出现的需求。请求在一个区段中引入所有页面。InnoDB使用两种预读算法来提高I/O性能:
-
Linear read-ahead(线性预读)是一种基于按顺序访问的缓冲池中的页面来预测可能很快需要哪些页面的技术。通过配置参数innodb_read_ahead_threshold,触发异步读请求所需的顺序页访问次数,来控制InnoDB执行预读操作的时间。在此之前,InnoDB只会在读取当前extent的最后一页时,计算是否对整个下一个extent发出异步预取请求。 例如,如果将该值设置为48,则只有在当前区段中有48个页面被连续访问时,InnoDB才会触发线性预读请求。 -
Random?read-ahead(随机预读)是一种技术,可以根据缓冲池中已经存在的页面预测何时可能需要页面,而不管这些页面的读取顺序如何。如果在缓冲池中发现同一个区段的13个连续页面,InnoDB会异步发出一个请求来预取该区段的剩余页面。通过配置变量innodb_random_read_ahead来控制随机读的。
2.变量和指标
对于预读参数有以下2个指标:
mysql> show variables like '%read_ahead%';
+-----------------------------+-------+
| Variable_name | Value |
+-----------------------------+-------+
| innodb_random_read_ahead | OFF |
| innodb_read_ahead_threshold | 56 |
+-----------------------------+-------+
- innodb_random_read_ahead:开启随机预读技术,优化InnoDB I/O。默认关闭
- innodb_read_ahead_threshold:控制InnoDB用于将页面预取到缓冲池中的线性预读的灵敏度
验证过程:
CREATE TABLE `sbtest1` (
`id` int NOT NULL AUTO_INCREMENT,
`k` int NOT NULL DEFAULT '0',
`c` char(120) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
`pad` char(60) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB
通过sysbench创建一个 1个page 基本包含16kb*1024=16384字节, INT 4字节 ,char按照长度 Id(4字节)+k(4字节)+c(120字节)+ pad(60字节)=188字节 16384/188=88, 一个页平均下来大致88行的数据。 Extent=64个页 * 88 =5632行的数据。
模式数据:
shell> ./sysbench ./lua/oltp_common.lua --mysql-user=root --mysql-password=123456 --mysql-socket=/opt/data8.0/mysql/mysql.sock --mysql-db=sbtest --tables=1 --table-size=6000 --db-driver=mysql --report-interval=10 --threads=10 --time=120 prapare
Initializing worker threads...
Creating table 'sbtest1'...
Inserting 6000 records into 'sbtest1'
Creating a secondary index on 'sbtest1'...
通过status指标:
#初次记录ahead指标
mysql> show global status like '%ahead%';
+---------------------------------------+-------+
| Variable_name | Value |
+---------------------------------------+-------+
| Innodb_buffer_pool_read_ahead_rnd | 387 |
| Innodb_buffer_pool_read_ahead | 0 |
| Innodb_buffer_pool_read_ahead_evicted | 0 |
+---------------------------------------+-------+
#不输出数据
mysql> pager cat > /dev/null
mysql>select * from sbtest1 where id <7000;
mysql> nopager
#记录结果
mysql> show status like '%ahead%';
+---------------------------------------+-------+
| Variable_name | Value |
+---------------------------------------+-------+
| Innodb_buffer_pool_read_ahead_rnd | 487 |
| Innodb_buffer_pool_read_ahead | 0 |
| Innodb_buffer_pool_read_ahead_evicted | 0 |
+---------------------------------------+-------+
Random read-ahead下通过上面6000行的数据大致需要100页的读取。 Linear read-ahead下指标没有变化,统计信息村子问题。
- Innodb_buffer_pool_read_ahead
由预读后台线程读入InnoDB缓冲池的页数。 - Innodb_buffer_pool_read_ahead_rnd:
由InnoDB发起的“随机”预读次数。当查询以随机顺序扫描表的大部分时,就会发生这种情况。 - Innodb_buffer_pool_read_ahead_evicted
由预读后台线程读入InnoDB缓冲池的页面数,这些页面随后在没有被查询访问的情况下被逐出。
SHOW ENGINE INNODB STATUS还显示了预读页面的读取速率,以及这些页面在不被访问的情况下被驱逐的速率。每秒平均数据是基于上次调用SHOW ENGINE INNODB STATUS以来收集的统计数据,显示在SHOW ENGINE INNODB STATUS输出的BUFFER POOL and MEMORY部分。
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 137039872
Dictionary memory allocated 375221
Buffer pool size 8192
Free buffers 6850
Database pages 1338
Old database pages 513
。。。
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 1338, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
3.aio的影响
预读操作是通过aio来进行: InnoDB使用Linux上的异步I/O子系统(native AIO)对数据文件页执行预读和写请求。该行为由innodb_use_native_aio(默认开启)配置选项控制。对于AIO机制下, 磁盘I/O调度算法一般建议使用noop和deadline I/O调度器。
mysql> show variables like '%aio%';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| innodb_use_native_aio | ON |
+-----------------------+-------+
InnoDB在特性提高了大量I/o限制系统的可伸缩性,这些系统通常在show ENGINE INNODB STATUS\G输出中显示许多挂起的读/写。
mysql> SHOW ENGINE INNODB STATUS\G
--------
FILE I/O
--------
Pending normal aio reads: [0, 0, 0, 0] , aio writes: [0, 0, 0, 0] ,
ibuf aio reads:, log i/o's:, sync i/o's:
Pending flushes (fsync) log: 0; buffer pool: 0
1501 OS file reads, 345 OS file writes, 46 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s
aio 线程读取文件是受系统参数控制。特别是运行大量InnoDB I/O线程,比如同一台服务器机器上运行多个这样的实例,可能会超过Linux系统的容量限制。在这种情况下,可能会收到以下错误:
EAGAIN: The specified maxevents exceeds the user's limit of available events.
可以通过在/proc/sys/fs/aio-max-nr中写入更高的限制来解决这个错误。
shell> cat /proc/sys/fs/aio-max-nr
65536
如果操作系统中的异步I/O子系统出现问题导致InnoDB无法启动,或 InnoDB检测到tmpdir位置、tmpfs文件系统和Linux内核不支持tmpfs上的AIO等潜在问题,也可以在启动过程中自动禁用该选项innodb_use_native_aio=0启动服务。
4.总结
结合MySQL使用场景,预读属性无法判断是提高性能,还是降低效率。这种情况下。采取默认就可以。不久的将来,预读功能可以通过数据库AI模块,有效的利用起来。
|