【发布时间】:2021-09-23 10:58:57
【问题描述】:
环境
- AMD 锐龙 9 5950X,128GB 3200MHz 双通道
- 具有 3GB/s+ 读写速度的数据中心 NVMe SSD
- MariaDB 10.6.3 x64
- Windows Server 2019(在 Debian 上同样的问题)
- 专用机器,没有其他任务在运行
my.ini
[mysqld]
default-storage-engine=INNODB
log-output=NONE
general-log=0
general_log_file="mariadb.log"
slow-query-log=0
query_cache_type=OFF
query_cache_size=0
innodb_buffer_pool_size=64G
DDL
CREATE TABLE testinnodb
(
a INTEGER NOT NULL, b INTEGER NOT NULL, c INTEGER NOT NULL,
i FLOAT NOT NULL, j FLOAT NOT NULL, k FLOAT NOT NULL,
x CHAR(20) NOT NULL, y CHAR(20) NOT NULL, z CHAR(20) NOT NULL
) ENGINE=InnoDB;
MyISAM 和 Memory 的架构相同。
表中填充了 1000 万行随机数据,生成的数据大小:
InnoDB: 1.0 GB
MyISAM: 810 MB
内存: 867 MB
SQL
SELECT * FROM testinnodb WHERE c=1;
SELECT * FROM testmyisam WHERE c=1;
SELECT * FROM testmemory WHERE c=1;
InnoDB: 2.4s !!!
MyISAM: 0.3s
内存: 0.2s
查询运行多次,但性能保持不变。 EXPLAIN 为所有三个查询(简单、使用 WHERE)提供相同的输出。
这显然不是 I/O 问题,考虑到 MyISAM 和 Memory 的硬件和性能对比。
64GB 的缓冲池也足以在内存中保存所有该表。
数据必须在缓冲池中,因为禁用innodb_buffer_pool_load_at_startup,查询将在第一次运行时需要 4.2s,然后在后续运行中需要 2.4s。innodb_buffer_pool_bytes_data 在第一次运行后将增长超过 1GB,所以看起来整个数据实际上都在缓冲池中。innodb_buffer_pool_read_requests 在每次执行时确实增加了大约 10M。
为什么使用 InnoDB 从缓冲池(即 RAM)读取数据比使用 MyISAM(即从 SSD)读取相同数据慢 10 倍?
我需要帮助了解发生了什么。这肯定不对吧?我尝试过使用 DB 配置(例如 innodb_old_blocks_time=0、innodb_read_io_threads=32 和 innodb_write_io_threads=32),但这实际上并没有改变。
我知道使用 INDEX 会有所改善,但这不是重点。
如果您需要一些用于调试的状态变量,请告诉我,我是 InnoDB 的新手,所以我不确定在这里发布什么相关。
SHOW ENGINE InnoDB STATUS; 启动并查询 InnoDB 表两次后的输出
=====================================
Per second averages calculated from the last 50 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 0 srv_active, 0 srv_shutdown, 50 srv_idle
srv_master_thread log flush and writes: 50
----------
SEMAPHORES
----------
------------
TRANSACTIONS
------------
Trx id counter 20001586
Purge done for trx's n:o < 20001583 undo n:o < 0 state: running
History list length 14
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION (000002727BD64108), not started
0 lock struct(s), heap size 1128, 0 row lock(s)
--------
FILE I/O
--------
Pending flushes (fsync) log: 0; buffer pool: 0
71726 OS file reads, 2 OS file writes, 2 OS fsyncs
0.00 reads/s, 16413 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
insert 0, delete mark 0, delete 0
discarded operations:
insert 0, delete mark 0, delete 0
0.00 hash searches/s, 0.00 non-hash searches/s
---
LOG
---
Log sequence number 8881303100
Log flushed up to 8881303100
Pages flushed up to 8881303100
Last checkpoint at 8881303088
0 pending log flushes, 0 pending chkp writes
4 log i/o's done, 0.08 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 68753031168
Dictionary memory allocated 424846000
Buffer pool size 4147712
Free buffers 4075870
Database pages 71842
Old database pages 26539
Modified db pages 0
Percent of dirty pages(LRU & free pages): 0.000
Max dirty pages percent: 90.000
Pending reads 0
Pending writes: LRU 0, flush list 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 71711, created 131, written 0
1434.19 reads/s, 2.62 creates/s, 0.00 writes/s
Buffer pool hit rate 996 / 1000, young-making rate 0 / 1000 not 0 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 71842, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
--------------
ROW OPERATIONS
--------------
0 read views open inside InnoDB
Process ID=0, Main thread ID=0, state: sleeping
Number of rows inserted 0, updated 0, deleted 0, read 20000000
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 399992.00 reads/s
Number of system rows inserted 0, updated 0, deleted 0, read 0
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================
【问题讨论】:
-
这不是答案,但我猜您的 MyISAM 表也在 RAM 中,因为 MyISAM 通过文件系统缓存使用缓冲 I/O。我不知道有什么方法可以禁用它以获得真正的 I/O 测试。所以 MyISAM 只对行数据进行顺序读取。而 InnoDB 将表存储为聚集索引,因此即使进行表扫描,它也必须遍历该 B 树。 MyISAM 在进行表扫描方面更胜一筹,这一直是正确的。
-
@BillKarwin 感谢您的洞察力!即使在 B 树结构中,扫描 1GB 数据需要 2.4 秒,这对于地球上最快的(单线程)CPU 之一来说听起来非常慢。但我想我必须接受它。在这种情况下,是否有一些技巧可以在一定程度上提高性能?调整 B-tree 配置或让 InnoDB 在内存中以更有利的格式或其他方式布局数据的副本?
-
据我所知。 InnoDB 记录基本上存储为一个复杂的链表网络,因此进行表扫描必须在许多页面上从一个记录跳到另一个记录。每个记录链接到下一个(和上一个)记录。这些页面在磁盘上不一定是连续的,当页面加载到缓冲池中时更是如此。 InnoDB 显然没有针对表扫描进行优化,它针对类似 CRUD 的查询模式进行了优化。
-
如果你想了解有关 InnoDB 记录和页面如何存储的所有信息,这位开发人员对其进行了逆向工程并发布了一系列描述每种数据结构的博客:blog.jcole.us/innodb 他还提供了转储数据的工具InnoDB 结构的内容。
-
请不要交叉发帖。
标签: mysql database mariadb innodb bufferpool