1. 为什么需要关注批量写入性能当我们需要向PostgreSQL数据库写入大量数据时单条插入的方式效率极其低下。我曾经在一个数据迁移项目中最初使用简单的单条INSERT语句结果导入10万条记录耗时超过15分钟。这种性能表现对于生产环境是完全不可接受的。批量写入技术通过减少网络往返和事务开销来大幅提升性能。根据我的实测合理的批量写入方式可以将同样的10万条记录导入时间缩短到10秒以内。这种性能差异在数据量越大时越明显。PostgreSQL作为企业级关系型数据库提供了多种批量数据加载方式每种方式都有其适用场景和性能特点。我们需要根据数据量大小、数据结构复杂度、硬件配置等因素选择最适合的方案。2. 环境准备与测试数据生成2.1 测试环境配置为了获得可靠的性能对比数据我搭建了以下测试环境服务器AWS EC2 t3.xlarge实例(4 vCPU, 16GB内存)操作系统Ubuntu 20.04 LTSPostgreSQL版本14.5Python版本3.9.7连接驱动psycopg2 2.9.3数据库配置方面我特别调整了以下参数以优化写入性能shared_buffers 4GB work_mem 32MB maintenance_work_mem 1GB wal_level minimal synchronous_commit off2.2 测试数据生成我编写了一个Python脚本来生成测试数据模拟典型的业务场景import random import string def generate_test_data(num_records): data [] for i in range(num_records): record ( i, .join(random.choices(string.ascii_letters, k20)), random.randint(1, 100), random.random() * 1000, random.choice([A, B, C, D]), random.choice([True, False]) ) data.append(record) return data这个函数会生成包含以下字段的记录id: 自增整数name: 随机20字符字符串category_id: 1-100的随机整数price: 0-1000的随机浮点数status: A/B/C/D随机选择is_active: 随机布尔值3. 批量写入方法对比3.1 单条INSERT语句基准测试这是最基础的方法性能也最差def single_insert(conn, data): cur conn.cursor() for record in data: cur.execute( INSERT INTO test_table (id, name, category_id, price, status, is_active) VALUES (%s, %s, %s, %s, %s, %s) , record) conn.commit()测试结果1,000条记录1.2秒10,000条记录12.5秒100,000条记录125秒这种方法的主要问题是每个INSERT语句都需要单独的网络往返和事务处理开销。3.2 使用executemany方法psycopg2提供的executemany方法可以批量执行相同参数的语句def batch_executemany(conn, data): cur conn.cursor() cur.executemany( INSERT INTO test_table (id, name, category_id, price, status, is_active) VALUES (%s, %s, %s, %s, %s, %s) , data) conn.commit()测试结果1,000条记录0.8秒10,000条记录7.2秒100,000条记录72秒虽然比单条INSERT快但性能提升有限因为底层仍然是逐条执行SQL语句。3.3 使用COPY命令PostgreSQL的COPY命令是专门为批量数据加载设计的def copy_from(conn, data): cur conn.cursor() f io.StringIO() for record in data: f.write(\t.join(map(str, record)) \n) f.seek(0) cur.copy_from(f, test_table, sep\t) conn.commit()测试结果1,000条记录0.15秒10,000条记录0.35秒100,000条记录2.1秒COPY命令的性能优势非常明显特别适合大规模数据导入。3.4 使用UNION ALL多值插入这种方法通过一个INSERT语句插入多行数据def multi_value_insert(conn, data, batch_size1000): cur conn.cursor() for i in range(0, len(data), batch_size): batch data[i:ibatch_size] values ,.join( cur.mogrify((%s,%s,%s,%s,%s,%s), row).decode(utf-8) for row in batch ) cur.execute(f INSERT INTO test_table (id, name, category_id, price, status, is_active) VALUES {values} ) conn.commit()测试结果batch_size10001,000条记录0.25秒10,000条记录1.8秒100,000条记录18秒这种方法在中等数据量时表现不错但SQL语句长度会随着批量大小增加而增长。4. 性能对比分析与优化建议4.1 综合性能对比将各种方法在100,000条记录下的表现汇总如下方法耗时(秒)相对性能单条INSERT1251xexecutemany721.7xUNION ALL批量插入187xCOPY命令2.160x4.2 内存使用分析COPY命令虽然最快但需要将所有数据先加载到内存中。对于超大数据集如数千万条记录这可能导致内存问题。解决方案是分批使用COPYdef batch_copy_from(conn, data, batch_size50000): cur conn.cursor() for i in range(0, len(data), batch_size): batch data[i:ibatch_size] f io.StringIO() for record in batch: f.write(\t.join(map(str, record)) \n) f.seek(0) cur.copy_from(f, test_table, sep\t) conn.commit()4.3 事务处理优化默认情况下每个批量操作都在一个事务中执行。对于超大数据集可以考虑将数据分成多个事务提交def batch_copy_with_transactions(conn, data, batch_size50000): cur conn.cursor() for i in range(0, len(data), batch_size): batch data[i:ibatch_size] with conn: f io.StringIO() for record in batch: f.write(\t.join(map(str, record)) \n) f.seek(0) cur.copy_from(f, test_table, sep\t)这样可以避免单个大事务导致的WAL日志膨胀问题。5. 实际应用中的注意事项5.1 数据类型转换问题在使用COPY命令时需要特别注意数据类型的正确转换。我曾经遇到一个案例日期字段格式不正确导致整个导入失败。建议添加类型检查def safe_str(value): if value is None: return \\N # PostgreSQL NULL表示 if isinstance(value, bool): return t if value else f return str(value)5.2 错误处理与重试机制批量操作中如果某条记录出错整个批次可能会失败。实现健壮的错误处理很重要def robust_copy_from(conn, data, max_retries3): for attempt in range(max_retries): try: copy_from(conn, data) break except Exception as e: if attempt max_retries - 1: raise print(fAttempt {attempt1} failed, retrying...) time.sleep(2 ** attempt) # 指数退避5.3 索引与约束的影响在导入大量数据时临时删除索引和外键约束可以显著提高性能。我的标准做法是删除非关键索引禁用外键约束执行数据导入重建索引重新启用约束-- 导入前 DROP INDEX IF EXISTS idx_test_table_name; ALTER TABLE test_table DISABLE TRIGGER ALL; -- 导入后 CREATE INDEX idx_test_table_name ON test_table(name); ALTER TABLE test_table ENABLE TRIGGER ALL;6. 高级技巧与替代方案6.1 使用pg_bulkload扩展对于超大规模数据导入可以考虑使用pg_bulkload扩展。它绕过了常规的SQL处理路径直接操作数据文件性能更高# 需要先安装扩展 sudo apt-get install postgresql-14-pg-bulkload然后使用Python生成符合格式要求的数据文件再调用pg_bulkload工具导入。6.2 并行导入技术对于多核服务器可以结合Python的多进程实现并行导入from multiprocessing import Pool def parallel_import(data, num_processes4): chunk_size len(data) // num_processes chunks [data[i:ichunk_size] for i in range(0, len(data), chunk_size)] with Pool(num_processes) as pool: pool.map(import_chunk, chunks) def import_chunk(chunk): conn psycopg2.connect(...) copy_from(conn, chunk) conn.close()6.3 使用SQLAlchemy批量操作如果项目中使用SQLAlchemy ORM可以使用bulk_insert_mappings方法from sqlalchemy.orm import sessionmaker Session sessionmaker(bindengine) session Session() session.bulk_insert_mappings(TestTable, [ dict(id1, nametest1, ...), dict(id2, nametest2, ...), # ... ]) session.commit()这种方法在ORM环境中提供了不错的性能但仍不及原生COPY命令。
企业数字化 ERP 产品动态
相关推荐
Linux权限模型:UID/EUID/SUID与SGID的进程身份和文件权限位 很多人第一次被这套身份体系绊倒,都是在写一个需要读取受保护配置的小工具时:程序明明用chmod 4755加上了 SUID 位,ls -l里也出现了那个显眼的s,但程序里打印出来的geteuid()依旧是自己,配置文件照样打不开。反过来也有… · 2026/11/3 12:06:15
零基础学Linux:从CentOS安装到Shell脚本与集群管理路线 简介:这份千页规模的 Linux 架构师文档面向希望从零基础走向企业级运维与架构岗位的读者,按基础、进阶、高级三段编排共 23 章,可用于系统梳理知识体系,也可作为日常查阅手册。压缩包内为 1 个 PDF 文件,约 28.82MB&am… · 2026/11/3 12:05:12
激活函数全解析:从梯度消失到端侧量化部署选型 调参调到凌晨,loss 卡在一个数上不动,最后发现问题出在一个默认激活函数上——这种事我遇到过不止一次。激活函数看起来是网络里最不起眼的一行代码,新手往往觉得框架里默认写个 ReLU 就完事了,但真正做过模型调优和端侧部署的人都… · 2026/11/3 12:05:12
UC网盘免登录直链下载技术解析 1. UC网盘免登录下载方案解析 最近在整理资料时发现不少资源都存放在UC网盘上,但每次下载都需要强制登录账号,对于临时下载的用户很不友好。经过多次测试,我总结出一套稳定的免登录直链获取方案,实测有效且不需要任何特殊工具。 … · 2026/11/3 12:54:33
R语言期末复习指南:从数据类型到假设检验的考点梳理 简介:面向R语言初学者与备考复习人群的试题练习文档,围绕描述性统计与推断性统计的区分、名义数据与计量数据等数据类型判定,以及R软件入门操作展开,帮助读者在刷题中夯实基础概念与语法细节。包内共1个docx文件,约96K… · 2026/11/3 12:54:33
具身智能技术前沿与应用趋势解析 1. 项目背景与核心价值 2026年大湾区具身智能论坛(EI-OAHV 2026)作为华南地区最具前瞻性的智能科技盛会,正进入最后筹备冲刺阶段。这个由学术界与产业界联合发起的国际会议,聚焦具身智能(Embodied Intelligence&#x… · 2026/11/3 12:54:33
2026大湾区具身智能论坛前瞻:技术亮点与产业机遇 1. 项目概述:2026大湾区具身智能论坛(EI-OAHV 2026)前瞻距离截稿只剩48小时,而这场智能科技领域的年度盛会也将在4天后正式拉开帷幕。作为国内首个聚焦"具身智能"(Embodied Intelligence)的垂直领… · 2026/11/3 12:54:33
C语言函数库详解:从报错到精通的实用指南 简介:这份PDF面向C语言初学者与需要随时查阅标准库的开发者,系统梳理了C标准库各头文件的功能定位与常用接口,帮助解决函数记不牢、头文件归属不清、查阅手册零散等问题。资源共1个PDF文件,压缩包约967KB,内容按章节组… · 2026/11/3 12:54:33
SQL Server即时通讯数据查询实战:多表关联、索引优化与窗口函数 1. 从一张“QQ查询”需求单说起:这个项目到底在做什么很多人第一次看到“数据库(SQL Server)QQ查询”这个标题,第一反应是“查QQ号?”,其实真正落到开发场景里,它指的是一套围绕即时通讯类业务数… · 2026/11/3 12:53:33
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46