文章目錄
- 0.概述
- 0.1Redis數據類型 中文官網 (不推薦,更新不及時)
- 0.2.Redis數據類型 英文官網 (推薦)
- 0.3.key的最大大小:512M
- 0.4.value的最大大小:512M
- 0.5.可以存儲的數據類型:字符串,整數,浮點型
- 1.String 字符串類型的相關命令
- 1.2.簡單的賦值:set key1 value1
- 1.2.2.批量操作
- 1.2.3.如果key不存在,則設置key的值爲value:setnx key value
- 1.2.4.刪除key(這個可以作爲釋放鎖的命令):del frank_age
- 1.2.4.1.設置10s後過期:set key value ex 10
- 1.2.4.2.單獨設置過期時間10s:EXPIRE name11 10
- 1.2.4.3.如果不存在的時候,設置key的值並且過期時間爲10s:set name12 gaoxinfu12 ex 10 nx
- 1.2.4.4.只有key存在的時候,設置key的值並且過期時間爲10s:set name13 gaoxinfu13_new ex 10 xx
- 1.2.5.value爲數值類型的操作
- 1.2.5.1.設置值爲數值:set frank_age 18
- 1.2.5.2.減少1:dec frank_age
- 1.2.5.3.增加1:incr frank_age
- 1.2.5.4.增加n:incrby frank_age 10
- 1.2.5.5.減少n:DECRBY frank_age 2
- 1.2.6.value爲浮點類型的操作
- 1.2.7.追加內容:APPEND name2 frank
- 1.2.8.獲取字符串指定位置內容:getrange name2 9 14
- 1.2.9.其他命令參考:http://redisdoc.com/string/index.html
- 2.源碼
- 2.1.dictEntry數據結構-源碼
- 2.2.redisObject數據結構-源碼
- 2.2.1.type :指的是我們客戶端調用存儲的數據類型
- 2.2.2.encoding:字符串在redis中的字符編碼類型(主要是根據存儲值的大小去劃分)
- 2.2.2.1.int類型:存儲 8 個字節的長整型(long,2^63-1)
- 2.2.2.2.embstr類型:代表embstr 格式的SDS(Simple Dynamic String 簡單動態字符串), 存儲小於 44 個字節的字符串
- 2.2.2.3.raw類型:代表raw 格式的SDS,存儲大於 44 個字節的字符串(3.2 版本之前是 39 字節)
- 2.2.3.lru :垃圾回收的策略:
- 2.2.4.refcount:被引用次數(如果爲0,標示沒有對象在引用,可以被回收)
- 2.2.5.*ptr:真正的value數據存儲結構的地址
- 2.3.SDS數據結構-源碼:(sdshdr5,sdshdr8,sdshdr16,sdshdr32,sdshdr64)
- 3.爲什麼我們說redis String類型是二進制安全的?
- 4.爲什麼Redis用SDS實現字符串的存儲?爲啥不直接用C語言中的Char[]數組呢?
- 4.1.首先,C語言本身是沒有字符串這種數據類型,而是通過Char[]數組存儲,但是Char[]存儲存在下面問題?
- 4.1.1.C語言Char[]類型必須去提前分配數組的大小,意味着必須存儲足夠大的空間,否則會溢出;
- 4.1.2.如果獲取數組長度,必須遍歷字符數據,時間複雜度O(n)
- 4.1.3.C語言 對於字符串的變更會對字符數組進行內存的重新分配
- 4.1.4.通過從字符串開始到結尾碰到的第一個'\0'來標記字符串的結束,因此不能保存圖片、音頻、視頻、壓縮文件等二進制(bytes)保存的內容,二進制不安全
- 4.2.使用SDS存儲的優勢
- 5.String字符串類型應用場景
- 5.1.熱點數據的存儲
- 5.2.數據共享緩存<----分佈式
- 5.3.分佈式鎖<----setnx方法:大家可以設置失效時間
- 5.4.全局唯一的Id(原子性):分庫分表主鍵
- 5.5.計數器:如微博點贊數量,或者抽獎數量,文章的閱讀數量
- 5.6.限流:客戶訪問次數限制等 incr
- 6.關於SDS的概念的學習,可以參考下面的地址
1.redis String類型的數據類型,是二進制安全的,那麼我們如何理解這個二進制安全呢?
0.概述
0.1Redis數據類型 中文官網 (不推薦,更新不及時)
http://www.redis.cn/topics/data-types-intro
0.2.Redis數據類型 英文官網 (推薦)
https://redis.io/topics/data-types-intro
1.String類型:是最簡單的一種數據類型,key-value的形式存儲,key都是String類型
0.3.key的最大大小:512M
0.4.value的最大大小:512M
0.5.可以存儲的數據類型:字符串,整數,浮點型
下面設置如下信息內容:
gaoxinfu年齡:18歲
gaoxinfu性別:男
gaoxinfu收入:10000.10
127.0.0.1:6379> mset gaoxinfu_age 18 gaoxinfu_sex man gaoxinfu_salary 10000.10
OK
127.0.0.1:6379>
1.String 字符串類型的相關命令
1.2.簡單的賦值:set key1 value1
1.2.1.格式
set key value [expiration EX seconds|PX milliseconds] [NX|XX]
127.0.0.1:6379> set key1 value1
OK
127.0.0.1:6379> get key1
"value1"
127.0.0.1:6379>
1.2.2.批量操作
1.2.2.1.批量賦值:mset key2 value2 key3 value3
127.0.0.1:6379> mset key2 value2 key3 value3
OK
127.0.0.1:6379> get key2
"value2"
127.0.0.1:6379> get key3
"value3"
127.0.0.1:6379>
1.2.2.3.批量取值:mget name2 name3
127.0.0.1:6379> keys *
1) "name2"
2) "salary"
3) "name3"
4) "rename1"
5) "gaoxinfu_sex"
6) "gaoxinfu_age"
7) "key1"
8) "gaoxinfu_salary"
9) "frank_age"
10) "key3"
11) "key2"
127.0.0.1:6379> mget name2 name3
1) "gaoxinfu2"
2) "gaoxinfu3"
127.0.0.1:6379>
1.2.3.如果key不存在,則設置key的值爲value:setnx key value
1.2.3.2.設置失敗返回:0
由於key:gaoxinfu_age前面已經存在,所以設置的時候沒成功,返回了0
127.0.0.1:6379> setnx gaoxinfu_age 19
(integer) 0
127.0.0.1:6379>
1.2.3.2.設置成功返回:1
新創建的key:frank_age 設置成功
127.0.0.1:6379> setnx frank_age 19
(integer) 1
127.0.0.1:6379>
1.2.3.3.總結:setnx可以用來作爲分佈式鎖,進行資源的競爭,設置成功即爲獲得鎖;
setnx可以用來作爲分佈式鎖,進行資源的競爭,設置成功即爲獲得鎖;
當然,獲取完鎖之後,如果要釋放需要用del命令
1.2.4.刪除key(這個可以作爲釋放鎖的命令):del frank_age
127.0.0.1:6379> del frank_age
(integer) 1
127.0.0.1:6379>
1.2.4.1.設置10s後過期:set key value ex 10
設置10s後過期
127.0.0.1:6379> set name10 gaoxinfu ex 10
OK
127.0.0.1:6379>
10s後去查詢,已經不存在
127.0.0.1:6379> get name10
(nil)
127.0.0.1:6379>
1.2.4.2.單獨設置過期時間10s:EXPIRE name11 10
127.0.0.1:6379> set name11 gaoxnfu11
OK
127.0.0.1:6379> EXPIRE name11 10
(integer) 1
127.0.0.1:6379>
1.2.4.3.如果不存在的時候,設置key的值並且過期時間爲10s:set name12 gaoxinfu12 ex 10 nx
127.0.0.1:6379> set name12 gaoxinfu12 ex 10 nx
OK
127.0.0.1:6379>
10s後再次查詢,已經不存在
127.0.0.1:6379> get name12
(nil)
127.0.0.1:6379>
1.2.4.4.只有key存在的時候,設置key的值並且過期時間爲10s:set name13 gaoxinfu13_new ex 10 xx
127.0.0.1:6379> set name13 gaoxinfu13
OK
127.0.0.1:6379> set name13 gaoxinfu13_new ex 10 xx
OK
127.0.0.1:6379>
1.2.5.value爲數值類型的操作
1.2.5.1.設置值爲數值:set frank_age 18
127.0.0.1:6379> set frank_age 18
OK
127.0.0.1:6379> type frank_age
string
1.2.5.2.減少1:dec frank_age
127.0.0.1:6379> dec frank_age
(error) ERR unknown command `dec`, with args beginning with: `frank_age`,
127.0.0.1:6379> decr frank_age
(integer) 17
1.2.5.3.增加1:incr frank_age
127.0.0.1:6379> incr frank_age
(integer) 18
127.0.0.1:6379>
1.2.5.4.增加n:incrby frank_age 10
127.0.0.1:6379> INCRBY frank_age 10
(integer) 29
127.0.0.1:6379>
1.2.5.5.減少n:DECRBY frank_age 2
127.0.0.1:6379> DECRBY frank_age 2
(integer) 27
127.0.0.1:6379>
1.2.6.value爲浮點類型的操作
1.2.6.1.增加n:incrbyfloat salary 8.4
127.0.0.1:6379> set salary 1002.6
OK
127.0.0.1:6379> incrbyfloat salary 8.4
"1011"
127.0.0.1:6379>
1.2.6.2.減少n:incrbyfloat salary 8.4
1.2.7.追加內容:APPEND name2 frank
127.0.0.1:6379> get name2
"gaoxinfu2"
127.0.0.1:6379> APPEND name2 frank
(integer) 14
127.0.0.1:6379> get name2
"gaoxinfu2frank"
127.0.0.1:6379>
1.2.8.獲取字符串指定位置內容:getrange name2 9 14
127.0.0.1:6379> get name2
"gaoxinfu2frank"
127.0.0.1:6379> getrange name2 9 14
"frank"
127.0.0.1:6379>
其中 9是開始位置,14是結束位置
1.2.9.其他命令參考:http://redisdoc.com/string/index.html
http://redisdoc.com/string/index.html
2.源碼
1.首先,redis的數據存儲通過dictEntry去存儲;
2.1.dictEntry數據結構-源碼
typedef struct dictEntry {
void *key; # 這個就是我們的key
union {
void *val; # 這個就是我們的value,但是這裏的value是通過redisOject對象(見下面的分析)去存儲,
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next;# 這個dictEntry是指向下一個dictEntry的引用地址
} dictEntry;
2.2.redisObject數據結構-源碼
#define OBJ_SHARED_REFCOUNT INT_MAX
typedef struct redisObject {
unsigned type:4;
unsigned encoding:4;
unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
* LFU data (least significant 8 bits frequency
* and most significant 16 bits access time). */
int refcount;
void *ptr;
} robj;
2.2.1.type :指的是我們客戶端調用存儲的數據類型
127.0.0.1:6379> get name1
(nil)
127.0.0.1:6379> get name2
"gaoxinfu2frank"
127.0.0.1:6379> type name2
string
127.0.0.1:6379>
2.2.2.encoding:字符串在redis中的字符編碼類型(主要是根據存儲值的大小去劃分)
1.關於字節的長度問題,我們這裏不做過多介紹,根據不同的變成語言(C語言,Java語言)以及不同字符編碼有關係
比如在C語言中:
UTF-8編碼:一個英文字符等於一個字節,一箇中文(含繁體)等於三個字節。中文標點佔三個字節,英文標點佔一個字節
2.2.2.1.int類型:存儲 8 個字節的長整型(long,2^63-1)
127.0.0.1:6379> set gaoxinfu_age 10
OK
127.0.0.1:6379> type gaoxinfu_age
string
127.0.0.1:6379> object encoding gaoxinfu_age
"int"
127.0.0.1:6379>
2.2.2.2.embstr類型:代表embstr 格式的SDS(Simple Dynamic String 簡單動態字符串), 存儲小於 44 個字節的字符串
127.0.0.1:6379> set name4 gaoxinfu4
OK
127.0.0.1:6379> type name4
string
127.0.0.1:6379> type name4
string
127.0.0.1:6379> object encoding name4
"embstr"
127.0.0.1:6379>
2.2.2.3.raw類型:代表raw 格式的SDS,存儲大於 44 個字節的字符串(3.2 版本之前是 39 字節)
127.0.0.1:6379> set name3 我叫高新富我來自山東臨沂非常高興認識大家,歡迎大家來我的博客學習,謝謝大家
OK
127.0.0.1:6379> type name3
string
127.0.0.1:6379> object encoding name3
"raw"
127.0.0.1:6379>
2.2.3.lru :垃圾回收的策略:
2.2.3.1.lru:Least Recently Used 最近最久未使用算法
1.如果一個數據在最近一段時間沒有被訪問到,那麼可以認爲在將來它被訪問的可能性也很小。
因此,當空間滿時,最久沒有訪問的數據最先被置換(淘汰)。
2.2.3.2.lfu Frequently Used :最小頻率未使用算法
1.如果一個數據在最近一段時間很少被訪問到,那麼可以認爲在將來它被訪問的可能性也很小。
因此,當空間滿時,最小頻率訪問的數據最先被淘汰
2.2.4.refcount:被引用次數(如果爲0,標示沒有對象在引用,可以被回收)
2.2.5.*ptr:真正的value數據存儲結構的地址
2.3.SDS數據結構-源碼:(sdshdr5,sdshdr8,sdshdr16,sdshdr32,sdshdr64)
1.sdshdr5,sdshdr8,sdshdr16,sdshdr32,sdshdr64爲不同的SDS存儲類型
其中:sdshdr5 已經不用
sdshdr5標示:0-2^5=32byte
sdshdr8表示:2^8=64byte
sdshdr16表示:2^16=64K <---- 2^16=2
sdshdr32表示:2^32=4G
sdshdr64表示:2^64=16G
typedef char *sds;
/* Note: sdshdr5 is never used, we just access the flags byte directly.
* However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) sdshdr5 {
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* used */
uint8_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
uint16_t len; /* used */
uint16_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
uint32_t len; /* used */
uint32_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
uint64_t len; /* used */
uint64_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
2.3.1.len:字符串長度
2.3.2.alloc:分配的內存空間
2.3.3.flags:sds類型(sdshdr5,sdshdr8,sdshdr16,sdshdr32,sdshdr64)
2.3.4.buf:數據內容(C語言中使用字符數組)
比如我們的value是“gaoxinfu”,那麼buf[] 存儲的就是如下的字符:'g','a','o','x','i','n','f','u'
3.爲什麼我們說redis String類型是二進制安全的?
4.爲什麼Redis用SDS實現字符串的存儲?爲啥不直接用C語言中的Char[]數組呢?
4.1.首先,C語言本身是沒有字符串這種數據類型,而是通過Char[]數組存儲,但是Char[]存儲存在下面問題?
4.1.1.C語言Char[]類型必須去提前分配數組的大小,意味着必須存儲足夠大的空間,否則會溢出;
4.1.2.如果獲取數組長度,必須遍歷字符數據,時間複雜度O(n)
4.1.3.C語言 對於字符串的變更會對字符數組進行內存的重新分配
4.1.4.通過從字符串開始到結尾碰到的第一個’\0’來標記字符串的結束,因此不能保存圖片、音頻、視頻、壓縮文件等二進制(bytes)保存的內容,二進制不安全
4.2.使用SDS存儲的優勢
4.2.1.不用擔心內存溢出問題,如果需要會對SDS 進行擴容
4.2.2.獲取字符串長度時間複雜度爲O(1),因爲定義了len 屬性
4.2.3.通過“空間預分配”( sdsMakeRoomFor)和“惰性空間釋放”,防止多次重分配內存
4.2.4.判斷是否結束的標誌是len 屬性(它同樣以’\0’結尾是因爲這樣就可以使用C語言函數庫操作字符串的函數)
5.String字符串類型應用場景
5.1.熱點數據的存儲
1.直接存儲的redis中,後面訪問的時候會更快
比如像一些前端界面的緩存數據等等
比如:對象緩存,全頁數據緩存
5.2.數據共享緩存<----分佈式
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
1.因爲redis是分佈式部署的,所以不同的應用服務是可以調用同一個redis請求的,
所以不同的應用服務器可以進行數據共享;
5.3.分佈式鎖<----setnx方法:大家可以設置失效時間
http://redisdoc.com/string/set.html
redis> EXISTS job # job 不存在
(integer) 0
redis> SETNX job "programmer" # job 設置成功
(integer) 1
redis> SETNX job "code-farmer" # 嘗試覆蓋 job ,失敗
(integer) 0
redis> GET job # 沒有被覆蓋
"programmer"
5.4.全局唯一的Id(原子性):分庫分表主鍵
incrby orderid 20
5.5.計數器:如微博點贊數量,或者抽獎數量,文章的閱讀數量
1.微博點贊數量,或者抽獎數量,文章的閱讀數量,都是可以先寫寫入redis,再寫入數據庫的
5.6.限流:客戶訪問次數限制等 incr
1.可以使用客戶訪問的ip或者其他信息作爲key,存儲訪問的次數,一旦超了次數,則直接返回不允許訪問