账号密码登录
微信安全登录
微信扫描二维码登录

登录后绑定QQ、微信即可实现信息互通

手机验证码登录
找回密码返回
邮箱找回 手机找回
注册账号返回
其他登录方式
分享
  • 收藏
    X
    如何充分利用单台服务器的性能将10亿级的json数据尽可能高效的插入postgresql?
    43
    0
    1. 问题说明:
      目前有一个文本格式的json数据,里面数据量约15亿,每一行数据的格式都是固定的,插入前先要将json反序列化。运行环境为:windows server 2016,postgresql 10,Xeon E5506@2.13GHz,16G ddr3 1333,硬盘io上限约120MB/s,服务器板载RAID无法使用,用windows的带区卷将两块硬盘组成一个卷,极限io也就170MB/s,性价比太低就没做,直接使用了单块硬盘。
    2. 思路和伪代码:
      基本思路,遍历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)
    3. 目前尝试了几种参数组合,最终使用的是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以下,速度明显不行。
    4. 初步分析
      对每个python语句的执行时间进行统计,发现主要的业务都在do_insert内,也就是具体执行插入操作的线程。不知道跟json.loads有无关系,还要进一步验证。但是感觉python效率以及程序处理逻辑上还存在缺陷,所以没有去进一步优化。
      插入线程越多,postgresql进程会越多,这样是否会导致cpu使用不平衡。
      此外,是否还有其他可以优化的地方,无论是从语言上,还是处理逻辑上,还是数据库配置上。
    0
    打赏
    收藏
    点击回答
        全部回答
    • 0
    • 牛顿莱布尼茨公式 普通会员 1楼

      要将10亿级的JSON数据高效地插入PostgreSQL,可以采取以下策略:

      1. 批量插入:一次性插入一行数据会触发大量的数据库操作,包括网络传输和事务处理。尽量采用批量插入的方式,例如使用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)

      1. 硬件优化:确保服务器有足够的内存,因为PostgreSQL会利用内存进行排序、哈希等操作,增大shared_buffers和work_mem参数能提升性能。

      2. 索引策略:在导入数据前先禁用所有不必要的索引,待数据导入后再创建或重建索引。插入大量数据时,每次插入都需要更新索引,这会严重影响效率。

      3. 事务设置:设置合适的事务大小,避免因为事务过大导致wal日志过多或者事务过小导致频繁提交。

      4. 并发插入:如果可能,考虑并发插入以利用多核CPU。但要注意控制并发数,避免争抢资源反而降低性能。

      5. 表分区:根据业务需求对表进行分区,可以提高写入和查询效率。

      6. 检查配置:确保PostgreSQL的其他配置如checkpoint_segments、maintenance_work_mem等也已调整为适合大数据导入的设置。

      7. 预处理数据:如果数据源允许,尽可能在加载到数据库之前进行预处理和清洗,去除无效或重复数据,减少实际需要插入的数据量。

      8. 监控系统负载:实时监控服务器的CPU、内存、I/O等资源使用情况,以便及时发现问题并做出调整。

      请注意,以上建议可能需要根据具体的应用场景和数据特性进行适当调整。

    更多回答
    扫一扫访问手机版
    • 回到顶部
    • 回到顶部