欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Redis鍵值設(shè)計的具體實(shí)現(xiàn)

 更新時間:2024年06月28日 11:17:42   作者:是誰偷吃了奶酪  
本文主要介紹了Redis鍵值設(shè)計的具體實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

1 優(yōu)雅的key結(jié)構(gòu)

Redis的Key雖然可以自定義,但最好遵循下面的幾個最佳實(shí)踐約定:

  • 遵循基本格式:[業(yè)務(wù)名稱]:[數(shù)據(jù)名]:[id]
  • 長度不超過44字節(jié)
  • 不包含特殊字符

例如:我們的登錄業(yè)務(wù),保存用戶信息,其key可以設(shè)計成如下格式:

在這里插入圖片描述

這樣設(shè)計的好處:

  • 可讀性強(qiáng)
  • 避免key沖突
  • 方便管理
  • 更節(jié)省內(nèi)存: key是string類型,底層編碼包含int、embstr和raw三種。embstr在小于44字節(jié)使用,采用連續(xù)內(nèi)存空間,內(nèi)存占用更小。當(dāng)字節(jié)數(shù)大于44字節(jié)時,會轉(zhuǎn)為raw模式存儲,在raw模式下,內(nèi)存空間不是連續(xù)的,而是采用一個指針指向了另外一段內(nèi)存空間,在這段空間里存儲SDS內(nèi)容,這樣空間不連續(xù),訪問的時候性能也就會收到影響,還有可能產(chǎn)生內(nèi)存碎片

在這里插入圖片描述

2 拒絕BigKey

BigKey通常以Key的大小和Key中成員的數(shù)量來綜合判定,例如:

  • Key本身的數(shù)據(jù)量過大:一個String類型的Key,它的值為5 MB
  • Key中的成員數(shù)過多:一個ZSET類型的Key,它的成員數(shù)量為10,000個
  • Key中成員的數(shù)據(jù)量過大:一個Hash類型的Key,它的成員數(shù)量雖然只有1,000個但這些成員的Value(值)總大小為100 MB

那么如何判斷元素的大小呢?redis也給我們提供了命令

在這里插入圖片描述

推薦值:

  • 單個key的value小于10KB
  • 對于集合類型的key,建議元素數(shù)量小于1000

2.1 BigKey的危害

網(wǎng)絡(luò)阻塞

  • 對BigKey執(zhí)行讀請求時,少量的QPS就可能導(dǎo)致帶寬使用率被占滿,導(dǎo)致Redis實(shí)例,乃至所在物理機(jī)變慢

數(shù)據(jù)傾斜

  • BigKey所在的Redis實(shí)例內(nèi)存使用率遠(yuǎn)超其他實(shí)例,無法使數(shù)據(jù)分片的內(nèi)存資源達(dá)到均衡

Redis阻塞

  • 對元素較多的hash、list、zset等做運(yùn)算會耗時較舊,使主線程被阻塞

CPU壓力

  • 對BigKey的數(shù)據(jù)序列化和反序列化會導(dǎo)致CPU的使用率飆升,影響Redis實(shí)例和本機(jī)其它應(yīng)用

2.2 如何發(fā)現(xiàn)BigKey

①redis-cli --bigkeys

利用redis-cli提供的–bigkeys參數(shù),可以遍歷分析所有key,并返回Key的整體統(tǒng)計信息與每個數(shù)據(jù)的Top1的big key

命令:redis-cli -a 密碼 --bigkeys

在這里插入圖片描述

②scan掃描

自己編程,利用scan掃描Redis中的所有key,利用strlen、hlen等命令判斷key的長度(此處不建議使用MEMORY USAGE)

在這里插入圖片描述

scan 命令調(diào)用完后每次會返回2個元素,第一個是下一次迭代的光標(biāo),第一次光標(biāo)會設(shè)置為0,當(dāng)最后一次scan 返回的光標(biāo)等于0時,表示整個scan遍歷結(jié)束了,第二個返回的是List,一個匹配的key的數(shù)組

import com.heima.jedis.util.JedisConnectionFactory;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.ScanResult;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class JedisTest {
    private Jedis jedis;

    @BeforeEach
    void setUp() {
        // 1.建立連接
        // jedis = new Jedis("192.168.150.101", 6379);
        jedis = JedisConnectionFactory.getJedis();
        // 2.設(shè)置密碼
        jedis.auth("123321");
        // 3.選擇庫
        jedis.select(0);
    }

    final static int STR_MAX_LEN = 10 * 1024;
    final static int HASH_MAX_LEN = 500;

    @Test
    void testScan() {
        int maxLen = 0;
        long len = 0;

        String cursor = "0";
        do {
            // 掃描并獲取一部分key
            ScanResult<String> result = jedis.scan(cursor);
            // 記錄cursor
            cursor = result.getCursor();
            List<String> list = result.getResult();
            if (list == null || list.isEmpty()) {
                break;
            }
            // 遍歷
            for (String key : list) {
                // 判斷key的類型
                String type = jedis.type(key);
                switch (type) {
                    case "string":
                        len = jedis.strlen(key);
                        maxLen = STR_MAX_LEN;
                        break;
                    case "hash":
                        len = jedis.hlen(key);
                        maxLen = HASH_MAX_LEN;
                        break;
                    case "list":
                        len = jedis.llen(key);
                        maxLen = HASH_MAX_LEN;
                        break;
                    case "set":
                        len = jedis.scard(key);
                        maxLen = HASH_MAX_LEN;
                        break;
                    case "zset":
                        len = jedis.zcard(key);
                        maxLen = HASH_MAX_LEN;
                        break;
                    default:
                        break;
                }
                if (len >= maxLen) {
                    System.out.printf("Found big key : %s, type: %s, length or size: %d %n", key, type, len);
                }
            }
        } while (!cursor.equals("0"));
    }
    
    @AfterEach
    void tearDown() {
        if (jedis != null) {
            jedis.close();
        }
    }

}

③第三方工具

④網(wǎng)絡(luò)監(jiān)控

  • 自定義工具,監(jiān)控進(jìn)出Redis的網(wǎng)絡(luò)數(shù)據(jù),超出預(yù)警值時主動告警
  • 一般阿里云搭建的云服務(wù)器就有相關(guān)監(jiān)控頁面

在這里插入圖片描述

2.3 如何刪除BigKey

BigKey內(nèi)存占用較多,即便時刪除這樣的key也需要耗費(fèi)很長時間,導(dǎo)致Redis主線程阻塞,引發(fā)一系列問題。

redis 3.0 及以下版本

  • 如果是集合類型,則遍歷BigKey的元素,先逐個刪除子元素,最后刪除BigKey

在這里插入圖片描述

Redis 4.0以后

  • Redis在4.0后提供了異步刪除的命令:unlink

3 恰當(dāng)?shù)臄?shù)據(jù)類型

例1:比如存儲一個User對象,我們有三種存儲方式:

①方式一:json字符串

user:1{“name”: “Jack”, “age”: 21}

優(yōu)點(diǎn):實(shí)現(xiàn)簡單粗暴

缺點(diǎn):數(shù)據(jù)耦合,不夠靈活

②方式二:字段打散

user:1:nameJack
user:1:age21

優(yōu)點(diǎn):可以靈活訪問對象任意字段

缺點(diǎn):占用空間大、沒辦法做統(tǒng)一控制

③方式三:hash(推薦)

user:1namejack
age21

優(yōu)點(diǎn):底層使用ziplist(壓縮列表),空間占用小,可以靈活訪問對象的任意字段

缺點(diǎn):代碼相對復(fù)雜(有工具類可以方便實(shí)現(xiàn))

例2:假如有hash類型的key,其中有100萬對field和value,field是自增id,這個key存在什么問題?如何優(yōu)化?

keyfieldvalue
someKeyid:0value0
..........
id:999999value999999

存在的問題:

hash的entry數(shù)量超過500時,會使用哈希表而不是ZipList,內(nèi)存占用較多

在這里插入圖片描述

可以通過hash-max-ziplist-entries配置entry上限。但是如果entry過多就會導(dǎo)致BigKey問題

方案一

拆分為string類型

keyvalue
id:0value0
..........
id:999999value999999

存在的問題:

string結(jié)構(gòu)底層沒有太多內(nèi)存優(yōu)化,內(nèi)存占用較多

在這里插入圖片描述

想要批量獲取這些數(shù)據(jù)比較麻煩

方案二

拆分為小的hash,將 id / 100 作為key, 將id % 100 作為field,這樣每100個元素為一個Hash

keyfieldvalue
key:0id:00value0
..........
id:99value99
key:1id:00value100
..........
id:99value199
....
key:9999id:00value999900
..........
id:99value999999

在這里插入圖片描述

package com.heima.test;

import com.heima.jedis.util.JedisConnectionFactory;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.ScanResult;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class JedisTest {
    private Jedis jedis;

    @BeforeEach
    void setUp() {
        // 1.建立連接
        // jedis = new Jedis("192.168.150.101", 6379);
        jedis = JedisConnectionFactory.getJedis();
        // 2.設(shè)置密碼
        jedis.auth("123321");
        // 3.選擇庫
        jedis.select(0);
    }

    @Test
    void testSetBigKey() {
        Map<String, String> map = new HashMap<>();
        for (int i = 1; i <= 650; i++) {
            map.put("hello_" + i, "world!");
        }
        jedis.hmset("m2", map);
    }

    @Test
    void testBigHash() {
        Map<String, String> map = new HashMap<>();
        for (int i = 1; i <= 100000; i++) {
            map.put("key_" + i, "value_" + i);
        }
        jedis.hmset("test:big:hash", map);
    }

    @Test
    void testBigString() {
        for (int i = 1; i <= 100000; i++) {
            jedis.set("test:str:key_" + i, "value_" + i);
        }
    }

    @Test
    void testSmallHash() {
        int hashSize = 100;
        Map<String, String> map = new HashMap<>(hashSize);
        for (int i = 1; i <= 100000; i++) {
            int k = (i - 1) / hashSize;
            int v = i % hashSize;
            map.put("key_" + v, "value_" + v);
            if (v == 0) {
                jedis.hmset("test:small:hash_" + k, map);
            }
        }
    }

    @AfterEach
    void tearDown() {
        if (jedis != null) {
            jedis.close();
        }
    }
}

4 總結(jié)

Key的最佳實(shí)踐

固定格式:[業(yè)務(wù)名]:[數(shù)據(jù)名]:[id]

足夠簡短:不超過44字節(jié)

不包含特殊字符

Value的最佳實(shí)踐:

  • 合理的拆分?jǐn)?shù)據(jù),拒絕BigKey
  • 選擇合適數(shù)據(jù)結(jié)構(gòu)
  • Hash結(jié)構(gòu)的entry數(shù)量不要超過1000
  • 設(shè)置合理的超時時間

到此這篇關(guān)于Redis鍵值設(shè)計的具體實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Redis鍵值設(shè)計內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

  • 一文弄懂Redis 線程模型

    一文弄懂Redis 線程模型

    使用Redis 時,幾乎不存在 CPU 成為瓶頸的情況, Redis 主要受限于內(nèi)存和網(wǎng)絡(luò) 使用了單線程后,可維護(hù)性高,感興趣的可以了解一下
    2024-02-02
  • Windows下安裝Redis的流程詳解

    Windows下安裝Redis的流程詳解

    Redis作為常用開源的非關(guān)系型數(shù)據(jù)庫,是開發(fā)中常用的數(shù)據(jù)庫之一,很多朋友不清楚Windows下安裝Redis的過程,今天小編通過分享本文給大家介紹詳細(xì)過程,一起看看吧
    2021-08-08
  • Redis配置文件redis.conf詳細(xì)配置說明

    Redis配置文件redis.conf詳細(xì)配置說明

    本文列出了Redis的配置文件redis.conf的各配置項(xiàng)的詳細(xì)說明,簡單易懂
    2018-03-03
  • redis實(shí)現(xiàn)簡單分布式鎖

    redis實(shí)現(xiàn)簡單分布式鎖

    這篇文章主要介紹了redis實(shí)現(xiàn)簡單分布式鎖,文中通過代碼示例講解的非常詳細(xì),需要的朋友可以參考下
    2013-09-09
  • redis的bigkey掃描腳本深入介紹

    redis的bigkey掃描腳本深入介紹

    這篇文章主要給大家介紹了關(guān)于redis的bigkey掃描腳本的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-07-07
  • redis實(shí)現(xiàn)主從模式(1主2從)

    redis實(shí)現(xiàn)主從模式(1主2從)

    本文主要介紹了在Windows環(huán)境下搭建和測試Redis的主從復(fù)制模式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-12-12
  • Redis消息隊(duì)列實(shí)現(xiàn)異步秒殺功能

    Redis消息隊(duì)列實(shí)現(xiàn)異步秒殺功能

    在高并發(fā)場景下,為了提高秒殺業(yè)務(wù)的性能,可將部分工作交給 Redis 處理,并通過異步方式執(zhí)行,Redis 提供了多種數(shù)據(jù)結(jié)構(gòu)來實(shí)現(xiàn)消息隊(duì)列,總結(jié)三種,本文詳細(xì)介紹Redis消息隊(duì)列實(shí)現(xiàn)異步秒殺功能,感興趣的朋友一起看看吧
    2025-04-04
  • 利用redis實(shí)現(xiàn)分布式鎖,快速解決高并發(fā)時的線程安全問題

    利用redis實(shí)現(xiàn)分布式鎖,快速解決高并發(fā)時的線程安全問題

    這篇文章主要介紹了利用redis實(shí)現(xiàn)分布式鎖,快速解決高并發(fā)時的線程安全問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • 解決redis修改requirepass后不生效的問題

    解決redis修改requirepass后不生效的問題

    今天小編就為大家分享一篇解決redis修改requirepass后不生效的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05
  • 無法連接redis服務(wù)器問題的解決辦法(非常詳細(xì)!)

    無法連接redis服務(wù)器問題的解決辦法(非常詳細(xì)!)

    這篇文章主要介紹了如何解決Spring?Boot項(xiàng)目連接Redis失敗的問題,通過修改Redis配置文件、添加防火墻白名單或關(guān)閉防火墻,并使用RESP工具進(jìn)行測試,需要的朋友可以參考下
    2025-02-02

最新評論