【问题标题】:heave writing to InnoDB大量写入 InnoDB
【发布时间】:2025-12-23 11:40:11
【问题描述】:

我们每天都在构建从推文用户 ID 到该用户发布的推文的推文 ID 列表的映射。我们使用的存储引擎是 Percona xtraDB "5.1.63-rel13.4 Percona Server (GPL), 13.4, Revision 443"

我们对每秒插入行的最大吞吐量不满意。我们使用 xtraDB 处理推文的最大吞吐量约为每秒 6000 到 8000 条推文。 (例如,如果我们必须从头开始重建数据,我们将不得不等待将近一天)

在大多数情况下,我们能够利用全部 twitter 数据(大约每秒 4000 到 5000 条推文)实时地做到这一点。

我们已将应用程序的瓶颈缩小到 MySQL InnoDB 插入。在我们的应用程序中,我们从磁盘读取提要并使用 jackson 解析它(每秒大约 30,000 条推文)。然后,我们的应用程序分批发送推文。对于生成这些推文的作者集,我们将它们划分为 8 个组(使用用户 id 模 8 进行简单划分)。为每个组分配一个表,并分配 1 个线程将数据写入该表。每天大约有 2600 万唯一用户生成这些推文,因此每个表大约有 400 万行。 对于一组用户,我们只使用一个事务进行读取和更新。组大小是运行时可调的。我们尝试了从 8 ~ 64000 的各种大小,我们确定 256 是一个很好的批量大小。

我们表的架构是

CREATE TABLE `2012_07_12_g0` (  `userid` bigint(20) NOT NULL,  `tweetId` longblob,  PRIMARY KEY (`userid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8

其中 tweetId 是推文 ID 长整数的压缩列表,使用 Google snappy 压缩

每个线程使用

Select userid,tweetId from <tablename> where userid IN (....)

解析用户标识以回读数据,线程使用

INSERT INTO <tablename> (userid,tweetId) VALUES (...) ON DUPLICATE KEY UPDATE tweetId=VALUES(tweetId)

用新的 tweetid 更新行。

我们尝试过设置各种 XtraDB 参数

innodb_log_buffer_size = 4M
innodb_flush_log_at_trx_commit = 2
innodb_max_dirty_pages_pct = 80
innodb_flush_method = O_DIRECT
innodb_doublewrite = 0
innodb_use_purge_thread = 1
innodb_thread_concurrency = 32
innodb_write_io_threads = 8
innodb_read_io_threads = 8 
#innodb_io_capacity = 20000 
#innodb_adaptive_flushing = 1
#innodb_flush_neighbor_pages= 0"

所有表每天的表大小约为 8G,InnoDB 有 24GB 可供使用。

我们正在使用:

  • 6 盘(crucial m4 SSD,512 GB,000F 固件)软件 RAID5。
  • Mysql innodb 数据,SSD 分区上的表空间
  • ext4 挂载 noatime,nodiratime,commit=60
  • centos 6.2
  • sun jdk 1.6.30

任何使我们的插入速度更快的提示将不胜感激,谢谢。

【问题讨论】:

  • 你的桌子上有什么索引?是否可以减少索引数量以加快写入速度?
  • 我们只使用主键查询
  • 这是否意味着你只有一个主键索引?我问是因为额外的索引会减慢写入速度,因为每次写入都会重新创建每个索引。
  • 是的,我们没有在 SQL 中声明任何其他索引。
  • 好的,这可能有点没用,但由于使用 RAID5,您会损失一些性能。除非空间有限,否则可能希望迁移到 RAID10。

标签: mysql performance innodb


【解决方案1】:

InnoDB 有 24GB

你的意思是这是 innodb_buffer_pool_size 吗?你没有说你有多少内存,也没有说你正在使用什么 CPU。如果是这样,那么您可能应该使用更大的 innodb_log_buffer_size。你对 innodb_log_file_size 的设置是什么?它应该在 96Mb 左右。

innodb_write_io_threads = 8

ISTR 认为 ext3 与多个写入器存在一些并发问题 - 但我不知道 ext4

您是否尝试过更改 innodb_flush_method?

您使用的是哪个 I/O 调度程序(在没有智能磁盘控制器的情况下,通常截止日期最快,有时是 CFQ)?

关闭 ext4 屏障将有助于提高吞吐量 - 这有点冒险 - 确保您在 JBD2 中启用了校验和。同样设置 innodb_flush_log_at_trx_commit=0 应该会显着增加,但风险更大。

既然您显然不关心以关系格式维护数据,那么您可以考虑使用 noSQL 数据库。

【讨论】:

  • 我们有“Intel(R) Xeon(R) CPU E5630 @ 2.53GHz”,96GB DDR3 内存。我们正在使用截止日期调度程序。是的,你是对的,24GB 我指的是 innodb 缓冲池大小。我已注释掉 innodb_flush_method = O_DIRECT 并将其保留为默认值。我会报告它是如何工作的。
  • 我们的日志文件大小是256MB,日志缓冲区大小是4MB
  • 日志缓冲区应该大一点。
【解决方案2】:

我最初的建议是:

  1. 由于您没有带内存的 RAID 卡,您可能需要注释掉 innodb_flush_method = O_DIRECT 行以允许系统缓存写入
  2. 当您禁用双写入缓冲区时,您还可以将 innodb_flush_log_at_trx_commit 设置为 0,这将比 2 更快
  3. 设置 innodb_log_buffer_size 以覆盖至少一秒的写入(30K 推文大约 12Mb)
  4. 如果您使用二进制日志 - 请确保您有 sync_binlog = 0

在硬件方面,我强烈建议尝试使用至少 256Mb RAM 和电池单元 (BBU) 的 RAID 卡来提高写入速度。市场上有支持 SSD 的 RAID 卡。

希望这会有所帮助。请告诉我进展如何。

【讨论】:

  • 我已注释掉 innodb_flush_method = O_DIRECT 并将其保留为默认值。我会报告它是如何工作的。
  • 谢谢!您是否考虑过更改 innodb_flush_log_at_trx_commit 和 innodb_log_buffer_size ?
  • 我现在的设置是:innodb_log_buffer_size = 4M innodb_flush_log_at_trx_commit = 0
  • 在 innodb_log_buffer_size = 4M innodb_flush_log_at_trx_commit = 0 的情况下,我们仍然以每秒大约 10,000 次插入的速度最高 [link] pastebin.com/raw.php?i=zaVLfUyS 想法?
  • 作为盲目猜测 - 尝试将 innodb_log_buffer_size 设置为 12M。下一步是动态检查 OS、MySQL 和 InnoDB 计数器。让我们从 iostat -dx 3 5 和 vmstat 3 5 开始 - 请让我知道输出