鍍金池/ 問答/人工智能  Linux  數(shù)據(jù)庫/ 如何充分利用單臺(tái)服務(wù)器的性能將10億級(jí)的json數(shù)據(jù)盡可能高效的插入postgr

如何充分利用單臺(tái)服務(wù)器的性能將10億級(jí)的json數(shù)據(jù)盡可能高效的插入postgresql?

  1. 問題說明:
    目前有一個(gè)文本格式的json數(shù)據(jù),里面數(shù)據(jù)量約15億,每一行數(shù)據(jù)的格式都是固定的,插入前先要將json反序列化。運(yùn)行環(huán)境為:windows server 2016,postgresql 10,Xeon E5506@2.13GHz,16G ddr3 1333,硬盤io上限約120MB/s,服務(wù)器板載RAID無法使用,用windows的帶區(qū)卷將兩塊硬盤組成一個(gè)卷,極限io也就170MB/s,性價(jià)比太低就沒做,直接使用了單塊硬盤。
  2. 思路和偽代碼:
    基本思路,遍歷json文本,每100萬行文本做一次插入。插入時(shí),將100萬行文本切割成小的分組用多線程的方式并行插入,每個(gè)線程每次都建立一個(gè)新的數(shù)據(jù)庫連接并處理一個(gè)分組。待100萬行文本處理完畢后再繼續(xù)遍歷json。

    首先進(jìn)行一些數(shù)據(jù)庫的基本優(yōu)化:

    • 創(chuàng)建數(shù)據(jù)庫和表,將表設(shè)置為unlogged
    • 開啟postgresql的異步提交
    # python偽代碼
    
    def do_insert(rows):
       # 每次插入都建立新的連接
       conn=psycopg2.connect()
       cur=conn.cursor()
       # 遍歷rows,進(jìn)行json反序列化,提取數(shù)據(jù)并構(gòu)造sql語句,執(zhí)行sql語句將rows一次性插入到數(shù)據(jù)庫
       
       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個(gè)rows,啟用n個(gè)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文本數(shù)據(jù)源
       # 按行讀取,每讀取100萬行數(shù)據(jù)調(diào)用一次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. 目前嘗試了幾種參數(shù)組合,最終使用的是10個(gè)線程,每個(gè)線程插入1萬條,每100萬條做一次多線程批量插入耗時(shí)約2min,平均插入速度約8400條/s,跑完15億條數(shù)據(jù)大約要2天。
    python執(zhí)行到穩(wěn)定狀態(tài)后:占用內(nèi)存約1G,cpu占用率~30%,cpu線程數(shù)持續(xù)緩慢上升(似乎沒有回收線程)。
    總的CPU使用率一直保持在~80%,其中python只占30%,另外有大量的postgres.exe進(jìn)程,這部分應(yīng)該占用了較多的cpu。硬盤寫io不固定,峰值30M/s、其余時(shí)間都是5M/s以下,速度明顯不行。
  4. 初步分析
    對每個(gè)python語句的執(zhí)行時(shí)間進(jìn)行統(tǒng)計(jì),發(fā)現(xiàn)主要的業(yè)務(wù)都在do_insert內(nèi),也就是具體執(zhí)行插入操作的線程。不知道跟json.loads有無關(guān)系,還要進(jìn)一步驗(yàn)證。但是感覺python效率以及程序處理邏輯上還存在缺陷,所以沒有去進(jìn)一步優(yōu)化。
    插入線程越多,postgresql進(jìn)程會(huì)越多,這樣是否會(huì)導(dǎo)致cpu使用不平衡。
    此外,是否還有其他可以優(yōu)化的地方,無論是從語言上,還是處理邏輯上,還是數(shù)據(jù)庫配置上。
回答
編輯回答
維她命

import.json文件有多大?一些建議:

1.數(shù)據(jù)庫參數(shù)需要優(yōu)化
    shared_buffers, max_connections, maintenance_work_mem, effective_cache_size, sync, commit_delay , commit_siblings ,checkpoint_segments, wal_buffers, wal_writer_delay,
2. 是不是可以先處理下文件,按照COPY的文件格式格式化好,再用cppy命令導(dǎo)入,或者外部表,但處理文件也會(huì)稍微麻煩些
2018年7月6日 23:16