BloomFilter如何快速檢查用戶名重復(fù)
背景
在我們的項(xiàng)目中,用戶名是不能重復(fù)的,因?yàn)樵跀?shù)藏項(xiàng)目中,用戶名的唯一性很重要,因?yàn)椴仄芬鏊菰?,要確保整個(gè)鏈路上的參與者的唯一性,雖然用戶 id 也是我唯一的,但是頁(yè)面上展示的時(shí)候不能用 id 呀。
有些網(wǎng)站,比如淘寶網(wǎng),也是要求用戶名唯一的。
所以為了實(shí)現(xiàn)這個(gè)用戶在注冊(cè),改名的時(shí)候的用戶名的唯一性,我們一般是先從數(shù)據(jù)庫(kù)查詢是否存在,不存在則讓用戶注冊(cè)。
但是為了考慮到性能,我們會(huì)把用戶名存儲(chǔ)到緩存中,一般使用 redis 緩存。
那既然都用了緩存了,那還不如干脆直接就用布隆過(guò)濾器來(lái)存儲(chǔ),既可以做重復(fù)校驗(yàn),又能節(jié)省空間。所以,我們?cè)谟脩裘貜?fù)檢驗(yàn)這里就用到了布隆過(guò)濾器。
簡(jiǎn)介
布隆過(guò)濾器是一種數(shù)據(jù)結(jié)構(gòu),用于快速檢索一個(gè)元素是否可能存在于一個(gè)集合(bit 數(shù)組)中。
它的基本原理是利用多個(gè)哈希函數(shù),將一個(gè)元素映射成多個(gè)位,然后將這些位設(shè)置為 1。當(dāng)查詢一個(gè)元素時(shí),如果這些位都被設(shè)置為 1,則認(rèn)為元素可能存在于集合中,否則肯定不存在。
所以,布隆過(guò)濾器可以準(zhǔn)確的判斷一個(gè)元素是否一定不存在,但是因?yàn)楣_突的存在,所以他沒(méi)辦法判斷一個(gè)元素一定存在。只能判斷可能存在。
代碼實(shí)現(xiàn)
所以,我們定義了兩個(gè)方法,nickNameExist用于判斷是否存在,addNickName用于向布隆過(guò)濾器中添加已注冊(cè)的用戶名。
public boolean nickNameExist(String nickName) {
//如果布隆過(guò)濾器中存在,再進(jìn)行數(shù)據(jù)庫(kù)二次判斷
if (this.bloomFilter.contains(nickName)) {
return userMapper.findByNickname(nickName) != null;
}
return false;
}
private boolean addNickName(String nickName) {
return this.bloomFilter.add(nickName);
}這里nickNameExist方法,先從布隆過(guò)濾器中查詢,如果如果查到了,再去數(shù)據(jù)庫(kù)中查了一下,為啥呢?
因?yàn)椴悸∵^(guò)濾器有誤判的,存在一定的誤判率,他會(huì)把不存在的用戶判斷為存在,所以當(dāng)檢查結(jié)果是存在的時(shí)候,需要再次判斷一次。
因?yàn)橛脩糇?cè)的時(shí)候,大多數(shù)情況下都是不重復(fù)的,所以我們可以快速的用布隆過(guò)濾器進(jìn)行不存在的判斷。
這里的bloomFilter是這樣被初始化出來(lái)的:
private RBloomFilter<String> bloomFilter;
@Override
public void afterPropertiesSet() throws Exception {
this.bloomFilter = redissonClient.getBloomFilter("nickName");
if (!bloomFilter.isExists()) {
this.bloomFilter.tryInit(10000000L, 0.01);
}
}我們?cè)O(shè)置了10000000的容量,誤判率是0.01。
然后在修改用戶名這里,用這樣的方式進(jìn)行調(diào)用的。

如果用戶名修改了怎么辦?
如果原來(lái)用戶名是Aizer,被放到布隆過(guò)濾器了,但是后面我改成 Aizer666了,那么意味著 Aizer已經(jīng)沒(méi)有了,那么如何從布隆過(guò)濾器刪除呢?
很遺憾,不支持!
那怎么辦呢?
其實(shí)問(wèn)題也不大,因?yàn)椴悸∵^(guò)濾器本身就存在誤判率,但我們檢查布隆過(guò)濾器發(fā)現(xiàn)存在的時(shí)候,還是會(huì)去數(shù)據(jù)庫(kù)再確認(rèn)一遍的。
只要我們定期的重建一下布隆過(guò)濾器就行了。重建就是都刪了,然后重新構(gòu)建。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
深入學(xué)習(xí)Java單元測(cè)試(Junit+Mock+代碼覆蓋率)
在做單元測(cè)試時(shí),代碼覆蓋率常常被拿來(lái)作為衡量測(cè)試好壞的指標(biāo),甚至,用代碼覆蓋率來(lái)考核測(cè)試任務(wù)完成情況,比如,代碼覆蓋率必須達(dá)到80%或 90%。下面我們就來(lái)詳細(xì)學(xué)習(xí)下java單元測(cè)試吧2019-06-06
Springboot通過(guò)配置WebMvcConfig處理Cors非同源訪問(wèn)跨域問(wèn)題
這篇文章主要介紹了Springboot通過(guò)配置WebMvcConfig處理Cors非同源訪問(wèn)跨域問(wèn)題,關(guān)于Cors跨域的問(wèn)題,前端有代理和jsonp的常用方式解決這種非同源的訪問(wèn)拒絕策略2023-04-04
Java?延時(shí)隊(duì)列及簡(jiǎn)單使用方式詳解
這篇文章主要介紹了Java延時(shí)隊(duì)列簡(jiǎn)單使用方式,通過(guò)本文學(xué)習(xí)知道延時(shí)隊(duì)列是什么可以用來(lái)干什么,本文通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08
Java將byte[]轉(zhuǎn)圖片存儲(chǔ)到本地的案例
這篇文章主要介紹了Java將byte[]轉(zhuǎn)圖片存儲(chǔ)到本地的案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10

