優化源於痛點(┬_┬)sql
有沒有痛點取決於業務場景的需求;有多痛取決於當前方案對業務的契合度數據庫
讓咱們從業務場景①、當前方案②切入,聯立①②來推導當前痛點③吧!優化
話很少說,開始分析spa
①業務場景:unix
1.同一時間段內出如今同一攝像頭下的用戶即爲同行關係(不須要兩我的同步出如今攝像頭下,由於我司暫不支持在一張圖片內一次性提取兩我的,處理邏輯太麻煩了,還不如後面分析)blog
2.計算須要並行進行,每次計算一天的數據量,大約千萬級排序
②當前方案:圖片
將所有數據拉到內存,全局排序後按照時間段大小分塊,而後經過滑動窗口進行計算,好比說時間段是10s,20.11.00到20.11.10爲一個窗口,20.11.10到20.11.20爲一個窗口,20.11.20到20.11.30爲一個窗口,同一窗口直接斷定同行,相鄰窗口間須要計算肯定是否同行,計算後還要每兩個窗口之間去重內存
③當前痛點:get
1.千萬級數據拉到內存,會對內存形成必定壓力,由於並非只有這一個服務須要使用內存
2.計算過程大量重複,去重邏輯繁瑣,浪費大量算力
那麼問題來了,是否存在什麼更合適的方案來解決這些痛點呢?
我想,是有的。
根據痛點③,反推咱們的預期目標④;
根據目標④,嘗試推導出優化思路⑤;
落地思路⑤,成爲最終的優化方案⑥
④預期目標
1.不佔內存
2.避免重複計算,一鍵去重
⑤優化思路
1.不把數據全拉到內存全局排序就不會形成內存壓力,因此咱們直接在sql裏dd分好片就好了
2.從數學上邏輯推導來解決重複計算問題,參考裂項相消法的思路
⑥優化方案
test:原表,存儲須要計算的數據
①假定取一天數據,劃分時間段是10s,咱們能夠把一天分紅8640個塊,而後按塊處理。這些塊就是咱們須要的分塊,不須要作排序再切片什麼的
with source as( select id, floor(unix_timestamp(time)/10) as time_part, unix_timestamp(time)as time from test )
②假定有四個塊,1234,那麼咱們須要計算的實際上是相鄰的塊之間的時間差,也就是1和2,2和3,3和4,其餘的11,22,33,44都直接就是咱們須要的結果,把這些返回結果加起來,就是咱們須要的答案。咱們把數據複製一份,第一份是本來的1234,第二份-1變成0123,而後按塊join,就能獲得11(本來的2,即12),22(本來的3,即23),33(本來的4,即34),第一份再和本身join,就能獲得11,22,33,44,加起來就是11,22,33,44,12,23,34,即咱們須要的全部結果
with source as ( select id, floor(unix_timestamp(time)/10) as time_part, unix_timestamp(time)as time from test ), target as ( select id, alias.time_part, time from source lateral view explode(split(concat(time_part-1,',',time_part),',')) alias as time_part )
上述操做實際上是一步到位,把全部數據塊平移了一個單位而後和本來的數據庫union了,a實現方式爲explode,alisas是別名。經過這種方式咱們能直接獲得須要計算的全部數據
③配對,計算
with source as ( select id, floor(unix_timestamp(time)/10) as time_part, unix_timestamp(time)as time from test ), target as ( select id, alias.time_part, time from source lateral view explode(split(concat(time_part-1,',',time_part),',')) alias as time_part ) select id from source join target on source.time_part=target.time_part where abs(source.time-target.time)<10
以上就是個人優化方案,全部sql均在spark.sql中執行,優勢以下:
1.數據庫內直接分好塊,不佔內存,來再多數據也沒影響
2.完美解決邊界點問題,沒有任何遺漏計算也沒有任何重複計算
3.具有可延展性,能夠輕鬆把邏輯延展到多維空間
以上就是本次優化從思考到實現的全過程啦,但願你們喜歡(≧▽≦)