美文网首页
记一次百万数据插入的优化

记一次百万数据插入的优化

作者: lz做过前端 | 来源:发表于2021-12-28 20:41 被阅读0次

说明

环境

  • mysql: docker + mysql(8.0.18、8.0.27)
  • jdk8
  • mac 系统

JDBC 方式插入

statement

  • 见 【cc.gegee.study.week7.practice2.Thread1Statement】
  • 19 秒

预处理

  • 见 【cc.gegee.study.week7.practice2.Thread1PreparedStatement】
  • 22 秒

多线程多连接 + statement

  • 见 【cc.gegee.study.week7.practice2.ThreadNStatement】
  • 21 秒

多线程多连接 + 预处理

  • 见 【cc.gegee.study.week7.practice2.ThreadNPreparedStatement】
  • 25 秒

总结

  • 感觉用不用多线程多连接差不多
  • statement 比 预处理方式要快,可能需要多跑几次才能确定

LOAD DATA 方式插入(mysql版本8.0.18)

  • 参考:https://dev.mysql.com/doc/refman/8.0/en/load-data.html
  • 因为我这边用的是 docker,会比本机应用程序会再慢些
  • 需要--local-infile=1 或者 登录后 SET GLOBAL local_infile = 1
  • bin log 打开时测试(其他参数已经优化过了,见下面的优化过程)
mysql -h 127.0.0.1 -uroot -P 3406 --local-infile=1 -p
SET GLOBAL local_infile = 1;
load data local infile '~/Downloads/order.csv' into table test.`order`
    character set utf8mb4 -- 可选,避免中文乱码问题
    fields terminated by ',' -- 字段分隔符,每个字段(列)以什么字符分隔,默认是 \t
    optionally enclosed by '' -- 文本限定符,每个字段被什么字符包围,默认是空字符
    escaped by '/' -- 转义符,默认是 \
    lines terminated by '\n' -- 记录分隔符,如字段本身也含\n,那么应先去除,否则load data
    ( `user_id`, `status`, `money`) -- 每一行文本按顺序对应的表字段,建议不要省略
    ;
  • 输出如下,8.61 sec
Query OK, 1000000 rows affected, 0 warnings (8.61 sec)
Records: 1000000  Deleted: 0  Skipped: 0  Warnings: 0
  • 关闭 bin log 测试,4.95 sec
Query OK, 1000000 rows affected, 0 warnings (4.95 sec)
Records: 1000000  Deleted: 0  Skipped: 0  Warnings: 0

importTable 方式插入(MySQL Shell mysql版本8.0.27)

## 进入 mysql shell 
mysqlsh
## 进入后 session 连接
shell.connect('mysql://root@localhost:3308')
## 然后输入密码进入
## 导出
util.exportTable("test.`order`", "~/Downloads/order.csv")
## 导入
## The parallel table import utility uses LOAD DATA LOCAL INFILE statements to upload data, 
## so the local_infile system variable must be set to ON on the target server. 
## You can do this by issuing the following statement in SQL mode before running the parallel table import utility
\sql SET GLOBAL local_infile = 1;
util.importTable("~/Downloads/order.csv", {"characterSet": "utf8mb4","schema": "test","table": "order"})
  • 输出如下,用时11.5021 sec
Importing from file '/Users/xuqingbin/Downloads/order.csv' to table `test`.`order` in MySQL Server at localhost:3308 using 1 thread
[Worker000] order.csv: Records: 1000000  Deleted: 0  Skipped: 0  Warnings: 0
100% (19.89 MB / 19.89 MB), 143.13 KB/s
File '/Users/xuqingbin/Downloads/order.csv' (19.89 MB) was imported in 11.5021 sec at 1.73 MB/s
Total rows affected in test.order: Records: 1000000  Deleted: 0  Skipped: 0  Warnings: 0
  • 关闭 bin log 测试,4.8136 sec
Importing from file '/Users/xuqingbin/Downloads/order.csv' to table `test`.`order` in MySQL Server at localhost:3406 using 1 thread
[Worker000] order.csv: Records: 1000000  Deleted: 0  Skipped: 0  Warnings: 0
100% (19.89 MB / 19.89 MB), 4.38 MB/s
File '/Users/xuqingbin/Downloads/order.csv' (19.89 MB) was imported in 4.8136 sec at 4.13 MB/s
Total rows affected in test.order: Records: 1000000  Deleted: 0  Skipped: 0  Warnings: 0
  • 从日志可以看出importTable的方式可以支持多个线程,应该可以调整,以进一步提高导入速度

优化过程

优化思路可以归结为

  • client层优化
    • Connecting
    • Sending query to server
    • combine many small operations into a single large operation、
    • prepare statement
  • server层优化
  • engine引擎层优化
    • undo log
    • engine层内存缓存
    • 数据磁盘刷新
    • redo log
    • bin log
  • 其他
    • innodb_autoinc_lock_mode
    • max_allowed_packet
    • bulk_insert_buffer_size

以下是具体说明

Inserting indexes

  • 开始主键用的是UUID,执行一次需要200多秒,后来改成主键自增
  • 影响性能的分析与总结
    • 表上如果有索引,插入时会建立索引
    • 聚集索引因为存储按索引排序存储,插入中间索引值时,可能会引起页分裂。所以,如果一定要用UUID,批量插入时,可以先排序在插入。
    • 对于其他索引,可以先删除索引,完成导入后再重新建索引

foreign key

  • SET @@foreign_key_checks=0;
  • 关闭外键检查来加速表导入

UNIQUE

  • SET @@unique_checks=0;
  • InnoDB最后通过缓冲区批量写入二级索引记录保证唯一性
  • 我这里没使用,不涉及

columns set default values

  • Insert values explicitly only when the value to be inserted differs from the default
  • This reduces the parsing that MySQL must do and improves the insert speed
  • 即:当插入的列的值和默认值一样时,可以不指定该列,减少Mysql解析列的时间
  • 即:如果某列的值出现的比较多,甚至可以设置为默认值
  • 我这里表比较简单没有设置默认值

change buffer

  • SET autocommit=0;
  • change buffer会影响磁盘IO次数,关闭自动提交

undo log

  • 没找到可以关闭的设置

redo log

bin log

  • 关闭 bin log
    [mysqld]
    skip-log-bin
  • 查看:show variables like '%log_bin%';

max_allowed_packet

  • 给server端发送的数据包的最大值
  • 我这边 statement 第一次执行时出现超过最大值,后修改
[mysqld]
## 100M
max_allowed_packet=104857600

bulk_insert_buffer_size

[mysqld]
## 100M
bulk_insert_buffer_size=104857600

最后

  • 因为我用的是 docker ,在 JDBC 上使用多线程是否有用以及有多大效果还不确定,不知道容器是否有自己的一套,需要后续使用应用程序起mysql服务方式测试
  • 看mysql官网应该还有针对 InnoDB 的具体优化参数,可以进一步优化,后续有时间再研究

相关文章

  • 记一次百万数据插入的优化

    说明 仓库地址:https://github.com/coolmeihuak/geekbang-study/tre...

  • mysql操作大批量的数据

    创建了4张视图 百万数据分页问题 这个语句参考:mysql百万级数据查询优化 百万级数据插入数据库 参考:快速导入...

  • MySQL优化—插入语句的优化

    插入语句如何优化? 插入数据时,影响插入速度的主要是索引、唯一性校验、一次插入数据的条数等。 插入数据的优化,不同...

  • MySQL数据库结构如何优化

    1.数据库结构优化 数据库结构优化的目的: 减少数据冗余 尽量避免数据维护中出现更新,插入和删除异常 插入异常:如...

  • MySQL优化

    数据库SQL优化大总结之 百万级数据库优化方案

  • SpringBoot+ThreadPoolTaskExecuto

    开发目的: 提高百万级数据插入效率。 采取方案: 利用ThreadPoolTaskExecutor多线程批量插入。...

  • day18(MongoDB)

    安装MongoDB 配置启动: 优化警告 插入数据 查询数据 更新数据 删除数据 mongostat mongot...

  • MySql语法(4)—exists和in的区别(explain分

    首先在mysql数据库中新建两张表,并且插入数据。 MySql(准备)—mysql使用存储过程快速插入百万条数据 ...

  • SQL - 基本操作

    插入数据 插入 插入检索数据(查询结果应该是单行单列) mysql 方言 ignore 忽略冲突记录,写入不冲突记...

  • 百万级数据插入mysql

    百万级数据插入 mysql版本:5.7 mysql配置: max_allowed_packet

网友评论

      本文标题:记一次百万数据插入的优化

      本文链接:https://www.haomeiwen.com/subject/ymmdqrtx.html