redis隊列里每天有超過一億條數據,使用python如何高效的將其寫入sql資料庫?有什麼解決方案?

使用python拉redis隊列里的數據,如何確保完整高效不阻塞的把每天上億條的數據寫入mysql/oracle資料庫?

拉取數據的client端與入庫的server端之間使用flume聚合數據。但也有可能直接從client端往server端傳數據。


一天上億,按10小時摺合計算,每小時千萬,每秒3000qps,的確有點多,外部組件來說的話,Redis應該沒問題,MySQL需要小心下性能是否足夠,是否需要配置集群,是否需要優化表結構等等。

這裡有一個重要的假設,要看是否滿足:

數據可以亂序寫入,也就是說,寫入MySQL的順序與入隊的順序可以不同。如果不可以的話,這個過程不適用負載均衡,需要進行進一步的優化

用Python的話,按300qps的處理能力來估計,至少需要10個進程,每個進程使用一條到redis的連接,再使用協程或者多線程並發訪問MySQL(因為MySQL也是多線程的模型,單次操作延時高但是並發度也比較高)。Redis單線程,MySQL多線程,這中間有個單到多的關係,所以需要一個帶隊列的線程池(或者協程池,因為MySQL連接數的限制,最好不要不限制協程並發量)

主線程建立到Redis的連接,線程池或者預創建的協程創建到MySQL的連接,每個線程或者協程創建一個。可以使用threading.Queue進行同步(協程應該也有相應的組件),隊列要設置最大長度,防止後端MySQL性能不足的時候隊列長度無限增加,引起負載不均衡或者內存使用過量的問題。主線程使用BRPOP命令從Redis中讀取數據,這個指令在隊列為空的時候會阻塞,並且一個數據只會被取到一次,到數據後放進Queue中,然後馬上開始下一次BRPOP。線程池中同樣從Queue Pop數據,對每一個Pop到的數據調用MySQL語句寫入。

這裡還有一些進一步優化的空間:

首先我們有一個問題,就是取走隊列的這個過程是不可靠的,如果程序崩潰了,正在處理的一些數據可能會永久丟失。可以為每個進程創建一個獨立的子隊列,然後進程改用BRPOPLPUSH來取走數據,並同時將數據放進自己的隊列中,然後在真正處理完之後再從列表中刪除(使用LREM)。這樣如果發生了程序崩潰之類的情況,可以將子隊列中尚未移除的元素重新放回原始隊列中重新處理。要注意這可能會導致相同的數據被寫入兩次,需要評估是否有危害,或者哪一種情況的危害更大。

在Redis讀取的過程中,我們每條命令等待返回結果,等到返回結果之後才發送下一條命令,下一條命令發送到Redis還需要時間,實際上每次請求浪費了至少一半RTT的時間在等待上,我們可以使用pipelining技術提高效率,直接連續發送兩個BRPOP命令,在每次收到一個新的響應的時候追加一個BRPOP命令,這樣使用在pipeline裡面保持一個BRPOP命令,在數據進的很快的時候可以提高吞吐量。如果到Redis的延遲比較大的話需要考慮這個優化,甚至增加pipeling的數量。

通常比起一條INSERT寫入一條數據來說,一條INSERT寫入多條數據更加有效率。如果多次MySQL寫入可以合併的話,可以在主線程當中先將短時間內收到的多條數據打包,然後一起送給線程池裡的線程,讓線程池一次寫入多條數據。

Flume這個中間組件我沒有用過,不清楚特性。


最近幾天做過MySQL 和MongoDB 的insert 測試,數據量也是在幾千萬到幾十億條記錄。

首先,每天上億條記錄寫入MySQ/Oracle 可以做到,區域網內,主流中端配置的機器,無優化。

  • 單線程寫入MySQL 可以達到 幾千條記錄每秒,多線程可以提到到1w 以上;(使用pyMySQLdb包 cur.execute(sql, params)方法寫入數據,每次插入多條和一條記錄速度差別不大)
  • Mongodb 的寫入性能更佳,多線程寫入記錄可以達到3W/s 以上(使用bulk insert() 不要用insert_one() 或者insert_many())。(以上都是未建立索引)

所以你的需求在系統不是重載的時候不是問題。

如果你需要用MySQL 作為存儲,可以考慮幾點優化速度:

  1. 使用memcached plugin 可以提高逐條插入速度
  2. 將記錄dump 為有格式的文件,在系統空閑時,用 LOAD DATA INFILE (MySQL :: MySQL 5.7 Reference Manual :: 14.2.6 LOAD DATA INFILE Syntax) 命令載入文件,批量插入,速度為逐條插入速度的20+倍。


每秒3000次寫入,資料庫伺服器配置好點再優化下資料庫就可以了


謝邀。

我只用過百萬級的數據架構而已。

以我粗淺的見識和經歷,堵塞多半都是發生在並發的時候鎖定數據造成的。你檢查一下業務邏輯會不會發生並發鎖同一個位置?

如果是我來完成這個任務,可能我會採購一種消息中間件(比如IBM的MQ)來處理數據寫入的隊列緩衝。(更多的技術細節我就不知道了,MQ我也沒用過,只是聽IBM來投標的時候提了一下。)

答得不好強答的,期待大神出來真正解惑。


抱歉確實沒遇到過這種情況。

這麼多行任何一個資料庫都是挑戰。

顯然瓶頸在IO和MySQL上。Hadoop試試。不樂觀。

最好在redis這邊先清洗一下。

這個數量級的產品,理應選擇更理想的架構,nosql,普通話業務級產品已經不足以支持了。


既然是數據入庫,肯定是為了更好的查詢。

而寫操作肯定會造成表鎖。所以無阻塞肯定是個偽命題。

如果能容忍先寫入,寫入完畢之後再查詢。而且又是用的FLUME,為什麼不嘗試下直接LOAD FILE 呢,非常高效的命令。

如果我來做這個事情的話,拋棄掉REDIS, FLUME直接按分鐘落地到本地文件,單文件10萬級別的數據量LOAD進去也就是幾秒鐘的事情。再把主從的讀寫分離做好,性能完全不是問題。


可以考慮在redis取數據到寫入的過程中加入支持持久化的消息中間件,可以採用kafka之類。鑒於數據量不是很少且增長速度不慢,如果不是對外提供實時查詢的話可以考慮寫入hbase,一些查詢統計可以用hive來做。一般redis過來的數據可能還是需要清洗的,那麼寫入的操作可以讓storm來做,推薦jstorm更好用。

當然一般這套流程用來處理10w以上TPS場景時,性價比才超高。


單從性能上來說 只要你的伺服器配置主流 程序合理 ,MySQL MSSQL Oracle都沒問題,關鍵在於插入之後怎麼辦? 這麼大的數據量 如何查詢 加工?

是否可以從業務層面考慮數據分層?

建立查詢庫專門查詢數據?


不請自來,希望建議對您有幫助。

建議寫入csv先,比如多個進程讀你的redis隊列,然後寫入多個csv中。csv要設計結構(其實也是一種簡單原始的資料庫),最好存儲在ssd硬碟甚至矩陣上。進程的多少依賴於你讀取redis和寫csv速度的平衡,比如讀取速度是寫入速度的10倍,可以考慮5-9個進程即可。

考慮csv的另外一個原因是每天一億的數據量一般不適合使用mysql(master/slave或cluster都不合適)了,不管只用來數據存儲還是查詢,如果良好csv結構可以支撐你的數據存儲(這個絕對沒問題)以及查詢(這個要看你的查詢分析複雜度了),就用csv吧,其索引等都可以通過文件夾和文件名實現。如果不能支撐,csv作為可靠數據存儲的同時,再轉為大數據的資料庫方式,比較典型得就是hadoop stack上的基於HDFS的分散式資料庫系統(關係和非關係的都有),直接存hdfs上,通過spark和類似的平台(據說apache HAWQ速度更快,現在剛出2.0)做查詢分析也可以。非hadoop stack的也有很多,我不熟悉,不說了。

另外就是前面那個從redis到csv的讀寫進程,我聽說過有開源的還有部分mq可以實現,沒用過,因為不難,所以我們用java和node都寫過。

當然,我說的您只能參考,因為團隊技術深度和項目特性都是需要綜合考慮的。


是每天新增1億條數據還是更新?前者的話三個月可就100億條數據了,使用傳統資料庫到時插入還是查詢都會巨慢的吧。我沒試過。

使用mongo,100個進程同時寫入,並且不用batch,共寫入1億條,qps在50000以上。當然機器性能比較好,128g內存,24核,SSD。

沒有測過mysql,雖說mongo對寫入有優勢,但也不應該差距很大吧。

所以我覺得,如果不要求嚴格的順序寫入,那麼應對每秒幾千的數據量完全不是問題。問題在於寫入的時候是否需要保證比較高的查詢性能。


感覺Hadoop 可以


考慮到處理線程掛了之後,導致數據丟失,為了解決這個,還不失性能,我給出的方案如下:

在搭建一個單機redis作為輔助,按照之前網友給出的每個線程300qps的速度設計,那可以在輔助redis建立10個隊列。啟動一個進程從生產環境redis讀取數據然後順序放進輔助的10個隊列里,在啟動10個進程或者線程或者協程建立和sql的鏈接,負責從輔助的10個隊列里取數據插進mysql,一條一條處理,這樣即使處理進程掛了,也不會擔心數據丟失。


單純答這個問題本身,不發散到更遠的地方的話,先導文本文件,然後一次性導入即可。

一億看著多,但是換個看法,G級別而已。對於db來說小意思。


不考慮資料庫的瓶頸,如果數據是無序的,單從入庫的優化來說可以多條數據合併成1條SQL執行或者多條數據事物提交,性能都會比單條提交好很多。


每天上億,如果沒有匯總,沒有做水平拆表,考慮換存儲方案吧,mysql早晚扛不住


類似情況我遇到過,我們數據量比LZ大的多,分表並使用多進程批量寫入,mysql抗3000綽綽有餘。要注意的就是靈劍提到的,程序掛了你是選擇丟數據還是數據重複,要做到不丟不重挺難的,而且損失性能。代碼如何寫靈劍寫的挺詳細了。

flume傳輸數據比較靠譜,不知道樓主的問題何在


1.建三個主線程,每個主縣城下可以有若干讀寫線程,其中讀或者檢索線程的優先順序要高於寫入線程,每個線程負責一個資料庫的操作,線程1的庫是個小型庫,主要存儲按照時間序列對數據進行分配的結果,就是這個時間序列下的數據是存入了2庫還是3庫。

線程1負責將數據根據時間序列進行分塊,按照時間順序將接收的數據進行分配,並將分配序列的結果存入主內存。

2庫為緩存庫,3庫為主存庫。數據會優先寫入3庫,如果3庫寫入速度小的話,則同時對2庫進行寫入。每天繁忙時段結束後,從2庫轉入3庫。

讀取需求產生的時候,線程1根據時間序列檢索出儲存在2庫還是3庫,然後轉交給對應線程進行讀取,同時將寫入轉到另一個庫。

如果有讀取請求,檢索分配結果後,按照結果將請求轉給線程2或3,同時將數據寫入非讀取操作的一個資料庫。

2.購買若干pcie介面的ssd組建兩個raid,根據數據情況可以選擇一個壓縮型主控的產品,建立一個主存一個緩衝庫。盡量消除硬體瓶頸。如果一台機器打不到速度要求,可以運行在多台電腦,留一台快的儲存數據分配結果,將數據寫入並行分配給多台伺服器去寫入,除了有讀取操作的那台電腦外,其他的3000+台都可以用於數據寫入。應該夠用了吧。


這個數據量直接寫入elasticsearch吧...單表4千萬以上查詢起來就很難受了,可以用logstash轉運進


數據分片啊,各種拆拆拆。


推薦閱讀:

銷售新人怎麼做銷售?
管理諮詢公司和設計諮詢公司區別大嗎?
產品、服務和解決方案的區別是什麼?
為什麼杭州的霧霾日超過200天,沒有多大感覺?

TAG:資料庫 | Python | Redis | 高並發 | 解決方案 |