MySQL 最佳实践 —— 高效插入数据
当你需要在 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 最佳实践 —— 高效插入数据的更多相关文章
- MySQL · 最佳实践 · 分区表基本类型
MySQL · 最佳实践 · 分区表基本类型 MySQL分区表概述 随着MySQL越来越流行,Mysql里面的保存的数据也越来越大.在日常的工作中,我们经常遇到一张表里面保存了上亿甚至过十亿的记录.这 ...
- paip.python连接mysql最佳实践o4
paip.python连接mysql最佳实践o4 python连接mysql 还使用了不少时间...,相比php困难多了..麻烦的.. 而php,就容易的多兰.. python标准库没mysql库,只 ...
- mysql创建表及插入数据操作步骤及注意要点
环境:mysql workbench 1.创建新的表,注意,指定要存放的数据库 列名可以加单引号(键盘上1左边的引号),也可以不加 2.插入数据 注意:如果数据是字符型,必须使用单引号或者双引号,如: ...
- MySql频繁查询、插入数据
当我们需要频繁地从数据库查询.插入数据时,可以将这些数据库操作汇集写到同一个类里,作为工具类直接调用. 将数据库的具体信息保存在.properties文件中,用log4j作为日志记录 MySql.ja ...
- MySQL创建数据库并插入数据
启动MySql 启动服务:sudo service mysql start 登陆:mysql -u root 新建数据库 CREATE DATABASE <数据库名>; 在大多数SQL系统 ...
- MySQL:JDBC批量插入数据的效率
平时使用mysql插入.查询数据都没有注意过效率,今天在for循环中使用JDBC插入1000条数据居然等待了一会儿 就来探索一下JDBC的批量插入语句对效率的提高 首先进行建表 create tabl ...
- mysql应用存储过程批量插入数据
--批量插入数据的sql语句 delimiter $$ DROP PROCEDURE IF EXISTS `test.sp_insert_batch` $$ CREATE DEFINER =`root ...
- 50个SQL语句(MySQL版) 建表 插入数据
本学期正在学习数据库,前段时间老师让我们做一下50个经典SQL语句,当时做的比较快,有一些也是百度的,自我感觉理解的不是很透彻. 所以从本篇随笔开始,我将进行50个经典SQL语句的复盘,加深理解. 答 ...
- MySql数据库-查询、插入数据时转义函数的使用
最近在看一部php的基础视频教程,在做案例的时,当通过用户名查询用户信息的时候,先使用了转义函数对客户提交的内容进行过滤之后再交给sql语句进行后续的操作.虽然能看到转义函数本身的作用,但是仍然有一些 ...
随机推荐
- axios提交表单
后端使用@RequestBody接收jsons数据 因为后端接收json数据,所以前端也要发送json 项目的前端是使用layui的数据表单 案例方法 方法一:JSON字符串 提交的数据格式 {&qu ...
- json的解析和生成
相比于xml,json的主要特点是数据小,解析速度快,但是描述性差. java中常见的json解析方法有Gson.Jackson.JSON.simple. 从解析速度上来看,Gson适合解析小数据量, ...
- Jedis基础详解
Jedis 使用Java来操作Redis 什么是Jedis 是Redis官方推荐的Java操作Redis中间件, 如果你要使用Java操作Redis, 那么就该对jedis熟悉 测试 导入对应的依赖 ...
- Cmder右键配置
Cmder右键配置 每次用camder手动一层一层的进入目标文件夹,是一件很麻烦的事儿.所以,将camder添加到系统右键菜单是个很好地解决方法. 1.把 Cmder 加到环境变量 把Cmder.ex ...
- Raspberry PI 4B 安装和配置 Raspbian
做记录,以备之后需要,待完成中 目录 做记录,以备之后需要,待完成中 下载镜像和安装程序 ssh 远程访问 下载镜像和安装程序 Raspbian: installer: ssh 远程访问 开启ssh ...
- 阿里面试官用HashMap把我问倒了
本人是一名大三学生,最近在找暑期实习,其中也面试过两次阿里,一次菜鸟网络部门.一次网商银行部门,当然我都失败了,同时也让我印象很深刻,因此记录了其中一些面试心得,我觉得这个问题很值得分享,因此分享给大 ...
- Python运算符 - Python零基础入门教程
目录 一.算术运算符 二.赋值运算符 三.比较运算符 四.运算符的优先等级 五.重点总结 六.猜你喜欢 零基础 Python 学习路线推荐 : Python 学习目录 >> Python ...
- 对Linux系统内核版本稳定性测试介绍
对Linux系统内核版本稳定性测试介绍 在对 Linux 内核版本稳定性的测试中,需要明确地声明并证明为什么版本是稳定的或者是不稳定的. 然而还没有被证明和证实当前现有的系统范围内的压力测试可以测试 ...
- cat ~/.bash_history
7.history命令 history命令用于显示历史执行过的命令,格式为"history [-c]". history命令应该是作者最喜欢的命令.执行history命令能显示出当 ...
- 攻防世界(七)ics-06
攻防世界系列:ics-06 1.打开题目 很LiuPi的样子,根据题目提示点开报表中心(其他的点开没什么变化) 注意到URL中有?id=1,测试其他数值无果 3.抓包对id爆破 设置1-10000,爆 ...