【原創】大叔案例分享(2)處理大批量數據時如何實現「高效」同時實現「斷點續傳」功能

問題

有一個發送100w短信的任務,如何儘可能縮短髮送時間,同時在中途由於各類緣由任務掛掉時,好比發送完50w時任務掛掉,重啓任務以後只發送剩餘50w短信?redis

 

這是一個比較通用的問題,容易想到的辦法是:數據庫

方案一

步驟

  1. 使用數據庫來存放全部數據(好比100w條待發送短信),同時設置status,未處理是0,已處理是1;
  2. 任務執行時查詢全部status=0的數據,即待處理數據;
    1.   逐條處理,而且在處理以後設置status=1,避免重複處理;

分析

這樣能夠解決問題,可是有一些缺點多線程

  • a 串行處理數據效率很低;
  • b 數據必須存放在可update的存儲,好比數據庫,不支持hive、文件這種不可update的存儲;
  • c 傳統RDBMS對數據量的支持有限;
  • d 即便串行,極端狀況下仍有可能有1條數據被重複處理(重複發送短信);

缺點d是由於發送操做和更新數據庫狀態是分開的,並且發送操做沒法回滾,因此沒法經過分佈式事務來根本解決,這個問題基本無解,即老是有可能重複處理,只能儘可能讓重複發送的機率下降分佈式

 

既然缺點d沒法避免,能夠經過多線程+批量提交事務來解決缺點a:優化

方案二

步驟

  1. 同上
  2. 同上
    1.   每次讀取B條數據,將數據平均分發給C個線程來同時處理,每一個線程處理完D條數據後批量設置這D條數據的status=1;

(好比每次讀取1000條status=0的短信,同時分給10個線程發送,每一個線程發送100條短信,而且每一個線程每成功發送20條後批量更新這20條短信的status=1)ui

分析

方案二相對方案一線程

理論上能夠將時間縮短爲1/C(上例中1/10)如下,同時最悲觀的狀況下有可能會有C*D個數據(上例中10*20=200)被重複處理,即解決缺點a時放大缺點d,同時缺點b和c依然存在;排序

 

有沒有可能同時解決缺點a、b、c?進程

方案三

步驟

  1. 待處理數據存放在數據庫、或者hive、或者文件;
  2. 處理前首先查詢當前任務上次處理處理的offset(若是是數據庫或者hive,offset就是自增id或者uuid;若是是文件,offset就是行數),若是不存在,則設置offset=0,offset能夠存放在數據庫中;
  3. 從上次處理的offset開始繼續查詢待處理數據;(數據庫或者hive即排序查詢跳過前邊的行,文件則直接按照行數跳過前邊的行)
    1.   每次查詢B個數據,分發給C個線程處理,B個數據所有處理完時設置offset+=B;

 

分析

方案三相比方案二事務

缺點a方面,節省了99%以上的數據庫操做,效率提高N倍;
缺點b和缺點c方面,完美解決;
缺點d方面,方案三最多有B個數據重複處理(上例中的1000),方案二最多有C*D個數據重複處理(上例中的10*20=200),即方案三相對方案二放大了缺點d;

 

有沒有可能在缺點d上進一步優化?

方案四

步驟

  1. 同上
  2. 同上
  3. 同上
    1.   每次查詢B個數據,分發給C個線程處理,每一個線程都在cache(memcache或redis,放數據庫也能夠)裏維護一個當前處理的relative_offset,若是relative_offset!=0,則跳過前relative_offset個數據;全部線程處理完後,設置全部線程的relative_offset=0,同時設置offset+=B;

(好比每次查詢1000個未發送短信,分發給10個線程處理,每一個線程都在cache裏維護一個relative_offset,初始值是0,每發送一條消息都將該線程cache裏的relative_offset+=1,10個線程處理完,設置全部線程的relative_offset=0,同時設置offset+=1000)

問題回放

發送8000條短信後取出下1000條待發送短信,即8001-9000,這時分給10個線程,第1個線程分發短信爲8001-8100,第2個線程分發短信爲8101-8200,...,第10個線程分發短信爲8901-9000,發送一段時間後,第1個線程的relative_offset是49,第2個線程的relative_offset是31,其餘線程任意,這時進程掛掉,任務重啓後,拿到offset=8000,此次取出下1000條待發送短信,依舊分給10個線程,第1個線程分發短信爲8001-8100,第2個線程分發短信爲8101-8200,...,第10個線程分發短信爲8901-9000,第1個線程發現relative_offset=49,則繼續發送8050-8100的短信,第2個線程發現relative_offset=31,則繼續發送8132-8200的短信,其餘線程以此類推。

分析

方案四相比方案三

缺點d方面,最悲觀的狀況下,方案三和方案四都會有B條數據重複發送,但方案四發生的機率極低(只有在設置全部線程的relative_offset=0和設置offset+=1000之間掛掉纔會發生),而且在極大的機率上最多隻有C條數據重複發送,這個比方案二的C*D的效果要好不少;

 

綜上,方案四能夠同時實現「高效」+「斷點續傳」,而且這是一個比較通用的方案,能夠實現一套基類,將讀取待處理數據以及處理單條數據做爲template method由子類實現,快速應用到全部問題場景中。

相關文章
相關標籤/搜索