天天看點

Redis 6.0 除了多線程,别忘了這個牛逼特性!

作者:鳥窩

https://urlify.cn/mYVVNf

Redis 6.0的新特性也是在一步步的讨論和優化中确定的。

很多的特性已經在之前的RC等版本中介紹過了。

但是正式GA版中也有一些新的變化:

  • SSL
  • ACL: 更好,指令支援
  • RESP3
  • Client side caching:重新設計
  • Threaded I/O
  • Diskless replication on replicas
  • Cluster support in Redis-benchmark and improved redis-cli cluster support
  • Disque in beta as a module of Redis: 開始侵入消息隊列領域
  • Redis Cluster Proxy
  • 支援RDB不再使用時可立即删除,針對不落盤的場景
  • PSYNC2: 優化的複制協定
  • 逾時設定支援更友好
  • 更快的RDB加載,20% ~ 30%的提升
  • STRALGO,新的字元串指令,目前隻有一個實作LCS (longest common subsequence)

@antirez 提到隻是Redis曆史上最大的一次版本更新,是以謹慎建議在應用的産品中還是多多測試評估,并且承諾一旦遇到大的bug就會緊急釋出6.0.1版。果不其然,一天後就釋出了 6.0.1版,修複了一個allocator的bug,這個bug是為了優化而引入的,現在暫時去掉了。

I just released Redis 6.0.1. Unfortunately there was a bug in Redis 6.0.0 introduced just a few days before the release, that only happens when using the non-default allocator (libc malloc in this case triggers it). Optimization reverted, 6.0.1 released. Sorry for the issue.

本文主要關注Client side caching(用戶端緩存)這一特性。

smallnest/RESP3 是Redis RESP3協定的解析庫,你可以使用它和Redis底層通訊,或者包裝它實作新版的Redis client庫或者Redis Server端。

一年前,當 @antirez 參加完紐約Redis大會後,5:30就在旅店中醒來了,面對曼哈頓街頭的美麗景色,在芸芸衆生中思索Redis的未來。包括用戶端緩存。

其實,用戶端緩存特性是收到Redis Conf 2018的Ben Malec的影響,一下子打開了 @antirez 思路。我們知道, 很多公司使用Redis做緩存系統,并且很好的提高了資料通路的性能,但是很多企業為了進一步應對熱點資料,還是會在redis的client端緩存一部分熱點資料,用來應對吃瓜事件。比如在微網誌我們經常遇到的是明星出軌、明星分分合合、突發事件等等,每年都會有幾次突發的事件,微網誌除了使用Redis做緩存避免直接通路資料庫,還會在前面加更多的cache層,比如

L1 cache

等,采用memcached等産品作為熱資料的緩存。那麼就有一個問題,如何能夠及時的同步這些cache和redis的資料呢?Ben提供了非常有意思的想法。

伫立在曼哈頓的街頭,@antirez 陷入了沉思,後來回到旅館他開始實作初版的用戶端的緩存。當然,最終Redis 6.0中實作和這個初版的實作差别很大,但是很是顯然,從用戶端的演化過程中我們還是能看到@antirez對這個特性所在的權衡(trade off)。關于這個曆史本文不做太多的介紹,因為我們更關注于這個特性最終是什麼樣子的。

Redis實作的是一個服務端協助的用戶端緩存,叫做

tracking

。用戶端緩存的指令是:

CLIENT TRACKING ON|OFF [REDIRECT client-id] [PREFIX prefix] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]
           

複制

tracking

開啟時, Redis會"記住"每個用戶端請求的key,當key的值發現變化時會發送失效資訊給用戶端(invalidation message)。失效資訊可以通過RESP3協定發送給請求的用戶端,或者轉發給一個不同的連接配接(支援RESP2+ Pub/Sub)。當廣播模式(broadcasting)開啟時,參與

tracking

的用戶端會收到它通過字首訂閱的key的相關的通知,即使它沒請求過對應的key。同時還提供了

OPTIN

OPTOUT

等模式。

失效消息:當一個key的資料有修改的時候,需要告訴用戶端它以前緩存的資料失效了,這時redis會主動發送一條失效消息
  • REDIRECT : 将失效消息轉發給另外一個用戶端。當我們不使用RESP3而是使用老的RESP2和Redis通訊時,client本身不支援處理失效消息,是以可以開啟一個支援Pub/Sub用戶端處理失效消息。當然如果用戶端支援RESP3也可以将失效消息轉發給另外一個用戶端。這個cace我們放在最後示範。
  • BCAST: 使用廣播模式開始

    tracking

    。在這種模式下用戶端需要設定将track的key的字首,這些key的失效消息會廣播給所有參與的用戶端,不管這些用戶端是否請求/緩存額這些key。不開始廣播模式時,Redis隻會track那些隻讀指令請求的key,并且隻會報告一次失效消息。
  • PREFIX : 隻應用了廣播模式,注冊一個key的字首。所有以這個字首開始的key有修改時,都會發送失效消息。可以注冊多個字首。如果不設定字首,那麼廣播模式會track每一個key。
  • OPTIN: 當廣播模式沒有激活時,正常不會track隻讀指令的key,除非它們在

    CLIENT CACHING yes

    之後被調用。
  • OPTOUT: 當廣播模式沒有激活時,正常會track隻讀指令的key,除非它們在

    CLIENT CACHING off

    之後被調用。
  • NOLOOP: 不發送client自己修改的key。

下面讓我們一一介紹每個選項。

測試環境搭建

首先讓我們介紹RESP3協定相關的選項,

REDIRECT <id>

放在最後介紹。

在嘗試之前,你首先需要安裝一個redis 6.x的版本,目前時6.0.1。在官方網站上有源代碼的下載下傳,編譯安裝也很簡單:

make distclean
make
make test
sudo make install
           

複制

相信很快就有編譯好的二進制包可以下載下傳。

啟動server, 它會在6379端口啟動一個服務:

redis-server
           

複制

使用

redis-cli

通路,預設通路本機的6379執行個體:

redis-cli
           

複制

當然你可以通過

-h

檢視額外的參數配置,比如使用其它端口等等,這裡我們使用最簡單的例子,重點是了解用戶端緩存的特性。

有時候為了更好的觀察redis的傳回結果,我們使用

telnet

而不是

redis-cli

作為client連接配接redis,因為redis-cli對結果做了處理,尤其是失效消息,你可能無法觀測到。

BCAST 廣播模式 (

client tracking on

)

啟動redis server:

Redis 6.0 除了多線程,别忘了這個牛逼特性!

啟動redis-cli:

Redis 6.0 除了多線程,别忘了這個牛逼特性!

當然,我們使用telnet來測試,友善觀察redis的傳回結果,剛才redis-cli用來更新key值,輔助測試。連接配接上之後發送

hello 3

開啟RESP3協定:

➜  ~ telnet localhost 6379
Trying ::1...
Connected to localhost.
Escape character is '^]'.
hello 3
%7
$6
server
$5
redis
$7
version
$5
6.0.1
......
           

複制

之後嘗試開啟

tracking

并讀取

a

的值:

client tracking on
+OK
set a 1
+OK
get a
$1
1
           

複制

這個時候如果使用redis-cli作為另外一個client更新

a

的值,telnet這個client應該能獲得通知:

127.0.0.1:6379> set a 2
OK
           

複制

觀察telnet,它收到了一個失效消息:

>2
$10
invalidate
*1
$1
a
           

複制

注意它采用RESP3中的PUSH類型(

>

)。

如果這個使用你再使用redis-cli更新

a

的值,telnet不會再收到失效消息。除非telnet client再

get a

一次,重新

tracking

a的值。

可以随時取消

tracking

:

client tracking off
           

複制

tracking特定字首的key (

client tracking on

)

上面的方式會tracking所有的key,如果你隻想跟蹤特定的key, 目前redis提供了一種方式,也就是字首比對的方式。你可以隻tracking特定字首的key。它值應用了廣播模式。

使用telnet client設定字首和開啟tracking:

hello 3
.......
client tracking on prefix a bcast
+OK
client tracking on prefix user bcast
+OK
           

複制

我們tracking兩個字首,以

a

開頭的所有的key和以

user

開頭的所有的key,所有

a

開頭的所有的key和以

user

開頭的所有的key(包括

a

user

)的key變動時它應該都收到消息。

然後我們使用redis-cli更新三個key:

abc

user:32432723213

feed:6532343243432

:

127.0.0.1:6379> set abc 100
OK
127.0.0.1:6379> set user:32432723213 good
OK
127.0.0.1:6379> set feed:6532343243432 abc
OK
           

複制

telnet client收到

abc

user:32432723213

的失效消息,而不會收到

feed:6532343243432

的失效消息:

>2
$10
invalidate
*1
$3
abc
>2
$10
invalidate
*1
$16
user:32432723213
           

複制

你可以通過

client tracking off

停止用戶端緩存。目前貌似不能隻停止對單個的字首的

tracking

。即使你使用

client tracking off prefix user

也是取消對所有的key的

tracking

......
} else if (!strcasecmp(c->argv[2]->ptr,"off")) {
    disableTracking(c);
} else {
......
           

複制

選擇加入

如果使用

OPTIN

,可以有選擇的開啟

tracking

。隻有你發送

client caching yes

之後的下一條的隻讀指令的key才會

tracking

, 否則其它的隻讀指令中的key不會tracking。

首先我們開始

optin

,讀取a的指,這個時候使用redis-cli client修改a的值為1000,我們并沒有收到

a

的失效消息。

client tracking on optin
+OK
get a
$1
2
           

複制

接下來我們發送

client caching yes

,緊接着擷取a的值,這個時候如果再修改a的值,你就可以收到一條a的失效消息:

client caching yes
+OK
get a
$4
1000
>2
$10
invalidate
*1
$1
a
           

複制

必須是緊跟着

client caching yes

嗎?是的,比如發送下面的指令,隻會

tracking

b,而不是a:

client caching yes
+OK
get b
_
get a
$4
2000
           

複制

選擇退出

如果使用

OPTOUT

,你也可以有選擇的退出

tracking

。隻有你發送

client caching off

之後的下一條的隻讀指令的key才會停止

tracking

, 否則其它的隻讀指令中的key都會被tracking。

可以看到它和

OPTIN

正好相反,你可以根據你的場景來選擇。

比如下面的例子,開啟

OPTOUT

之後,對任意的key的變動都收到失效消息:

client tracking on optout
+OK
get a
$4
3000
>2
$10
invalidate
*1
$1
a
           

複制

這個時候如果我們想排除

b

這個key,可以隻針對它進行設定:

client caching no
+OK
get b
$1
3
           

複制

之後對b的變動并不會收到b的失效消息。

注意:

OPTIN

OPTOUT

是針對的非BCAST場景,也就是隻有你發送了key的隻讀指令後,才會跟蹤相應的key。而廣播模式是無論你是否發送過key的隻讀指令,隻要redis修改了key,都會發送相應key(或者比對字首的key)的失效消息。

NOLOOP

正常設定時,失效消息是發給所有參與的client,但是如果設定了

NOLOOP

,則不會發送給更新這個key的client。

client tracking on bcast noloop
+OK
set a 1
+OK
client tracking off
+OK
client tracking on bcast
+OK
set a 1
+OK
>2
$10
invalidate
*1
$1
a
           

複制

注意,取消tracking隻需調用

client tracking off

即可。

REDIRECT

最後,讓我們看一下轉發消息的處理。這是為了相容RESP2協定一個處理方式,将失效消息轉發給另外一個client。

首先我們檢視redis-cli的client id:

127.0.0.1:6379> client list
id=4 addr=127.0.0.1:61017 fd=8 name= age=33103 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=client user=default
           

複制

使用telnet連接配接redis,檢視client id:

client id
:12
           

複制

telnet 用戶端開啟訂閱失效消息:

SUBSCRIBE __redis__:invalidate
*3
$9
subscribe
$20
__redis__:invalidate
:1
           

複制

然後我們就可以将redis-cli的失效消息轉發給telnet client:

client tracking on bcast redirect 12
127.0.0.1:6379> set a 1000
OK
           

複制

可以看到telnet用戶端收到了失效消息:

*3
$7
message
$20
__redis__:invalidate
*1
$1
a
           

複制

如果你要轉發的目的client開啟了RESP3協定,你就不需要RESP3 Pub/Sub了,因為RESP3原生支援Push消息。

redis的tracking feature的實作代碼在:tracking.c。