mysql开启布隆过滤器_海量数据去重之布隆过滤器
背景
在使?word?檔時(shí),word如何判斷某個(gè)單詞是否拼寫正確?
?絡(luò)爬?程序,怎么讓它不去爬相同的url???
垃圾郵件(短信)過(guò)濾算法如何設(shè)計(jì)?
公安辦案時(shí),如何判斷某嫌疑?是否在?逃名單中?
緩存穿透問(wèn)題如何解決?
先來(lái)看一個(gè)場(chǎng)景,假如我們的數(shù)據(jù)庫(kù)使用的是 mysql,緩存使用 redis。
server
redis
mysql
數(shù)據(jù)讀取步驟是這樣的:
先訪問(wèn) redis ,如果數(shù)據(jù)存在直接返回;如果不存在則進(jìn)行步驟 2;
訪問(wèn) mysql ,如果數(shù)據(jù)不存在,直接返回;如果存在則進(jìn)行步驟 3;
將 mysql 中存在的 key 寫回 redis;
出現(xiàn)的問(wèn)題: 如果 redis 和 mysql 中都沒(méi)有相應(yīng)的數(shù)據(jù),此時(shí)又有大量的該數(shù)據(jù)的請(qǐng)求(偽造數(shù)據(jù)攻擊),最終的壓力還是會(huì)全部涌向 mysql。這就是所謂的 緩存穿透。
解決方案:
在 redis 端設(shè)置 鍵值對(duì),以避免訪問(wèn) mysql。當(dāng)然缺點(diǎn)是如果 過(guò)多時(shí)會(huì)占用過(guò)多的內(nèi)存。我們可以給 key 設(shè)置過(guò)期時(shí)間,比如 exoire key 600ms, 停止攻擊后最終由 redis 自動(dòng)清除這些無(wú)用的 key ;
在 server 端設(shè)置一個(gè)布隆過(guò)濾器,將 mysql 中包含的 key 放入布隆過(guò)濾器中;布隆過(guò)濾器能過(guò)濾一定不存在的數(shù)據(jù)。
布隆過(guò)濾器
假設(shè)我么你現(xiàn)在提出一個(gè)需求:從海量數(shù)據(jù)中查詢某字符串是否存在。
在 c++ 中我們首先想到的應(yīng)該是使用 STL 中的 set 或者 map。
set 和 map
c++ 標(biāo)準(zhǔn)庫(kù)(STL)中的 set 和 map 結(jié)構(gòu)都是采?紅?樹實(shí)現(xiàn)的,它增刪改查的時(shí)間復(fù)雜度是:
o
(
l
o
g
2
n
)
o(log_{2}n)o(log2?n)
對(duì)于嚴(yán)格平衡?叉搜索樹(AVL),100w 條數(shù)據(jù)組成的紅?樹,只需要?較20次就能找到該值;對(duì)于10億條數(shù)據(jù)只需要?較30次就能找到該數(shù)據(jù);也就是查找次數(shù)跟樹的?度是?致的;
對(duì)于紅?樹來(lái)說(shuō)平衡的是?節(jié)點(diǎn)?度,所以研究?較次數(shù)需要考慮樹的?度差,最好情況某條樹鏈路全是?節(jié)點(diǎn),假設(shè)此時(shí)?度為 h1,最差情況某條樹鏈路全是?紅節(jié)點(diǎn)間隔,那么此時(shí)樹?度為 2*h1;
在紅?樹中每?個(gè)節(jié)點(diǎn)都存儲(chǔ) key 和 val 字段,key 是?來(lái)做?較的字段;紅?樹并沒(méi)有要求 key 字段唯?,在 set 和 map 實(shí)現(xiàn)過(guò)程中限制了 key 字段唯?。
另外 set 和 map 的關(guān)鍵區(qū)別是 set 不存儲(chǔ) val 字段;
優(yōu)點(diǎn):存儲(chǔ)效率?,訪問(wèn)速度?效;
缺點(diǎn):對(duì)于數(shù)據(jù)量?且查詢字符串?較?且查詢字符串相似時(shí)將會(huì)是噩夢(mèng);
unordered_map
c++ 標(biāo)準(zhǔn)庫(kù)(STL)中的 unordered_map 是采? hashtable 實(shí)現(xiàn)的;
構(gòu)成:數(shù)組 + hash 函數(shù);
它是將字符串通過(guò) hash 函數(shù)?成?個(gè)整數(shù)再映射到數(shù)組當(dāng)中;它增刪改查的時(shí)間復(fù)雜度是 o(1);
hash 函數(shù)的作?:避免插?的時(shí)候字符串的?較;hash函數(shù)計(jì)算出來(lái)的值通過(guò)對(duì)數(shù)組?度的取模能隨機(jī)分布在數(shù)組當(dāng)中;
hash 函數(shù)?般返回的是 64 位整數(shù),將多個(gè)?數(shù)映射到?個(gè)?數(shù)組中,必然會(huì)產(chǎn)?沖突;
如何選取 hash 函數(shù)?
選取標(biāo)準(zhǔn):
選取計(jì)算速度快;
哈希相似字符串能保持強(qiáng)隨機(jī)分布性(防碰撞);
murmurhash1,murmurhash2,murmurhash3,siphash( redis6.0 當(dāng)中使?,rust 等?多數(shù)語(yǔ)?選?的 hash 算法來(lái)實(shí)現(xiàn) hashmap),cityhash 都具備強(qiáng)隨機(jī)分布性;測(cè)試地址如下:https://github.com/aappleby/smhasher
負(fù)載因?:數(shù)組存儲(chǔ)元素的個(gè)數(shù)/數(shù)組?度;負(fù)載因?越?,沖突越?;負(fù)載因?越?,沖突越?;
hash沖突解決?案
鏈表法
引?鏈表來(lái)處理哈希沖突;也就是將沖突元素?鏈表鏈接起來(lái);這也是常?的處理沖突的?式;但是可能出現(xiàn)?種極端情況,沖突元素?較多,該沖突鏈表過(guò)?,這個(gè)時(shí)候可以將這個(gè)鏈表轉(zhuǎn)換為紅?樹;由原來(lái)鏈表時(shí)間復(fù)雜度 o(n) 轉(zhuǎn)換為紅?樹時(shí)間復(fù)雜度 ;那么判斷該鏈表過(guò)?的依據(jù)是多少?可以采?超過(guò)256(經(jīng)驗(yàn)值)個(gè)節(jié)點(diǎn)的時(shí)候?qū)㈡湵斫Y(jié)構(gòu)轉(zhuǎn)換為紅?樹結(jié)構(gòu);
開放尋址法
將所有的元素都存放在哈希表的數(shù)組中,不使?額外的數(shù)據(jù)結(jié)構(gòu);?般使?線性探查的思路解決;
當(dāng)插?新元素的時(shí),使?哈希函數(shù)在哈希表中定位元素位置;
檢查數(shù)組中該槽位索引是否存在元素。如果該槽位為空,則插?,否則進(jìn)行第 3 步;
在第 2 步檢測(cè)的槽位索引上加?定步?接著檢查第 2 步;
加?定步?分為以下?種:
i+1,i+2,i+3,i+4 ... i+n
i- ,i+ ,i- ,1+ ...
這兩種都會(huì)導(dǎo)致同類 hash 聚集;也就是近似值它的 hash 值也近似,那么它的數(shù)組槽位也靠近,形成 hash 聚集;第?種同類聚集沖突在前,第?種只是將聚集沖突延后;
另外還可以使?雙重哈希來(lái)解決上?出現(xiàn) hash 聚集現(xiàn)象。
在 .net HashTable 類的 hash 函數(shù) Hk 定義如下:
Hk(key) = [GetHash(key) + k * (1 + (((GetHash(key) >> 5) + 1) %(hashsize – 1)))] % hashsize
在此 (1 + (((GetHash(key) >> 5) + 1) % (hashsize – 1))) 與 hashsize互為素?cái)?shù)(兩數(shù)互為素?cái)?shù)表示兩者沒(méi)有共同的質(zhì)因?);執(zhí)?了 hashsize 次探查后,哈希表中的每?個(gè)位置都有且只有?次被訪問(wèn)到,也就是說(shuō),對(duì)于給定的 key,對(duì)哈希表中的同?位置不會(huì)同時(shí)使? Hi 和 Hj。
具體原理:https://www.cnblogs.com/organic/p/6283476.html
同樣的 hashtable 中節(jié)點(diǎn)存儲(chǔ)了 key 和 val,hashtable 并沒(méi)有要求 key 的??順序,我們同樣可以修改代碼讓插?存在的數(shù)據(jù)變成修改操作;
優(yōu)點(diǎn):訪問(wèn)速度更快;不需要進(jìn)?字符串?較;
缺點(diǎn):需要引?策略避免沖突,存儲(chǔ)效率不?;空間換時(shí)間;
總結(jié)
紅?樹和 hashtable 都不能解決海量數(shù)據(jù)問(wèn)題,它們都需要存儲(chǔ)具體字符串,如果數(shù)據(jù)量?,提供不了?百 G 的內(nèi)存;所以需要嘗試探尋不存儲(chǔ) key 的?案,并且擁有 hashtable 的優(yōu)點(diǎn)(不需要?較字符串);
布隆過(guò)濾器
布隆過(guò)濾器是?種概率型數(shù)據(jù)結(jié)構(gòu),它的特點(diǎn)是?效的插?和查詢,能明確告知某個(gè)字符串?定不存在或者可能存在;相?傳統(tǒng)的查詢結(jié)構(gòu)(例如:hash,set,map等數(shù)據(jù)結(jié)構(gòu))更加?效,占?空間更?;但是其缺點(diǎn)是它返回的結(jié)果是概率性的,也就是說(shuō)結(jié)果存在誤差的,雖然這個(gè)誤差是可控的;同時(shí)它不?持刪除操作;
組成:位圖(bit 數(shù)組)+ n 個(gè) hash 函數(shù)
原理:當(dāng)?個(gè)元素加?位圖時(shí),通過(guò) k 個(gè) hash 函數(shù)將這個(gè)元素映射到位圖的 k 個(gè)點(diǎn),并把它們置為 1;當(dāng)檢索時(shí),再通過(guò) k 個(gè) hash 函數(shù)運(yùn)算檢測(cè)位圖的 k 個(gè)點(diǎn)是否都為 1;如果有不為 1 的點(diǎn),那么認(rèn)為不存在;如果全部為1,則可能存在(存在誤差);
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-ahhhbjn3-1610794049644)(原理.png)]
在位圖中每個(gè)槽位只有兩種狀態(tài)(0 或者 1),?個(gè)槽位被設(shè)置為 1 狀態(tài),但不明確它被設(shè)置了多少次;也就是不知道被多少個(gè) str1 哈希映射以及是被哪個(gè) hash 函數(shù)映射過(guò)來(lái)的;所以不?持刪除操作;
在實(shí)際應(yīng)?過(guò)程中,布隆過(guò)濾器該如何使??要選擇多少個(gè) hash 函數(shù),要分配多少空間的位圖,存儲(chǔ)多少元素?另外如何控制假陽(yáng)率(布隆過(guò)濾器能明確?定不存在,不能明確?定存在,那么存在的判斷是有誤差的,假陽(yáng)率就是錯(cuò)誤判斷存在的概率)?
n -- 布隆過(guò)濾器中元素的個(gè)數(shù),如上圖 只有str1和str2 兩個(gè)元素 那么 n=2
p -- 假陽(yáng)率,在0-1之間 0.000000
m -- 位圖所占空間
k -- hash函數(shù)的個(gè)數(shù)
公式如下:
n = ceil(m / (-k / log(1 - exp(log(p) / k))))
p = pow(1 - exp(-k / (m / n)), k)
m = ceil((n * log(p)) / log(1 / pow(2, log(2))));
k = round((m / n) * log(2));
假定我們選取這四個(gè)值為:
n = 4000
p = 0.000000001
m = 172532
k = 30
四個(gè)值的關(guān)系:
在實(shí)際應(yīng)?中,我們確定 n 和 p,通過(guò)上?的計(jì)算算出 m 和 k;也可以在?站上選取合適的值:https://hur.st/bloomfilter
已知 k,如何選擇 k 個(gè) hash 函數(shù)?
// 采??個(gè)hash函數(shù),給hash傳不同的種?偏移值
// #define MIX_UINT64(v) ((uint32_t)((v>>32)^(v)))
uint64_t hash1 = MurmurHash2_x64(key, len, Seed);
uint64_t hash2 = MurmurHash2_x64(key, len, MIX_UINT64(hash1));
for (i = 0; i < k; i++) // k 是hash函數(shù)的個(gè)數(shù)
{
Pos[i] = (hash1 + i*hash2) % m; // m 是位圖的??
}
// 通過(guò)這種?式來(lái)模擬 k 個(gè)hash函數(shù) 跟我們前?開放尋址法 雙重hash是?樣的思路
總結(jié)
以上是生活随笔為你收集整理的mysql开启布隆过滤器_海量数据去重之布隆过滤器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 离线网页 HTML+CSS+DIV
- 下一篇: MySQL存储过程和函数的区别