https://cloud.tencent.com/developer/article/1159061 awk 30分鐘入門
磁盤尋址基礎(chǔ)知識普及
隨著文件變大,速度為何變慢?
- 硬盤IO成為瓶頸. 全量IO大.
CPU, 高速緩存, RAM, SSD, HDD的性能差異?
RAM尋址 ns級
HDD尋址 ms級
RAM比HDD快了10W倍. 高了5個數(shù)量級.
SSD尋址: 通過IOPS來顯示4KB頁大小文件的隨機(jī)讀寫性能
簡介
全稱: remote dictionary server
開源的基于C語言實(shí)現(xiàn)的內(nèi)存數(shù)據(jù)結(jié)構(gòu)存儲系統(tǒng), 可以用作數(shù)據(jù)庫, 緩存和消息中間件.
高性能
讀取 10W次/秒
寫入 8W次/秒
高可用:
Redis3.0支持分布式集群, 集群可以是多主多從, 當(dāng)主節(jié)點(diǎn)發(fā)生異常不可用時, 可由其對應(yīng)的從節(jié)點(diǎn)頂替, 以保持整個集群的高可用.
易于拓展:
Redis3.0支持分布式集群. Redis所有的數(shù)據(jù)都存儲在16384個槽(slot)中, 創(chuàng)建集群時, 需要把槽分配給各個主節(jié)點(diǎn).
擴(kuò)容時,只要把槽重新分配給新的主節(jié)點(diǎn), 然后開啟數(shù)據(jù)遷移即可. 在擴(kuò)容過程中集群仍然是可用的.
原子性:
Redis是單線程的, 其所有的操作都是原子性的. 避免了多線程帶來的復(fù)雜性.
可持久化:
Redis支持?jǐn)?shù)據(jù)的持久化, 可以將內(nèi)存中的數(shù)據(jù)保存在磁盤中, 重啟的時候可以再次加載進(jìn)行使用.
總結(jié):
在實(shí)際工作中, 我們之所以選擇使用redis的原因, 主要是因?yàn)槠涓咝阅? 以及數(shù)據(jù)類型豐富.
支持的數(shù)據(jù)結(jié)構(gòu)和使用場景
-
string
incr, decr 計(jì)數(shù)器
setnx, expire, del 分布式鎖
setbit, bitcount, bitop 統(tǒng)計(jì)
set, get 存儲對象 (使用json存儲序列化的不常變化的部分. 因?yàn)槎M(jìn)制安全,也可以存儲小文件)
源碼為: t_string.c
checkStringLength函數(shù)校驗(yàn),string的長度小于 512 * 1024 * 1024即512MB.
-
hash
hset, hget, hdel, hincrby, hgetall 購物車(用戶id為key, 商品id為field, 商品數(shù)量為value, 通過hincreby 調(diào)整購物車中商品數(shù)量, hgetall 全選, hdel 刪除購物車中指定商品, )
hset, hincrby 存儲對象(頻繁變化的部分)
-
list
lpush, rpop 消息隊(duì)列
lpush, lpop 棧
rpush, lrange 排行榜(定時計(jì)算的, 不支持實(shí)時)
lpush, lrange 最新列表 (不需要按時間范圍查詢 && (不需要分頁 || 更新頻率低))
-
set
sismember, scard, smove 好友/關(guān)注/粉絲/感興趣的人集合
sranmember 隨機(jī)展示, 抽獎
sismember 黑名單/白名單
sorted set
支持范圍查詢, bitmaps, hyperloglog, geospatial索引半徑查詢.
redis內(nèi)置了: 復(fù)制replica, LUA腳本, LRU驅(qū)動事件, 事務(wù), 和不同級別的磁盤數(shù)據(jù)持久化.
通過哨兵sentinel和自動分區(qū)cluster提供高可用性high availability
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="c" cid="n103" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">/**
- redis中, string,hash,list,set,sortedset都通過如下一種類型進(jìn)行存儲.
<< server.h >>
/
/ Objects encoding. Some kind of objects like Strings and Hashes can be - internally represented in multiple ways. The 'encoding' field of the object
- is set to one of this fields for this object. */
define OBJ_ENCODING_RAW 0 /* Raw representation */
define OBJ_ENCODING_INT 1 /* Encoded as integer */
define OBJ_ENCODING_HT 2 /* Encoded as hash table */
define OBJ_ENCODING_ZIPMAP 3 /* Encoded as zipmap */
define OBJ_ENCODING_LINKEDLIST 4 /* No longer used: old list encoding. */
define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
define OBJ_ENCODING_INTSET 6 /* Encoded as intset */
define OBJ_ENCODING_SKIPLIST 7 /* Encoded as skiplist */
define OBJ_ENCODING_EMBSTR 8 /* Embedded sds string encoding */
define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */
define OBJ_ENCODING_STREAM 10 /* Encoded as a radix tree of listpacks */
?
define LRU_BITS 24
define LRU_CLOCK_MAX ((1<<LRU_BITS)-1) /* Max value of obj->lru */
define LRU_CLOCK_RESOLUTION 1000 /* LRU clock resolution in ms */
?
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;</pre>
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="shell" cid="n256" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;"># --------------------------------------- GET, SET, STRLEN, MSET, MGET,
設(shè)置key
set key1 boa
OK
?
獲取key對應(yīng)的value
get key1
"boa"
?
獲取指定key的值的長度
STRLEN key10
(integer) 10
?
同時設(shè)置多個key-value對
MSET k1 value1 k2 value2
OK
?
同時獲取多個key-value對
MGET k1 k2
- "value1"
- "value2"
?
--------------------------------------- GETRANGE, SETRANGE
設(shè)置key
set key10 0123456789
OK
?
獲取 key對應(yīng)值的子串. index從0開始. [startIndex, endIndex]
GETRANGE key10 0 5
"012345"
?
指定字符串的指定位置開始進(jìn)行覆蓋設(shè)置. 1個字符
SETRANGE key10 10 X
(integer) 13
?
get key10
"0123456789Xbc"
?
指定字符串的指定位置開始進(jìn)行覆蓋設(shè)置. 多個字符
SETRANGE key10 10 XXXX
(integer) 14
?
get key10
"0123456789XXXX"
?
--------------------------------------- GETSET
set key1 oldvalue
OK
?
設(shè)置key,并返回舊值
GETSET key1 newvalue
"oldvalue"
?
--------------------------------------- APPEND
在指定key的值后面追加新字符串
APPEND key10 abc
(integer) 13
GET key10
"0123456789abc"
?
--------------------------------------- SETNX
當(dāng)key不存在時設(shè)置值. 成功返回1, 失敗返回0
SETNX keynx 1
(integer) 1
?
已存在時
SETNX keynx 1
(integer) 0
?
已存在時
SETNX keynx 0
(integer) 0</pre>
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="shell" cid="n267" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;">> set num1 1
OK
?
get num1
"1"
?
---------------------- INCR, DECR, INCRBY, DECRBY
加1 當(dāng)key不存在時設(shè)置并返回1
INCR num1
(integer) 2
?
get num1
"2"
?
減1 當(dāng)key不存在時設(shè)置并返回-1
DECR num1
(integer) 1
?
get num1
"1"
?
加指定數(shù)值
INCRBY num1 10
(integer) 11
?
減指定數(shù)值
DECRBY num1 10
(integer) 1</pre>
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="shell" cid="n284" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;"># -------------------------- SETBIT, BITCOUNT
?
setbit 將指定key的第一個位設(shè)置為1. 返回old值或者默認(rèn)值0
SETBIT bit1 1 1
(integer) 0
SETBIT bit1 2 1
(integer) 0
setbit bit1 3 1
(integer) 0
統(tǒng)計(jì)為1的個數(shù), 返回3
bitcount bit1
(integer) 3
?
將值都寫為0, 返回值1
setbit bit1 1 0
(integer) 1
setbit bit1 2 0
(integer) 1
setbit bit1 3 0
(integer) 1
統(tǒng)計(jì)為1的個數(shù), 返回0
bitcount bit1
(integer) 0
?
?
-------------------------- BITOP
SETBIT 01 1 1
(integer) 0
?
GETBIT 01 1
(integer) 1
?
SETBIT 02 1 1
(integer) 0
?
GETBIT 02 1
(integer) 1
?
使01和02這兩個數(shù)值做或運(yùn)算,結(jié)果存入destkey中
BITOP OR destkey 01 02
(integer) 1
?
BITCOUNT destkey
(integer) 1
?</pre>
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="shell" cid="n290" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;"># 用戶名為KEY, 一年365天. 在具體的天數(shù)所在位上設(shè)置為1. 則根據(jù)用戶名進(jìn)行匯總,得到登錄天數(shù).
統(tǒng)計(jì)一個用戶需要46 * 8bit = 46Byte.
假設(shè)1000W用戶,則需要使用 10000000 * 46Byte = 460000000字節(jié)(b) = 438.6901855兆字節(jié)(mb)
setbit zhangsan 1 1
(integer) 0
setbit zhangsan 364 1
(integer) 0
登陸天數(shù)通過bitcount
bitcount zhangsan
(integer) 2
STRLEN zhangsan
(integer) 46
GETRANGE zhangsan 0 -1
"@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\b"</pre>
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="shell" cid="n296" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;"># 思路: 使用日期作為key, 用戶ID的int值作為bit位.
假如20200101日, 1001和1991兩個客戶都登陸過; 20200102只有1001客戶登陸過. 則統(tǒng)計(jì)結(jié)果預(yù)期為2人.
setbit 20200101 1001 1
(integer) 0
setbit 20200101 1991 1
(integer) 0
setbit 20200102 1001 1
(integer) 0
bitop or destkey 20200101 20200102
(integer) 249
bitcount destkey
(integer) 2</pre>
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="shell" cid="n305" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background-color: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; background-position: initial initial; background-repeat: initial initial;"># 實(shí)現(xiàn)棧 (同向命令)
lpush list1 1
(integer) 1
?
lpush list1 2
(integer) 2
?
lpush list1 3
(integer) 3
?
LRANGE list1 0 -1
- "3"
- "2"
- "1"
?
?
實(shí)現(xiàn)隊(duì)列 (反向命令)
lpush FIFO 1
(integer) 1
lpush FIFO 2
(integer) 2
lpush FIFO 3
(integer) 3
rpop FIFO
"1"
rpop FIFO
"2"
rpop FIFO
"3"
?
?
數(shù)組
堵塞的單播隊(duì)列. (FIFO)
127.0.0.1:6380> BRPOP FIFO 10000 //堵塞,指導(dǎo)另一個線程將數(shù)據(jù)PUSH進(jìn)去
- "FIFO"
- "1"
(106.64s)
?
?
127.0.0.1:6380> RPUSH FIFO 1 //連接2, 向其中push.
(integer) 1</pre>
緩存的沒key設(shè)值隨機(jī)時間
設(shè)值緩存永不過期, 數(shù)據(jù)更新之后單獨(dú)做更新
解決方案
電商首頁基礎(chǔ)數(shù)據(jù)緩存, 定時集中刷新緩存
9點(diǎn), 15點(diǎn), 24點(diǎn)等各種關(guān)鍵時刻的跨日參數(shù)集中刷新
場景
指的是并打量大, 當(dāng)大片key緩存集中失效, 導(dǎo)致所有并發(fā)打到db; 數(shù)據(jù)庫重啟恢復(fù)之后,還是會被新流量打死; 如果沒做熔斷, 將很難恢復(fù)掰烟。
雪崩
雪崩,擊穿,穿透問題
http://www.reibang.com/p/05cfc604ded0
代碼整合
使用場景
常用命令
[圖片上傳失敗...(image-d824a3-1592100524919)]
結(jié)構(gòu)圖
可以存儲約2^23-1條數(shù)據(jù). 約40億.
List
- 比如過節(jié)時,做活動,需要備貨.需要統(tǒng)計(jì)1號和2號的累計(jì)用戶登錄數(shù), 同一個用戶2天都登陸, 只算做1個人.
統(tǒng)計(jì)用戶最近一年內(nèi)的登錄天數(shù)
使用場景:
bitmap 位圖
搶購
秒殺
點(diǎn)贊數(shù)量
評論數(shù)量
規(guī)避并發(fā)下對DB的事務(wù)操作. 完全由REDIS內(nèi)存操作代替. 可以后續(xù)異步回寫到DB中.
使用場景
數(shù)值
簡單緩存. eg: 商品詳情數(shù)據(jù)json串
分布式鎖 setnx
使用場景:
常用函數(shù):
字符串
最基本的字符串類型. 但是在支持字符串同時,還兼容了數(shù)值和bitmap操作.
String
Redis各個數(shù)據(jù)類型使用DEMO
redis二進(jìn)制安全. 存儲的是字節(jié)流
如果客戶端使用的是GBK編碼, 則存儲的都是經(jīng)過GBK編碼后的字節(jié)流. 如果客戶端通過GBK編碼則能正常編碼展示.否則就會亂碼.
Q: Redis存儲String類型的值,是一什么方式存儲? 字節(jié)流還是字符流?
redis管理客戶端連接的方式, 以及與kernel交互的方式采用的是epoll
[圖片上傳失敗...(image-bd3e0-1592100524917)]
Q: Redis單進(jìn)程,單線程,但實(shí)例, 并發(fā)很多的請求,如何變得很快呢?
[圖片上傳失敗...(image-7b67c1-1592100524919)]
ctl add del sfd wait()
read(fd)
mmap 用戶空間和內(nèi)核空間共享空間. 紅黑樹和鏈表
epoll()函數(shù)調(diào)用內(nèi)核.
對于多個client連接, 只是用一個線程進(jìn)行維護(hù)連接. 用戶空間通過create epfd,
多路復(fù)用NIO
[圖片上傳失敗...(image-1daea5-1592100524919)]
多路復(fù)用NIO
fd相關(guān)的數(shù)據(jù)會拷貝來去.
輪詢發(fā)生在用戶空間
對于多個client連接, 只是用一個線程進(jìn)行維護(hù)連接. 然后通過select(Set<fd>) 調(diào)用kernel檢測IO資源狀態(tài)是否就緒, kernel返回資源狀態(tài). 用戶態(tài)再通過read(fd)函數(shù)進(jìn)行讀取數(shù)據(jù).
多路復(fù)用NIO
[圖片上傳失敗...(image-ca8581-1592100524919)]
socket fd nonblock 同步非阻塞 NIO
如果有1000fd, 代表用戶進(jìn)程輪詢調(diào)用1000次kernel, 用戶空間輪詢成本高.
輪詢發(fā)生在用戶空間
對于多個client連接, 只使用一個線程維護(hù)連接, 然后通過輪詢訪問kernel的IO資源. 哪個資源就緒, 就返回給線程進(jìn)行處理.
NIO
socket就是在這個時期blocking
如果有1000個連接, 則有000個線程, 內(nèi)存成本高
JVM 一個線程的成本大約1MB
-
無輪詢, 但是線程調(diào)度CPU浪費(fèi)嚴(yán)重.
[圖片上傳失敗...(image-3969b0-1592100524917)]
針對每個client連接維護(hù)一個獨(dú)立線程, 然后通過線程訪問kernel, 獲取IO內(nèi)容.
BIO
網(wǎng)絡(luò)連接并發(fā)場景下, 以下IO方式.
關(guān)于IO的插曲
一個物理機(jī)可以啟動多個實(shí)例. 只需要不同網(wǎng)絡(luò)端口區(qū)分即可.
1切诀,yum install wget
2,cd ~
3,mkdir soft
4,cd soft
5,wget http://download.redis.io/releases/redis-5.0.5.tar.gz
6,tar xf redis...tar.gz
7,cd redis-src
8,看README.md
9, make
....yum install gcc
.... make distclean
10,make
11,cd src ....生成了可執(zhí)行程序
12, cd ..
13,make install PREFIX=/opt/mashibing/redis5
14,vi /etc/profile
... export REDIS_HOME=/opt/mashibing/redis5
... export PATH=REDIS_HOME/bin
..source /etc/profile
15,cd utils
16,./install_server.sh (可以執(zhí)行一次或多次)
a) 一個物理機(jī)中可以有多個redis實(shí)例(進(jìn)程)喜鼓,通過port區(qū)分
b) 可執(zhí)行程序就一份在目錄,但是內(nèi)存中未來的多個實(shí)例需要各自的配置文件猿涨,持久化目錄等資源
c) service redis_6379 start/stop/stauts > linux /etc/init.d/
d)腳本還會幫你啟動!
17,ps -fe | grep redis
Linux環(huán)境安裝步驟
-
mem 只支持key,value存儲. value為字符串. value沒有其他類型概念.
需要客戶端層進(jìn)行封裝和拆解.
redis雖然也是key,value存儲. 但是value的類型更加豐富. 很多類型操作由服務(wù)端承擔(dān),提供了更多靈活和高性能的api.
與Memcached對比
文檔
當(dāng)一個對象比較龐大且很少去變動時,采用string進(jìn)行直接存儲. 如果需要更新,則直接覆蓋更新.
當(dāng)一個對象經(jīng)常變動,且很少去轉(zhuǎn)換為對象使用, 多以單個屬性值使用時, 可以使用hash. 存取更新方便,存取粒度細(xì).
結(jié)合表格我們可以得出結(jié)論:
比較\方案 | string: json | hash: key + field + value |
---|---|---|
效率 | 很高 | 高 |
容量 | 低 | 低 |
靈活性 | 低 | 高 |
序列化 | 簡單 | 復(fù)雜 |
在Hash的場景下, 可以通過key + field + value存儲. 那么這兩種存儲方式的優(yōu)缺點(diǎn)如何呢?
比如在String的場景下, 可以通過String+json的方式存儲
關(guān)于如何在緩存中存儲對象**