天天看點

随機記錄并發查詢與更新(轉移、删除)的"無恥"優化方法

某張表有一批記錄,a使用者說,這批記錄是我要的,但是我隻要一條,b使用者也說,這批記錄是我要的,我也隻要一條。

是不是有點像一群男人去逛怡紅院,妹子們都是目标,但是今晚隻要一位,至于是誰暫時還不确定,雖然不需要搶,但是得鎖單。

被動配置設定式,等着媽媽給你分一個。

主動挑選式,主動到姑娘們群裡挑,就涉及到鎖單的問題了,一個妹子隻能陪一位公子哦。  

上面的例子可能不太适合未成年人,那麼看看另一個形象的比喻,某處有一堆磚塊,每塊磚頭都有一個唯一編号,然後一群小夥伴同時來取磚塊(每人每次取1塊),要求每個小夥伴拿到的磚塊id是随機的,并且要求以最快的方式将磚塊取完。

這次真的來搬磚了,來比一比誰的搬磚能力強吧。

随機記錄并發查詢與更新(轉移、删除)的"無恥"優化方法

我們将問題轉化一下,一塊磚一個id,作為一條記錄存入資料庫,假設我們有1000萬塊磚。有128個小夥伴同時來搬磚,怎麼能以最快的速度,随機的把磚搬完呢?

這個場景實際上有一個來頭,某個群紅包密碼業務,由于該業務沒有對接賬務系統,沒有使用者id也沒有使用者手機号,是以沒法将領紅包的資格做判定,為了防止任何人都能猜測密碼的方式來領取紅包,搞了一個批量生成随機密碼的方法,發紅包的時候從資料庫取走一條(随機密碼)。既要考慮随機,又要考慮使用者體驗,是以選擇了8位數值(比較容易猜測),然後又要考慮高并發的發紅包場景,是以還要求取值快。

了解了需求後,我們看看如何優化?

考慮随機、并發還不夠,因為資料要取走(轉移到一個已消耗的表中),是以還需要考慮資料的收縮。

比如postgresql的堆表,末端的空資料塊是可以被回收的,那麼我們在設計的時候,如果能從末端開始取,是最好的。

1. 插入時就讓資料随機,而不是取時随機。

建立測試表, 存放一堆唯一值.

唯一值随機插入, 取資料時按照資料塊倒序取出, 這麼做的好處是vacuum時可以直接回收這部分空間.

随機的插入1000萬資料

從資料來看 , 已經随機插入了.

在ctid(行号)上建立索引, 取資料時使用這個索引, 倒序從最後的資料塊開始取資料.

例如:

為了防止多個程序重複取資料, 使用這種方法.

測試并行取資料.

測試方法, 将資料插入另一張表,表示資料從一張表搬運到另一張表。

使用pgbench 測試, 16個并行取資料程序, 每次取5條.

測試完成後, 查詢test表, 看看有沒有重複資料就知道這種方法是否靠譜了.

性能見下 :

經查沒有重複資料, 方法靠譜,搬磚成功

以上方法資料是從堆表的末端開始搬運的,是以表會随着搬運,autovacuum使之變小。

但是實際上,以上query有一個問題,select沒有加鎖,在delete時,可能已經被其他并發程序搬走了。競争的問題也被掩蓋了。

為了改善這個問題,比如要求每次請求,必須搬走1塊磚。那麼需要加limit 1 for update skip locked,這樣能解決競争的問題,但是無法解決重複掃描的問題。

我們先看看效果

64個搬運工,每秒隻能搬運4000條左右。

因為他們中最差的那個詢問了64塊磚才拿到搬運這塊磚頭的所有權,隻有第一個人,詢問了1塊磚就拿到了所有權。

那麼怎麼優化呢? 如何讓每個搬運工每次拿到的磚頭id都是随機的,并且沒人和他搶。

随機記錄并發查詢與更新(轉移、删除)的"無恥"優化方法

如何拿到随機的記錄是關鍵,postgresql提供了一個随機通路接口tablesample,通過這個接口,可以随機通路資料(提供一個百分比1-100即可),注意随機通路的資料是在where過濾條件前,是以百分比太小的話,你可能會通路不到任何資料。

目前支援兩種随機采樣方法,1. system,按塊随機(整個資料塊的記錄被取出);2. bernoulli掃全表,按百分比傳回随機記錄;是以bernoulli比system随機度更精準,但是system的效率更高。

壓測

搬磚性能從4000提升到了将近9萬。

随機記錄并發查詢與更新(轉移、删除)的"無恥"優化方法

除了這個搬磚場景,還有一些其他場景也能使用類似方法,感謝萬能的postgresql。

比如有一個場景初始化了一批賬号id,初始id=0,每次有使用者來注冊時,将id=0的記錄修改為此次注冊的使用者id,相當于消耗一條id=0的記錄。

使用采樣的方法可以優化這個場景,不過别急着套用,因為資料采樣是在過濾條件之前發生的,是以當所有資料範圍都是我們的目标資料是沒問題的,但是如果你把目标資料和非目标資料混到一起,這種采樣的方法就可能導緻備援掃描,如果采樣比例低,甚至找不到目标資料。是以前面的搬磚場景,我們每次都把資料搬走,剩餘的所有資料依舊是目标資料,是以不存在問題。

那麼了解了以上原理之後,第二個場景,我們也采樣轉移法,即申請id的時候,将資料轉移走,而不僅僅是update id=newid的做法。

例子

函數

測試

每秒轉移9.8萬記錄,采樣法消除沖突後性能驚人。

1. 為了解決高并發的資料随機通路、更新、轉移等熱點與掃描相似悖論的問題,postgresql 采樣接口打開一種很"無恥"的優化之門,讓小夥伴們可以開足并發,卯足瑪麗開搞。

為什麼一個蛋糕,大家都要從一處搶呢,圍成一圈,每人在各自的方向挖一勺不是更好麼?就好像小時候長輩較我們夾菜,要夾靠近自己這一邊的一樣。

<a href="https://www.postgresql.org/docs/9.6/static/plpgsql-statements.html">https://www.postgresql.org/docs/9.6/static/plpgsql-statements.html</a>

<a href="https://wiki.postgresql.org/wiki/tablesample_implementation">https://wiki.postgresql.org/wiki/tablesample_implementation</a>

繼續閱讀