当你需要在 MySQL 数据库中批量插入数百万条数据时,你就会意识到,逐条发送 INSERT 语句并不是一个可行的方法。

MySQL 文档中有些值得一读的 INSERT 优化技巧。

在这篇文章里,我将概述高效加载数据到 MySQL 数据库的两大技术。

LOAD DATA INFILE

如果你正在寻找提高原始性能的方案,这无疑是你的首选方案。LOAD DATA INFILE 是一个专门为 MySQL 高度优化的语句,它直接将数据从 CSV / TSV 文件插入到表中。

有两种方法可以使用 LOAD DATA INFILE。你可以把数据文件拷贝到服务端数据目录(通常 /var/lib/mysql-files/),并且运行:

LOAD DATA INFILE '/path/to/products.csv' INTO TABLE products;

这个方法相当麻烦,因为你需要访问服务器的文件系统,为数据文件设置合适的权限等。

好消息是,你也能将数据文件存储在客户端,并且使用 LOCAL 关键词:

LOAD DATA LOCAL INFILE '/path/to/products.csv' INTO TABLE products;

在这种情况下,从客户端文件系统中读取文件,将其透明地拷贝到服务端临时目录,然后从该目录导入。总而言之,这几乎与直接从服务器文件系统加载文件一样快,不过,你需要确保服务器启用了此 选项。

LOAD DATA INFILE 有很多可选项,主要与数据文件的结构有关(字段分隔符、附件等)。请浏览 文档 以查看全部内容。

虽然从性能角度考虑, LOAD DATA INFILE 是最佳选项,但是这种方式需要你先将数据以逗号分隔的形式导出到文本文件中。如果你没有这样的文件,你就需要花费额外的资源来创建它们,并且可能会在一定程度上增加应用程序的复杂性。幸运的是,还有一种另外的选择。

扩展的插入语句(Extended inserts)

一个典型的 INSERT SQL 语句是这样的:

INSERT INTO user (id, name) VALUES (1, 'Ben');

extended INSERT 将多条插入记录聚合到一个查询语句中:

INSERT INTO user (id, name) VALUES (1, 'Ben'), (2, 'Bob');

关键在于找到每条语句中要插入的记录的最佳数量。没有一个放之四海而皆准的数字,因此,你需要对数据样本做基准测试,以找到性能收益的最大值,或者在内存使用和性能方面找到最佳折衷。

为了充分利用 extended insert,我们还建议:

  • 使用预处理语句
  • 在事务中运行该语句

基准测试

我要插入 120 万条记录,每条记录由 6 个 混合类型数据组成,平均每条数据约 26 个字节大小。我使用了两种常见的配置进行测试:

  • 客户端和服务端在同一机器上,通过 UNIX 套接字进行通信
  • 客户端和服务端在不同的机器上,通过延迟非常低(小于 0.1 毫秒)的千兆网络进行通信

作为比较的基础,我使用 INSERT ... SELECT 复制了该表,这个操作的性能表现为每秒插入 313,000 条数据。

LOAD DATA INFILE

令我吃惊的是,测试结果证明 LOAD DATA INFILE 比拷贝表更快:

  • LOAD DATA INFILE:每秒 377,000 次插入
  • LOAD DATA LOCAL INFILE 通过网络:每秒 322,000 次插入

这两个数字的差异似乎与从客户端到服务端传输数据的耗时有直接的关系:数据文件的大小为 53 MB,两个基准测试的时间差了 543 ms,这表示传输速度为 780 mbps,接近千兆速度。

这意味着,很有可能,在完全传输文件之前,MySQL 服务器并没有开始处理该文件:因此,插入的速度与客户端和服务端之间的带宽直接相关,如果它们不在同一台机器上,考虑这一点则非常重要。

Extended inserts

我使用 BulkInserter 来测试插入的速度,BulkInserter 是我编写的 开源库 PHP 类的一部分,每个查询最多插入 10,000 条记录:

正如我们所看到的,随着每条查询插入数的增长,插入速度也会迅速提高。与逐条插入速度相比,我们在本地主机上性能提升了 6 倍,在网络主机上性能提升了 17 倍:

  • 在本地主机上每秒插入数量从 40,000 提升至 247,000
  • 在网络主机上每秒插入数量从 1,2000 提升至 201,000

这两种情况都需要每个查询大约 1,000 个插入来达到最大吞吐量。但是每条查询 40 个插入就足以在本地主机上达到 90% 的吞吐量,这可能是一个很好的折衷。还需要注意的是,达到峰值之后,随着每个查询插入数量的增加,性能实际上是会下降。

extended insert 的优势在网络连接的情况下更加明显,因为连续插入的速度取决于你的网络延迟。

max sequential inserts per second ~= 1000 / ping in milliseconds

客户端和服务端之间的延迟越高,你从 extended insert 中获益越多。

结论

不出所料,LOAD DATA INFILE 是在单个连接上提升性能的首选方案。它要求你准备格式正确的文件,如果你必须先生成这个文件,并/或将其传输到数据库服务器,那么在测试插入速度时一定要把这个过程的时间消耗考虑进去。

另一方面,extended insert 不需要临时的文本文件,并且可以达到相当于 LOAD DATA INFILE 65% 的吞吐量,这是非常合理的插入速度。有意思的是,无论是基于网络还是本地主机,聚集多条插入到单个查询总是能得到更好的性能。

如果你决定开始使用 extended insert,一定要先用生产环境的数据样本和一些不同的插入数来测试你的环境,以找出最佳的数值。

在增加单个查询的插入数的时候要小心,因此它可能需要:

  • 在客户端分配更多的内存
  • 增加 MySQL 服务器的 max_allowed_packet 参数配置。

最后,值得一提的是,根据 Percona 的说法,你可以使用并发连接、分区以及多个缓冲池,以获得更好的性能。

基准测试运行在装有 Centos 7 和 MySQL 5.7 的裸服务器上,它的主要硬件配置有 Xeon E3 @3.8 GHz 处理器,32 GB RAM 和 NVMe SSD。MySQL 的基准表使用 InnoBD 存储引擎。

基准测试的源代码保存在 gist 上,结果图保存在 plot.ly 上。

MySQL 最佳实践 —— 高效插入数据的更多相关文章

  1. MySQL · 最佳实践 · 分区表基本类型

    MySQL · 最佳实践 · 分区表基本类型 MySQL分区表概述 随着MySQL越来越流行,Mysql里面的保存的数据也越来越大.在日常的工作中,我们经常遇到一张表里面保存了上亿甚至过十亿的记录.这 ...

  2. paip.python连接mysql最佳实践o4

    paip.python连接mysql最佳实践o4 python连接mysql 还使用了不少时间...,相比php困难多了..麻烦的.. 而php,就容易的多兰.. python标准库没mysql库,只 ...

  3. mysql创建表及插入数据操作步骤及注意要点

    环境:mysql workbench 1.创建新的表,注意,指定要存放的数据库 列名可以加单引号(键盘上1左边的引号),也可以不加 2.插入数据 注意:如果数据是字符型,必须使用单引号或者双引号,如: ...

  4. MySql频繁查询、插入数据

    当我们需要频繁地从数据库查询.插入数据时,可以将这些数据库操作汇集写到同一个类里,作为工具类直接调用. 将数据库的具体信息保存在.properties文件中,用log4j作为日志记录 MySql.ja ...

  5. MySQL创建数据库并插入数据

    启动MySql 启动服务:sudo service mysql start 登陆:mysql -u root 新建数据库 CREATE DATABASE <数据库名>; 在大多数SQL系统 ...

  6. MySQL:JDBC批量插入数据的效率

    平时使用mysql插入.查询数据都没有注意过效率,今天在for循环中使用JDBC插入1000条数据居然等待了一会儿 就来探索一下JDBC的批量插入语句对效率的提高 首先进行建表 create tabl ...

  7. mysql应用存储过程批量插入数据

    --批量插入数据的sql语句 delimiter $$ DROP PROCEDURE IF EXISTS `test.sp_insert_batch` $$ CREATE DEFINER =`root ...

  8. 50个SQL语句(MySQL版) 建表 插入数据

    本学期正在学习数据库,前段时间老师让我们做一下50个经典SQL语句,当时做的比较快,有一些也是百度的,自我感觉理解的不是很透彻. 所以从本篇随笔开始,我将进行50个经典SQL语句的复盘,加深理解. 答 ...

  9. MySql数据库-查询、插入数据时转义函数的使用

    最近在看一部php的基础视频教程,在做案例的时,当通过用户名查询用户信息的时候,先使用了转义函数对客户提交的内容进行过滤之后再交给sql语句进行后续的操作.虽然能看到转义函数本身的作用,但是仍然有一些 ...

随机推荐

  1. Access denied for user '电脑用户名'@'localhost'

    之前没有碰到这个问题,但是这次从gitee上面拉取代码运行,发现存在bug 错误描述 java.sql.SQLException: Access denied for user '10134'@'lo ...

  2. intellij idea的Maven项目运行报程序包找不到的错误

    概括一句话:IDEA的Terminal命令行输入mvn idea:idea或者mvn idea:module , 即可解决 重做过开发环境后在intellij idea中载入java工程,通过mave ...

  3. liunx磁盘挂载操作

    目标: 欲挂载目录:/home欲挂载磁盘:/dev/sdd 1.查看磁盘使用情况 [root@localhost ~]# df -h 文件系统 容量 已用 可用 已用% 挂载点 devtmpfs 47 ...

  4. RTTI之typeid运算符

    1 #include <iostream> 2 #include <cstdlib> 3 #include <ctime> 4 #include <typei ...

  5. Scrum Meeting 2

    Basic Info where:三号教学楼 when:2020/4/25 target: 简要汇报一下已完成任务,下一步计划与遇到的问题 Progress Team Member Position ...

  6. volatile 的使用

    ① 编译器很聪明,会帮我们做些优化,比如: int a; a = 0; // 这句话可以优化掉,不影响 a 的结果 a = 1; ② 有时候编译器会自作聪明,比如: int *p = ioremap( ...

  7. 使用 MegaCLI 检测磁盘状态并更换磁盘

    专栏首页阿dai_linux使用 MegaCLI 检测磁盘状态并更换磁盘 原 10

  8. [OS] 概述&学习资料

    计算机启动 启动自检 初始化启动 启动加载 内核装载 登录 中断 硬件中断 I/O设备 CPU Timer:时间片结束后,发中断给CPU Scheduler:将CPU合理分配任务使用 异常中断 内存: ...

  9. kvm虚拟化安装与部署(2)

    一.虚拟化VT开启确认 KVM 本身也有一些弱点,那就是相比裸金属虚拟化架构的 Xen . VMware ESX 和 HyperV , KVM 是运行在 Linux 内核之上的寄居式虚拟化架构,会消耗 ...

  10. STM32独立看门狗(IWDG)

    造成程序跑飞,只是程序的正常运行状态被打断而进入死循环,从而使单片机控制的系统无法正常工作.看门狗就是一种专门用于检测单片机程序运行状态的硬件结构. STM32内部自带了两个看门狗,独立看门狗(IWD ...