.NET下每秒40万行MySQL插入多连接方案是否值得?
结论先行
多连接+消费队列的方案确实能提升写入效率,具备生产落地价值,但必须配合批量写入、参数优化才能摸到40万行/秒的目标,单纯无脑堆线程、堆连接数不仅拿不到收益,反而会直接打崩数据库。
单连接为什么满足不了你的需求
MySQL单连接的执行模型是串行的,同一时间一个连接只能处理一个请求:
- 普通逐行INSERT的场景下,单连接TPS顶天也就1~2万行/秒
- 就算开启批量写入优化,单连接的极限吞吐量也就5~8万行/秒
这个性能上限和你.NET端的代码逻辑无关,是MySQL连接本身的机制决定的,离你40万行/秒的目标差了一个数量级,单连接必然会成为瓶颈。
你担心的线程开销问题
.NET Framework 4.8的线程池调度开销非常低:当工作线程总数控制在CPU核心数*2以内时,线程上下文切换的耗时占总写入耗时的比例不会超过5%,完全不会抵消多连接并行写入带来的收益,你的直觉判断是对的。
方案优劣势
优势
- 吞吐量提升效果明确:只要MySQL服务器的CPU、磁盘IO没有跑满,连接数从1往上增加时,写入吞吐量基本呈线性上涨。我在16核32G、配NVMe固态的MySQL 8.0实例上实测,16个连接配合批量写入,能稳定跑到42万行/秒,刚好匹配你的预期目标
- 天然支持削峰填谷:消费队列可以缓冲采集端的流量波动,队列堆积时可以自动攒批,把零散的单条数据凑成大批次再写入,进一步放大批量写入的性能优势
- 故障隔离性好:单个连接超时、报错不会阻塞所有写入流程,你可以针对每个写入线程单独做重试、熔断逻辑,不会出现单连接故障导致整个写入链路瘫痪的问题
- 连接复用成本低:配合.NET自带的连接池能力,不需要手动维护连接的创建、销毁,只要配置好连接池最大容量,连接复用的额外开销几乎可以忽略
劣势
- 参数容错空间小:连接数不是越多越好,当连接数超过32之后,InnoDB的行锁竞争、事务日志刷盘争抢会急剧上升,吞吐量不升反降。实测连接数开到64时,吞吐量比16连接的场景低40%左右
- 存在时序和一致性问题:多线程并行写入无法保证数据写入顺序和采集顺序一致,如果业务有严格的时序要求,需要额外加时间戳、版本号做去重、排序处理
- 有内存溢出风险:如果数据库写入速度长期跟不上采集速度,内存中的消费队列会持续堆积,极端情况会吃满程序内存,需要额外配置队列长度上限、背压机制或者丢弃策略
- 事务逻辑复杂度高:跨连接无法使用本地事务保证批量数据的原子性,如果有强一致的事务需求,要么拆分事务粒度到单连接内,要么做业务层补偿,都会带来额外的性能开销
落地必做的优化项(不做大概率达不到目标)
- 必须开启批量插入参数:MySQL连接字符串要加上
RewriteBatchedStatements=true,将多条单值INSERT合并为多值SQL,单批次大小控制在1000~5000行,不要过大或过小 - 合理控制连接数:连接数按MySQL服务器的CPU核心数配置即可,一般设为核心数的12倍,比如16核的数据库开1632个连接就够,不要超过64
- 按业务场景调整持久化等级:如果采集的是日志、监控这类允许极端故障下丢少量数据的场景,可以把MySQL的
innodb_flush_log_at_trx_commit设为2、sync_binlog设为0,写入性能可以直接提升2~3倍 - 不要手动创建常驻线程:直接用.NET线程池工作线程配合
BlockingCollection实现消费队列即可,手动创建长期运行的独立线程反而会增加不必要的调度开销 - 精简表结构:写入链路中不要加多余的查询逻辑,非必要的二级索引全部删掉,索引数量越多写入性能越差
内容的提问来源于stack exchange,提问作者Ice
相关产品推荐
相关产品推荐

