一、一致性hash算法
對我們有什麼啓發?
一致性hash,本質就是把數據分散在(2^32-1) 圓形的槽裏面!
假設我們的(2^32-1) 每個槽裏面,只存儲一個bit,那我們佔用的內存之後
(2^32)-1 bit 大
1 個int = 4 個byte
1 個byte = 8bit
1 int = 32 個bit
二、問題的引入?
怎麼判斷1億 數據裏面是否存在某個數據?要求算法的複雜度,控制在常數範圍內!
14億人身份證號碼,怎麼快速判斷某個身份證號碼在這個裏面?
2.1 解決方案1 HashSet
把14億個字符串存在在HashSet 裏面,使用HashSet的 判斷是否存在某個值解決
2.2 解決方案2 TreeSet 裏面
把14億個字符串存在在TreeSet 裏面,使用TreeSet 的 判斷是否存在某個值解決
速度問題:使用Hash>>Tree
佔用空間問題:若14個值都存儲滿,佔用大小基本是一樣的!
2.3 使用集合存儲字符串數據的優缺點
優點:非常簡單,我們可以直接是HashSet 來存儲
缺點:當我們的數據量約大,它的存儲的數據將無法估量,可能導致JVM 內存的移除
HashSet 和TreeSet 直接存儲字符串佔用的內存太大了,我們需要去選擇別的結構了
三、引入位集合
3.1 圖示
3.2 特點
集合裏面只能存儲0/1 ,
每個點都佔用1 個bit
四、Hash
4.1 hash
什麼叫hash?
Hash 就是爲把某個值,映射在一個有序的長度上面,得到一個映射值
4.2 hash 和hashCode
在一段長度的位置值
和HashCode的關係:
HashCode 不等於該位置的值,
HashCode 一般是個非常特殊的值,是通過一個質數做變化得來的?
該值不容易重複,該值算出來可以很多!
什麼是位置的pos:
位置的值,不等於hashCode 的值:
比如我們的長度有20 ,但是你的hashCode 是100 ;
我們沒有放下你,需要一個變化?
怎麼把hashCode 值變成一個Pos的值?
Int pos = getPos(hashCode);
Int pos(){
Int pos = hashCode & (len-1) ;
}
通過該方法我們可以把任意的hashCode 映射在我們的固定長度位置上面
4.3 怎麼解決Hash 衝突的問題
有2 個值,他們的hash值/pos值都是相同的?
Hash衝突怎麼搞?
4.3.1 拉鍊法解決
就是HashMap 裏面的解決思路
4.3.2 二次hash
我們若計算出來該值的hash 和之前的值有衝突,我們使用另一個hashCode函數在計算hashCode,得的不同的pos ,有新的pos
4.3.3 跳躍法
五、使用位集合存在字符串,並且判斷該字符串是否存儲在該位集合裏面
5.1 思維導圖
5.2 將上面的圖,使用代碼表示出來
5.3.1 新建HashCodeFun
public class HashCodeFun {
/**
* 質數,計算hashCode值的關鍵
*/
private int seed ;
public HashCodeFun(int seed){
this.seed = seed ;
}
/**
* 給一個value ,計算該value的hashCode
* @param value
* @return
*/
public int hashCodeFun(String value) {
char[] chars = value.toCharArray();
int h = 0;
if (h == 0 && chars.length > 0) {
char val[] = chars;
for (int i = 0; i < chars.length; i++) {
h = seed * h + val[i];
}
}
return h;
}
}
5.3.2 新建Quesion類
public class Question {
/**
* 位集合的長度
*/
private int len = 32 ;
/**
* 創建一個位集合
*/
private BitSet bitSet = new BitSet(len) ;
private int []seeds = new int[]{31,37,41,43};
private HashCodeFun[] hashCodeFuns = new HashCodeFun[4] ;
{
for (int i = 0; i < seeds.length; i++) {
hashCodeFuns[i] = new HashCodeFun(seeds[i]) ;
}
}
/**
* 往位集合裏面設置一個值
* @param value
*/
public void addValue(String value){
for (HashCodeFun hashCodeFun : hashCodeFuns) { // 循環hashCode的方法
int hashCode = hashCodeFun.hashCodeFun(value); // 通過質數計算HashCode的值
int pos = hashCode & (len-1) ;
System.out.println(pos);
bitSet.set(pos,true);
}
}
/**
* 怎麼判斷該位集合裏是否存在某個值
* 1 判斷集合裏面的代表該value的4 個點
*/
public boolean existValue(String value){
for (HashCodeFun hashCodeFun : hashCodeFuns) {
int hashCode = hashCodeFun.hashCodeFun(value);
int pos = hashCode & (len-1) ;
if(!bitSet.get(pos)){ // 若該位集合上面的該點,沒有被佔用,
return false ; // 有一個點不符合
}
}
return true ;
}
public static void main(String[] args) {
Question question = new Question();
question.addValue("mayun");
// question.addValue("mahuateng");
System.out.println(question.existValue("mahuateng"));
// System.out.println(question.existValue("mahuateng"));
}
}
5.3 hash 函數的本質是是啥?
Hash過程的本質是啥:
Hash函數的本質在於特徵的提取
做人臉識別:鼻子 + 嘴巴 + 耳朵 + 眼睛
鼻子 = Hash1(人臉);
嘴巴 = Hash2(人臉);
耳朵 = Hash3(人臉);
眼睛 = Hash4(人臉);
假設我們的hash函數可以做一個特徵的提取,
利用高斯函數疊加:得到一個具體的特徵
你的臉和劉德華的臉:
對比你們2個的鼻子,嘴巴,耳朵,眼睛
在人臉識別裏面,需要一個特徵的提取,在把字符串映射在位集合上,它的本質也是提取字符串某個特徵!
5.4 我們上面使用位集合存儲值,並且判斷位集合裏面是否有值,屬於BloomFilter的算法
5.6 作業
5.6.1 把今天Mycat的高可用測試成功
5.6.2 使用位集合存儲值,並且判斷值是否有,這個算法實現了
5.6.3 使用Redis 來實現我們上面寫的BloomFilter這個算法
提示:
1 bitset 在jvm的內存裏面,無法和別的jvm 共享
2 bitset 太大了,可能會佔用內存!
使用Redis 使用BloomFilter
在redis 裏面有個數據結構:BitMap