Redis學習之緩存問題(五)

緩存

緩存粒度控制

三個角度

1)通用性:全量屬性更好。數據庫

2)佔用空間:部分屬性更好。數組

3)代碼維護:表面上全量屬性更好。緩存

緩存穿透

定義:大量請求不命中。bash

緣由

1)業務代碼自身問題。服務器

2)惡意攻擊、爬蟲等等。網絡

如何發現

1)業務的相應時間。函數

2)業務自己問題。性能

3)相關指標:總調用數、緩存層命中數、存儲層命中數。優化

解決問題

1)緩存空對象。ui

一、須要更多的鍵。

二、緩存層和存儲層數據「短時間」不一致。

2)布隆過濾器攔截。

所謂的布隆過濾器是一個很長的二進制向量,存放0或者1。通過幾回hash計算,獲得5,9,2三個數字,那麼存放結果以下圖所示:

僅僅從布隆過濾器自己而言,根本沒有存放完整的數據,只是運用一系列隨機映射函數計算出位置,而後填充二進制向量。所以,可使用固定的計算方式 ,來判斷一個數據是否存在。可是因爲相似哈希衝突的緣由存在誤判的可能性,也就是說,布隆過濾器只能判斷數據不存在,而不能判斷數據必定存在。

  • 優勢:因爲存放的不是完整的數據,因此佔用的內存不多,並且新增,查詢速度夠快;
  • 缺點: 隨着數據的增長,誤判率隨之增長;沒法作到刪除數據;只能判斷數據不存在,而不能判斷數據必定存在。

代碼

public class RedisTest {
    static final int expectedInsertions = 100;//要插入多少數據
    static final double fpp = 0.01;//指望的誤判率

    //bit數組長度
    private static long numBits;

    //hash函數數量
    private static int numHashFunctions;

    static {
        numBits = optimalNumOfBits(expectedInsertions, fpp);
        numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, numBits);
    }

    public static void main(String[] args) {
        Jedis jedis = new Jedis("你的ip", 6379);
        for (int i = 0; i < 100; i++) {
            long[] indexs = getIndexs(String.valueOf(i));
            for (long index : indexs) {
                jedis.setbit("codebear:bloom", index, true);
            }
        }
        for (int i = 0; i < 100; i++) {
            long[] indexs = getIndexs(String.valueOf(i));
            for (long index : indexs) {
                Boolean isContain = jedis.getbit("codebear:bloom", index);
                if (!isContain) {
                    System.out.println(i + "確定沒有重複");
                }
            }
            System.out.println(i + "可能重複");
        }
    }

    /**
     * 根據key獲取bitmap下標
     */
    private static long[] getIndexs(String key) {
        long hash1 = hash(key);
        long hash2 = hash1 >>> 16;
        long[] result = new long[numHashFunctions];
        for (int i = 0; i < numHashFunctions; i++) {
            long combinedHash = hash1 + i * hash2;
            if (combinedHash < 0) {
                combinedHash = -combinedHash;
            }
            result[i] = combinedHash % numBits;
        }
        return result;
    }

    private static long hash(String key) {
        Charset charset = Charset.forName("UTF-8");
        return Hashing.murmur3_128().hashObject(key, Funnels.stringFunnel(charset)).asLong();
    }

    //計算hash函數個數
    private static int optimalNumOfHashFunctions(long n, long m) {
        return Math.max(1, (int) Math.round((double) m / n * Math.log(2)));
    }

    //計算bit數組長度
    private static long optimalNumOfBits(long n, double p) {
        if (p == 0) {
            p = Double.MIN_VALUE;
        }
        return (long) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
    }
}
複製代碼

緩存雪崩

定義:某一個時間段,緩存集中過時失效。

緣由

1)集中設置統一的過時時間,到了必定時間時,便所有懟到數據庫上了。

2)服務器某個結點宕機或斷網。

解決

1)使用隨機因子儘量分散過時時間。

2)使用集羣高可用化。

無底洞問題優化

問題描述:2010年,Facebook有了3000個Memcache節點,發現增長機器性能沒能提高,反而降低。

問題關鍵點

1)更多的機器!=更高的性能。

2)批量接口需求(mget, mset等)。

3)數據增加與水平擴展等。

優化IO的幾種方法

1)命令自己優化:例如慢查詢keys、hgetall bigkey。

2)減小網絡通訊次數。

3)下降接入成本:例如客戶端長鏈接/鏈接池、NIO等。

熱點key重建

問題描述:熱點key+較長的重建時間

可能會存在多個線程共同執行查詢數據源與重建緩存的操做。

目標

1)減小重建緩存的次數。

2)數據儘量一致。

3)減小潛在風險。

解決

互斥鎖(mutex key)

在重建時開始加鎖,完成重建後進行解鎖。

代碼:

優勢:

1)思路簡單。

2)保證一致性。

缺點:

1)代碼複雜度增長。

2)存在死鎖的風險。

永遠不過時

1)緩存層面:沒有設置過時時間。

2)功能層面:爲每一個value添加邏輯過時時間,但發現超過邏輯過時後,會使用單獨的線程去構建緩存。

代碼:

優勢:

基本杜絕熱點key重建問題。

缺點:

1)不保證一致性。

2)邏輯過時時間增長維護成本和內存成本。

相關文章
相關標籤/搜索