- 43
- 0
- 问题说明:
目前有一个文本格式的json数据,里面数据量约15亿,每一行数据的格式都是固定的,插入前先要将json反序列化。运行环境为:windows server 2016,postgresql 10,Xeon E5506@2.13GHz,16G ddr3 1333,硬盘io上限约120MB/s,服务器板载RAID无法使用,用windows的带区卷将两块硬盘组成一个卷,极限io也就170MB/s,性价比太低就没做,直接使用了单块硬盘。 -
思路和伪代码:
基本思路,遍历json文本,每100万行文本做一次插入。插入时,将100万行文本切割成小的分组用多线程的方式并行插入,每个线程每次都建立一个新的数据库连接并处理一个分组。待100万行文本处理完毕后再继续遍历json。首先进行一些数据库的基本优化:
- 创建数据库和表,将表设置为unlogged
- 开启postgresql的异步提交
# python伪代码 def do_insert(rows): # 每次插入都建立新的连接 conn=psycopg2.connect() cur=conn.cursor() # 遍历rows,进行json反序列化,提取数据并构造sql语句,执行sql语句将rows一次性插入到数据库 for row in rows: v = json.loads(row) insert_sql = "insert into ... values (%s,%s)" % (v[1], v[2]) cur.execute(insert_sql) cur.commit() conn.close() def insert(Rows): # 将Rows切割成100份,获得100个rows,启用n个do_insert线程 rows_list = split_list(Rows, 100) pool = threadpool.ThreadPool(n) requests = threadpool.makeRequest(do_insert, rows_list) [pool.putRequest(req) for req in requests] pool.wait() def main(): # 载入json文本数据源 # 按行读取,每读取100万行数据调用一次insert() with open('import.json','r') as f: index=0 Rows=[] for line in f: Rows.append(line) index += 1 if index % 1000000 == 0: insert(Rows) - 目前尝试了几种参数组合,最终使用的是10个线程,每个线程插入1万条,每100万条做一次多线程批量插入耗时约2min,平均插入速度约8400条/s,跑完15亿条数据大约要2天。
python执行到稳定状态后:占用内存约1G,cpu占用率~30%,cpu线程数持续缓慢上升(似乎没有回收线程)。
总的CPU使用率一直保持在~80%,其中python只占30%,另外有大量的postgres.exe进程,这部分应该占用了较多的cpu。硬盘写io不固定,峰值30M/s、其余时间都是5M/s以下,速度明显不行。 - 初步分析
对每个python语句的执行时间进行统计,发现主要的业务都在do_insert内,也就是具体执行插入操作的线程。不知道跟json.loads有无关系,还要进一步验证。但是感觉python效率以及程序处理逻辑上还存在缺陷,所以没有去进一步优化。
插入线程越多,postgresql进程会越多,这样是否会导致cpu使用不平衡。
此外,是否还有其他可以优化的地方,无论是从语言上,还是处理逻辑上,还是数据库配置上。
- 共 0 条
- 全部回答
-
牛顿莱布尼茨公式 普通会员 1楼
要将10亿级的JSON数据高效地插入PostgreSQL,可以采取以下策略:
- 批量插入:一次性插入一行数据会触发大量的数据库操作,包括网络传输和事务处理。尽量采用批量插入的方式,例如使用COPY命令或者
INSERT INTO ... VALUES (...), (...), ...;语句插入多行数据,减少网络交互和事务提交次数。
sql COPY your_table FROM '/path/to/json/data' (FORMAT 'json');或者使用psycopg2等Python库执行批量插入:
python with open('/path/to/json/file', 'r') as f: data = [json.loads(line) for line in f] psycopg2.extras.execute_batch(cursor, "INSERT INTO your_table VALUES (%s)", [(d,) for d in data], page_size=10000)-
硬件优化:确保服务器有足够的内存,因为PostgreSQL会利用内存进行排序、哈希等操作,增大shared_buffers和work_mem参数能提升性能。
-
索引策略:在导入数据前先禁用所有不必要的索引,待数据导入后再创建或重建索引。插入大量数据时,每次插入都需要更新索引,这会严重影响效率。
-
事务设置:设置合适的事务大小,避免因为事务过大导致wal日志过多或者事务过小导致频繁提交。
-
并发插入:如果可能,考虑并发插入以利用多核CPU。但要注意控制并发数,避免争抢资源反而降低性能。
-
表分区:根据业务需求对表进行分区,可以提高写入和查询效率。
-
检查配置:确保PostgreSQL的其他配置如checkpoint_segments、maintenance_work_mem等也已调整为适合大数据导入的设置。
-
预处理数据:如果数据源允许,尽可能在加载到数据库之前进行预处理和清洗,去除无效或重复数据,减少实际需要插入的数据量。
-
监控系统负载:实时监控服务器的CPU、内存、I/O等资源使用情况,以便及时发现问题并做出调整。
请注意,以上建议可能需要根据具体的应用场景和数据特性进行适当调整。
- 批量插入:一次性插入一行数据会触发大量的数据库操作,包括网络传输和事务处理。尽量采用批量插入的方式,例如使用COPY命令或者
- 扫一扫访问手机版
回答动态

- 神奇的四哥:发布了悬赏问题阿里云幻兽帕鲁服务器更新之后。服务器里面有部分玩家要重新创建角色是怎么回事啊?预计能赚取 0积分收益

- 神奇的四哥:发布了悬赏问题函数计算不同地域的是不能用内网吧?预计能赚取 0积分收益

- 神奇的四哥:发布了悬赏问题ARMS可以创建多个应用嘛?预计能赚取 0积分收益

- 神奇的四哥:发布了悬赏问题在ARMS如何申请加入公测呀?预计能赚取 0积分收益

- 神奇的四哥:发布了悬赏问题前端小程序接入这个arms具体是如何接入监控的,这个init方法在哪里进行添加?预计能赚取 0积分收益

- 神奇的四哥:发布了悬赏问题阿里云幻兽帕鲁服务器刚到期,是不是就不能再导出存档了呢?预计能赚取 0积分收益

- 神奇的四哥:发布了悬赏问题阿里云幻兽帕鲁服务器的游戏版本不兼容 尝试更新怎么解决?预计能赚取 0积分收益

- 神奇的四哥:发布了悬赏问题阿里云幻兽帕鲁服务器服务器升级以后 就链接不上了,怎么办?预计能赚取 0积分收益

- 神奇的四哥:发布了悬赏问题阿里云幻兽帕鲁服务器转移以后服务器进不去了,怎么解决?预计能赚取 0积分收益

- 神奇的四哥:发布了悬赏问题阿里云幻兽帕鲁服务器修改参数后游戏进入不了,是什么情况?预计能赚取 0积分收益
- 回到顶部
- 回到顶部
