优化Python对MySQL批量插入的效率

本文分享了使用Python的MySQLdb模块优化MySQL批量插入性能的经验,包括多条插入、事务管理和有序插入等策略。同时,解决了由于SQL语句过长导致的连接断开问题,并讨论了事务大小的限制及其对性能的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前测试cobar的效率,因为cobar不支持存储过程,所以需要应用程序插入数据,jdbc不灵活,用Python的MySQLdb模块可以实现。

  开始测试的时候用单条insert语句循环n次,最后commit,结果慢的要死,插一万条用了两分钟,十万条我去吃了个饭回来还在插。十万条用存储过程插单库也用了50多秒。

  从UC的这篇文章学习了一些SQL优化的知识。

  主要有三条:
1、insert的时候尽量多条一起插,不要单条插。这样可以减少日志量,降低日志刷盘数据量和频率,效率提高很多。

这是文章提供的测试对比:

191419430163905.jpg

2、在事务中进行插入。也就是每次部分commit,否则每条insert commit 创建事务的消耗也是不小的。

191419553293001.jpg

3、 数据插入的时候保持有序。比如Innodb用的是B+树索引,对B+树的插入如果是在索引中间就会需要树节点分裂合并,这也会有一定的消耗。

191420095488711.jpg

几种方法综合起来的测试数据如下:

191420251412408.jpg

  可以看到1000万以下数据的优化效果是明显的,但是单合并数据+事务的方式在1000万以上性能会有急剧下降,因为此时已经达到了innodb_buffer的上限,随机插入每次需要大量的磁盘操作,性能下降明显。而有序插入在1000万以上时也表现稳定,因为索引定位方便,不需要频繁对磁盘读写,维持较高性能。

据此修改了Python程序:

 import MySQLdb

 db=MySQLdb.connect(host='127.0.0.1', user='test', passwd='test', port=8066)

 cur=db.cursor()

 cur.execute('use dbtest')

 cur.execute('truncate table tb5')

 for t in range(0,100):

     sql = 'insert into tb5 (id, val) values '

     for i in range(1,100000):
            sql += ' ('+`t*100000+i`+', "tb5EXTRA"),'
     sql += ' ('+`t`+'00000, "tb5EXTRA")'

     cur.execute(sql)

     db.commit()

 cur.close()

 db.close()

共插入了1000万条数据,sublime显示用了333.3s,比之前插入10万条都减少了很多,测试插10万条只需要3s。

191420588766948.png

15个分库每个60十多万条。

  •   过程中有个问题,开始我的sql变量是连接所有插入条目的,但是16节点的cobar在输100万条的时候就连接断开了,而单库直接10条也插不进去,显示mysql连接断开。

191421204694678.png

  这个是sql语句长度限制的问题,在mysql的配置文件中有一个max_allowed_packet = 1M,10万条插入语句已经超过这个限额了,100万条分给16个cobar节点也超过了,所以可以把这个参数调大,或者代码里分段执行sql。

  •   还有一个问题是事务大小的问题,innodb_log_buffer_size参数决定这个,超限的话数据会写入磁盘,从而导致效率下滑,所以事务提交也不能攒得太大。

转载于:https://www.cnblogs.com/hyace/p/4173831.html

### 使用 Python 进行 MySQL 数据库批量插入 为了高效地执行大量数据插入操作,可以采用 `executemany` 方法或者通过构建 SQL 插入语句并一次性提交事务的方式。以下是具体的实现方法: #### 方案一:使用 `executemany` 这种方法适用于较小规模的数据集,在处理大规模数据时可能不是最优的选择。 ```python import pymysql conn = pymysql.connect( host='192.90.5.13', port=3306, user='root', passwd='root123', db='test' ) cur = conn.cursor() data_to_insert = [ ('value1', 'value2'), ('value3', 'value4') ] sql_query = """INSERT INTO table_name (column1, column2) VALUES (%s, %s)""" try: cur.executemany(sql_query, data_to_insert) conn.commit() except Exception as e: print(f"An error occurred: {e}") finally: cur.close() conn.close() ``` 此代码片段展示了如何利用 `executemany` 函数来简化多条记录的同时写入[^4]。 #### 方案二:构建大批次插入语句 对于非常大的数据集(如题目中的 5 千万条),建议先将待插入数据组装成一个较大的 INSERT 语句字符串再执行一次性的插入动作,这样能显著减少网络往返次数从而提高效率。 ```python import pymysql def batch_insert_large_data(data_list): chunk_size = 1000 # 每次处理的数量可以根据实际情况调整 connection_params = { 'host': '192.90.5.13', 'port': 3306, 'user': 'root', 'passwd': 'root123', 'db': 'test' } with pymysql.connect(**connection_params) as conn: try: cursor = conn.cursor() placeholders = ', '.join(['(%s,%s)' for _ in range(len(data_list))]) sql = f'INSERT INTO table_name (col1, col2) VALUES {placeholders}' flattened_values = [] for item in data_list: flattened_values.extend(item[:2]) # 假设每项有两个字段 cursor.execute(sql, tuple(flattened_values)) conn.commit() except Exception as err: print(err) raise finally: cursor.close() if __name__ == '__main__': large_dataset = [('val_a_{}'.format(i), 'val_b_{}'.format(i)) for i in range(5 * 10 ** 7)] # 实际应用中应分批加载数据而不是一次性创建如此庞大的列表 batch_insert_large_data(large_dataset) ``` 上述例子演示了当面对海量数据时的一种更优策略——即通过拼接单个大型 SQL 查询来进行大批量插入[^1]。 #### 注意事项 - 对于超大数据集的操作应当考虑服务器资源消耗以及潜在的风险; - 如果表结构允许的话,还可以探索其他优化手段比如禁用索引重建直到所有数据都已成功载入后再开启它们; - 需要确保有足够的权限去修改目标数据库内的对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值