有一個發送100w短信的任務,如何儘可能縮短髮送時間,同時在中途由於各類緣由任務掛掉時,好比發送完50w時任務掛掉,重啓任務以後只發送剩餘50w短信?redis
這是一個比較通用的問題,容易想到的辦法是:數據庫
這樣能夠解決問題,可是有一些缺點:多線程
缺點d是由於發送操做和更新數據庫狀態是分開的,並且發送操做沒法回滾,因此沒法經過分佈式事務來根本解決,這個問題基本無解,即老是有可能重複處理,只能儘可能讓重複發送的機率下降。分佈式
既然缺點d沒法避免,能夠經過多線程+批量提交事務來解決缺點a:優化
(好比每次讀取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?進程
方案三相比方案二事務
缺點a方面,節省了99%以上的數據庫操做,效率提高N倍;
缺點b和缺點c方面,完美解決;
缺點d方面,方案三最多有B個數據重複處理(上例中的1000),方案二最多有C*D個數據重複處理(上例中的10*20=200),即方案三相對方案二放大了缺點d;
有沒有可能在缺點d上進一步優化?
(好比每次查詢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由子類實現,快速應用到全部問題場景中。