主頁 > 後端開發 > Redis6.0新特性簡述和驗證分析——ACL權限控制、TLS加密管理、多執行緒IO

Redis6.0新特性簡述和驗證分析——ACL權限控制、TLS加密管理、多執行緒IO

2021-09-29 07:58:13 後端開發

目錄

1. 簡介

2. 新功能測驗驗證

2.1 新功能——ACL權限控制

2.1.1 ACL簡介

2.1.2 ACL 引數決議

2.1.3 ACL 賦權配置及示例

(1)ACL權限持久化方式

(2)ACL權限持久化配置示例

2.1.4 ACL部分原始碼簡析

(1)ACL初始化

(2)ACL權限加載

(3)ACL用戶操作

2.2 新功能——TLS加密管理

2.2.1 TLS簡介

2.2.2 TLS 配置與示例

(1)TLS 編譯示例

2.2.3 Redis TLS使用示例

2.2.4 TLS部分原始碼簡析

(1)TLS初始化

(2)TLS讀寫

2.2.5 TLS局限性

2.3新功能——多執行緒IO

2.3.1 多執行緒IO簡介

2.3.2 多執行緒IO實作及配置示例

(1)多執行緒IO流程

(2)多執行緒IO引數決議

2.3.3多執行緒IO使用及性能對比

(1)Redis版本間對比測驗

(2)Redis CPU間性能對比測驗

2.3.4 多執行緒IO部分原始碼簡析

(1)多執行緒IO初始化

(2)多執行緒IO任務處理

3. 小結


1. 簡介

Redis(Remote Dictionary Server),即遠程字典服務,是一個使用 C撰寫的開源(BSD許可)、包含多種資料結構、支持網路、基于記憶體、可選持久性的鍵值對存盤資料庫,是現在最受歡迎的NoSQL資料庫之一,依賴于Redis的高性能、多資料型別等特性,Redis應用場景十分廣泛,可用于游戲快取應用、互聯網快取應用、電商高并發應用及其他快取加速訪問應用,能有效承載巨大的讀寫壓力,輕松實作高并發訪問,

Redis最新版本為Redis6.X,其中有Redis6.0.X及Redis6.2.X兩個分支,Redis6.0中已于2020年上半年發布,6.0版本最新已發布至6.0.15,Redis6.0帶來了諸多新功能:

  • 新增ACL權限控制,可實作賬號的權限管控;
  • 新增TLS加密管理,可實作Redis加密訪問控制;
  • 多執行緒IO,可實作單個Redis實體更高性能,

除此之外,Redis6.0還有RESP3協議、客戶端快取加速及redis-benchmark、redis-cli優化等新功能,

本文主要對Redis6.0的ACL權限管控、TLS加密管理及多執行緒IO進行了測驗驗證和決議,

2. 新功能測驗驗證

2.1 新功能——ACL權限控制

2.1.1 ACL簡介

Redis ACL 是Access Control List(訪問控制串列)的縮寫,該功能允許對訪問 Redis 的連接做一些可執行命令和可訪問 KEY 的限制,它的作業方式是,在連接之后,要求客戶端進行身份驗證,以提供用戶名和有效密碼,如果身份驗證成功,該客戶端連接與給定用戶系結,并具有該用戶的訪問權限,

Redis6.0版本之前,用戶只有一個default用戶,同一Redis實體所有讀寫操作都共享此用戶,難免出現誤操作洗掉資料或泄露資料情況,雖然可以使用rename命令禁用部分敏感操作,但也同時意味著需要進行敏感操作時,將有額外操作需要進行,權限管控復雜且不完善,在Redis6.0之后,通過ACL(權限管理)功能,可以設定不同的用戶并對其授權命令或資料權限,這一功能,可以有效降低用戶誤操作導致資料丟失或資料泄露風險,其中,密碼由SHA256進行加密,

Tips:linux下sha256加密方式:echo -n passowrd | sha256sum

2.1.2 ACL 引數決議

本章節簡要說明ACL常用命令,以及筆者使用時遇到的問題和需關注的地方,

常用的命令如下:

引數說明
CAT查看類別
DELUSER洗掉用戶
GENPASS創建密碼
GETUSER獲得用戶
HELP幫助
LIST查看用戶詳情
LOAD加載aclfile
SAVE保存至aclfile
SETUSER設定用戶
USERS查看用戶
WHOAMI查看當前用戶
LOG顯示日志

1)ACL CAT示例:

ACL將所有命令分為21子類,ACL可以較細粒度地進行權限劃分,但部分命令同時處于不同的權限子類中,設定多個子類權限時,需關注這些命令權限問題,

2)ACL LIST 示例:

通過ACL生成的用戶以如下default、test賬號為例,進行簡要說明:

引數說明
user test代表用戶是test
on代表用戶是啟用的,如果是off,代表用戶是禁用的,在off狀態下,登錄會失敗;
#e...cc7是sha256加密后的密碼串
~*~為添加指定模式的鍵(~*代表allkeys)
+代表用戶可以使用該命令,如果是 - ,代表用戶無法使用該命令
@用戶可以使用某類別命令,類別可以通過acl cat獲得

3)ACL SETUSER示例:

ACL SETUSER時,可通過“>”符號分次設定多個密碼且密碼均為有效;可通過“<”符號使密碼無效化,不建議同一賬號設定過多密碼,當密碼無法找回時,可先洗掉賬號再建立相同權限用戶方式更新密碼,以防有效密碼過多導致密碼泄露進而導致資料泄露,

對default用戶的權限設定需謹慎,筆者進行測驗時,設定了一個與default完全相同的admin用戶并將default用戶權限縮小,因操作原因出現了權限不可用情況,盡可能保留default權限不刪減,

普通用戶賦權后,可通過auth username password方式登錄普通賬號,此方式登錄需使用6.0及以上版本redis-cli客戶端,低版本客戶端無法識別,多版本混用需注意,

筆者測驗時,一個實體中生成7000+普通賬號,redis運行正常權限管控正常,示例如下:

acl setuser test1 >Admin123 ~* +get #賬號test1,密碼Admin123
acl setuser test2 >Admin123 ~* +set
acl setuser test2 >Admin123 ~* * +get +set
...
acl setuser test7000 >Admin123 ~* +@all
acl list #7000個用戶顯示正常
auth test7000 Admin123 #登陸正常

2.1.3 ACL 賦權配置及示例

(1)ACL權限持久化方式

通過上述的ACL SETUSER方式,對用戶的賬號進行了賦權后可進行持久化,Redis 實作ACL權限持久化的方式主要有兩種:

① redis.conf方式:直接將賬號密碼持久化保存在redis.conf中;

② aclfile方式:將賬號密碼保存至users.acl檔案中,并把users.acl 路徑寫入redis.conf,

兩種方式中,更推薦aclfile方式,因為redis.conf方式加載配置需要重啟Redis,而aclfile方式執行acl load即可,

此外,我們可以在客戶端對賬戶密碼進行操作,如果使用redis.conf方式,可通過config rewrite持久化;如果使用aclfile方式,可使用acl save進行持久化,

對比項redis.conf方式aclfile方式
加載配置重啟Redis執行acl load
持久化配置執行config rewrite執行acl save

(2)ACL權限持久化配置示例

兩種持久化方式的示例如下:

① redis.conf方式

cat redis.conf | grep "~*"可得:

② aclfile方式

  • cat users.acl,查看賬號密碼權限:

  • cat redis.conf | grep users.acl,查看aclfile檔案的位置:

Tips:redis6.2.X與redis6.0.X在acl權限控制上,明顯區別在于增加了 Pub/Sub channel (&*)管控,

2.1.4 ACL部分原始碼簡析

上面提到了ACL權限控制和持久化方式,下面對ACL初始化、權限加載、用戶操作原始碼進行簡要的決議,

(1)ACL初始化

在server.c的main函式中,有ACLInit()函式,ACLInit()中,主要為初始化ACL的結構,同時,ACLInitDefaultUse初始化默認用戶default,default用戶初始化時,具有全部權限及默認不需要密碼,

void ACLInit(void) {
    Users = raxNew();
    UsersToLoad = listCreate();
    ACLLog = listCreate();
    ACLInitDefaultUser();
    server.requirepass = NULL; /* Only used for backward compatibility. */
}

(2)ACL權限加載

在ACLInit()完成后,main函式中通過ACLLoadUsersAtStartup()進行ACL權限加載,分別通過ACLLoadConfiguredUsers()及ACLLoadFromFile()函式判斷用戶資訊及加載方式,ACLLoadConfiguredUsers()使用redis.conf進行權限加載,ACLLoadFromFile()使用acl檔案進行權限加載,

void ACLLoadUsersAtStartup(void) {
    if (server.acl_filename[0] != '\0' && listLength(UsersToLoad) != 0) {
        serverLog(LL_WARNING,
            "...");
        exit(1);
    }

    if (ACLLoadConfiguredUsers() == C_ERR) {
        serverLog(LL_WARNING,
            "Critical error while loading ACLs. Exiting.");
        exit(1);
    }

    if (server.acl_filename[0] != '\0') {
        sds errors = ACLLoadFromFile(server.acl_filename);
        if (errors) {
            serverLog(LL_WARNING,
                "Aborting Redis startup because of ACL errors: %s", errors);
            sdsfree(errors);
            exit(1);
        }
    }
}

(3)ACL用戶操作

在進行ACL操作命令時,實際呼叫了acl.c的aclCommad()命令,aclCommand中,對各類ACL操作分別進行了定義,如setuser,通過判斷命令中是否含setuser關鍵字,再判斷usename是否含有空格,user是否已存在,最后呼叫ACLSetUser(),ACLCreateUser()實作setuser操作,

void aclCommand(client *c) {
    char *sub = c->argv[1]->ptr;
    if (!strcasecmp(sub,"setuser") && c->argc >= 3) {
        sds username = c->argv[2]->ptr;
        /* Check username validity. */
        if (ACLStringHasSpaces(username,sdslen(username))) {
            addReplyErrorFormat(c,
                "Usernames can't contain spaces or null characters");
            return;
        }
        user *tempu = ACLCreateUnlinkedUser();
        user *u = ACLGetUserByName(username,sdslen(username));
        if (u) ACLCopyUser(tempu, u);

        for (int j = 3; j < c->argc; j++) {
            if (ACLSetUser(tempu,c->argv[j]->ptr,sdslen(c->argv[j]->ptr)) != C_OK) {
                char *errmsg = ACLSetUserStringError();
                addReplyErrorFormat(c,
                    "Error in ACL SETUSER modifier '%s': %s",
                    (char*)c->argv[j]->ptr, errmsg);

                ACLFreeUser(tempu);
                return;
            }
        }

        /* Overwrite the user with the temporary user we modified above. */
        if (!u) u = ACLCreateUser(username,sdslen(username));
        serverAssert(u != NULL);
        ACLCopyUser(u, tempu);
        ACLFreeUser(tempu);
        addReply(c,shared.ok);
    }
...
}

在普通用戶使用權限時,通過ACLCheckCommandPerm()及ACLGetUserCommandBit()函式判斷用戶是否具有操作權限,如無權限則報ACL_DENIED_CMD權限不足,

2.2 新功能——TLS加密管理

2.2.1 TLS簡介

TLS(Transport Layer Security,安全傳輸層)是建立在傳輸層TCP協議之上的協議,服務于應用層,在實作上分為記錄層和握手層兩層,其中握手層又含四個子協議:握手協議、更改加密規范協議、應用資料協議和警告協議,實作了將應用層的報文進行加密后再交由TCP進行傳輸的功能,解決了保密、完整性、認證等網路安全問題,Redis從6.0版本開始支持TLS安全加密,

2.2.2 TLS 配置與示例

Redis 內部使用 OpenSSL 開發庫來實作TLS功能,因此,需要在 Redis 編譯之前預先安裝 OpenSSL 套件庫,此外,TLS是可選功能,需要在編譯時加上引數后才會加入TLS功能,

下面將對TLS編譯使用進行示例,并簡要說明程序中遇到的問題和需關注的地方,

(1)TLS 編譯示例

編譯時,需添加 BUILD_TLS=yes以加入TLS功能,因為依賴主機OpenSSL版本,所以不同OpenSSL版本主機編譯出的redis-server不通用,此外,需要注意gcc版本,下面以CentOS7.6、Ubuntu16.04為例進行說明及錯誤示例,

① 以CentOS7.6為例

執行 “make BUILD_TLS=yes”時出現has no member named報錯:

原因為gcc版本過低,升級gcc版本即可,升級方式:

yum -y install centos-release-scl scl-utils-build 
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils 
scl enable devtoolset-9 bash

升級完成后,執行gcc -v,出現gcc version 9.3.1 20200408 (Red Hat 9.3.1-2) (GCC)即為升級成功,再次編譯執行“make distclean;make BUILD_TLS=yes”,即可編譯成功,

② 以Ubuntu16.04為例

執行“make BUILD_TLS=yes”,若出現“jemalloc/jemalloc.h: No such file or directory”或“Newer version of jemalloc required”報錯:

方案一:README.md檔案的官方解決方案

在README.md檔案中,Redis官方給出了解決方案“To force compiling against libc malloc, use: % make MALLOC=libc”,再次執行“make distclean”,筆者將“make BUILD_TLS=yes”命令,改為“make BUILD_TLS=yes MALLOC=libc”,即可編譯成功,

方案二:關注deps/jemalloc權限是否錯誤

通過wget獲得對應Redis版本后,替換掉deps檔案夾中內容,再次執行“make distclean;make BUILD_TLS=yes”,若通過這種方式可以編譯成功,Redis采用tcmalloc時碎片率會更低最低,

使用redis:6.0.X替換容器版Redis6.0的鏡像時,使用CentOS7.6編譯鏡像無法啟動,使用Ubuntu16.04可成功啟動,經分析,CentOS7.6與Ubuntu16.04的OpenSSL版本分別為OpenSSL 1.0.2k-fips、OpenSSL 1.1.1,因OpenSSL版本不同,所以編譯出的redis-server不通用,驗證時,使用Ubuntu16.04上編譯的redis-server啟動于CentOS7.6時,出現報錯,可證明不通用,

(2)TLS 證書生成

在完成Redis TLS編譯后,根據Redis官方檔案TLS.md中說明,可通過如下命令生成根CA證書和服務器證書:

./utils/gen-test-certs.sh

其中,生成DH(Diffie-Hellman)params檔案耗時較長,

相關證書在配置時限制如下:

證書是否必選
ca.crt必選
redis.crt必選
redis.key必選
redis.dh可選

分析gen-test-certs.sh可知,所有證書均通過openssl生成,在實際使用中,除了使用shell腳本生成證書,還可以通過java、golang等代碼方式呼叫ssl、tls、x509庫生成,其中,證書有效期可以通過days動態生成,到期后可以對證書進行續期,

(3)TLS 引數決議

Redis啟用TLS主要涉及如下引數:

--tls-cert-file /path/to/redis.crt
--tls-key-file /path/to/redis.key
--tls-ca-cert-file /path/to/ca.crt
--tls-dh-params-file /path/to/redis.dh
#以上4個引數為Redis證書及私鑰,包括可信任的根證書和DH PARAMS檔案路徑,本次測驗中為對tls-dh-params-file進行測驗,
--port 0
--tls-port 6379
#以上2個引數為TLS連接埠和非TLS連接埠,埠0代表完全禁用非TLS埠,
--tls-auth-clients yes
#以上為是否啟用雙向TLS并要求客戶端使用有效證書進行身份驗證,默認開啟,
--tls-replication yes
#Redis主服務器以相同的方式處理連接客戶端和從服務器,在從服務器端,必須指定tls-replication yes才能將TLS用于到主服務器的對外連接,sentinel也需要同步設定,
--tls-cluster yes
#Redis集群需使用tls-cluster yes以便為集群和跨節點連接啟用TLS,本次測驗中未涉及此引數,

2.2.3 Redis TLS使用示例

為方便查看,啟動引數直接置于啟動命令列,以單機版redis-server啟動為例:

./src/redis-server --tls-port 6379 --port 0 --tls-cert-file ./tests/tls/redis.crt --tls-key-file ./tests/tls/redis.key --tls-ca-cert-file ./tests/tls/ca.crt

redis server成功啟動,使用redis-cli進行連接,連接時,同時需要加上證書資訊:

./src/redis-cli --tls --cert ./tests/tls/redis.crt --key ./tests/tls/redis.key --cacert ./tests/tls/ca.crt

在進行主備版測驗時,redis-server及sentinel除新增上述引數外,需額外增加“--tls-auth-clients yes”和“--tls-replication yes”方可啟動成功,否則將無法連接,解釋見Redis TLS引數決議,

2.2.4 TLS部分原始碼簡析

(1)TLS初始化

在server.c的main函式中,tlsInit()函式用于初始化SSL庫,initServerConfig()、initConfigValues()用于初始化tls_port和tls_ctx_config等TLS相關引數(相關引數均在redis.conf中獲得),在InitServer()中,獲得的TLS引數tls_ctx_config和tls_port被用于tls.c的tlsConfigure()中用于初始化TLS,

if ((server.tls_port || server.tls_replication || server.tls_cluster)
            && tlsConfigure(&server.tls_ctx_config) == C_ERR) {
    serverLog(LL_WARNING, "Failed to configure TLS. Check logs for more info.");
    exit(1);
}

(2)TLS讀寫

讀寫操作由connTLSRead()和connTLSWrite()組成,connTLSRead()是一個SSL_Read的封裝,將從tls連接中讀取的位元組緩沖到buf中,connTLSWrite()是一個SSL_Write的封裝,將位元組寫入data套接字,其中,tls_connection為建立起的tls連接,

static int connTLSRead(connection *conn_, void *buf, size_t buf_len) {
    tls_connection *conn = (tls_connection *) conn_;
    int ret;
    int ssl_err;

    if (conn->c.state != CONN_STATE_CONNECTED) return -1;
    ERR_clear_error();
    ret = SSL_read(conn->ssl, buf, buf_len);
    ...
    return ret;
}

2.2.5 TLS局限性

Redis特性在于支持高并發且6.0開始支持多執行緒,然而開啟了TLS功能后,出現了明顯的局限性:

① 不再支持多執行緒功能(TLS.md:**Multi-threading I/O is not currently supported for TLS**);

② QPS顯著下降,經測驗只有未開啟TLS的60%(詳見下圖);

③ 6.0.X版本的redis-benchmark不支持TLS功能,需使用6.2.X版本的redis-benchmark才能執行測驗,

使用redis-benchmark在同一臺主機上進行對SET、GET、LRANGE_600測驗,結果如圖所示,

可以看出,開啟TLS后,QPS明顯降低,

2.3新功能——多執行緒IO

2.3.1 多執行緒IO簡介

Redis6.0 之前都是使用單執行緒異步IO處理資料的,單執行緒IO只能使用一個CPU核,當key的value比較大時容易拖垮Redis,而且QPS到達10W+后難以更進一步,從Redis6.0開始支持多執行緒IO,多執行緒IO不僅可以充分利用服務器CPU資源,還可以分攤IO讀寫負荷,大幅提升了Redis性能,

2.3.2 多執行緒IO實作及配置示例

(1)多執行緒IO流程

流程主要如下:

① 主執行緒負責接收建立連接請求,獲取 socket 放入全域等待讀處理佇列;

② 主執行緒處理完讀事件后,通過 RR(Round Robin) 將這些連接分配給這些 IO 執行緒;

③ 主執行緒阻塞等待 IO 執行緒讀取 socket 完畢;

④ 主執行緒通過單執行緒的方式執行請求命令,請求資料讀取并決議完成,但并不執行;

⑤ 主執行緒阻塞等待 IO 執行緒將資料回寫 socket 完畢;

⑥ 解除系結,清空等待佇列,

(2)多執行緒IO引數決議

在redis.conf中,可以對多執行緒進行配置(修改配置需重啟redis實體),多執行緒配涉及兩個引數:

引數說明
io-threads開啟多執行緒的個數(io-thread最大值不能超過服務器CPU可使用總核數)
io-threads-do-reads取yes表示多執行緒開啟

2.3.3多執行緒IO使用及性能對比

添加多執行緒IO引數重啟redis后,通過redis-cli config get *命令可查看當前io-threads及io-threads-do-reads配置現況,

多執行緒IO主要表現在提升了redis的并發能力上,故對不同版本的redis及同版本redis在不同機器上進行了比對測驗,分析多執行緒IO提升能力,

(1)Redis版本間對比測驗

在同一臺主機上,對Redis4.0和Redis6.0進行redis-benchmark性能測驗,測驗時,redis-benchmark使用了--threads引數進行多執行緒測驗,測驗結果詳見下圖:

可以看出,Redis6.0較Redis4.0同在單執行緒時,性能并沒有得到提升;Redis6.0 二執行緒,較redis6.0單執行緒、Redis4.0并發量得到了明顯提升,因此,可以得出結論,多執行緒IO可明顯提高Redis的并發量,

(2)Redis CPU間性能對比測驗

多執行緒IO可明顯提升Redis并發量,在不同型別的CPU主機上測驗,也有不同的提升,具體如下:

① 多執行緒IO 并發上限不到單執行緒IO的三倍,io-threads到一定數之后(測驗時值為6-10,因服務器CPU差別),并發增幅極為有限,在這種情況下,即使增加執行緒數,也不能明顯增加Redis 并發量,只會導致服務器負載增加,

② CPU頻率越高,能達到的最大并發相對也越高,故選用CPU時,選擇較高頻率CPU可以提升Redis性能,使用時,打開高性能模式也可以提升Redis性能,

Redis6.0 SET Intel Xeon測驗結果:

2.3.4 多執行緒IO部分原始碼簡析

多執行緒IO中,組態檔redis.conf主要涉及的引數是io_threads_do_reads、io-threads,原始碼中,除了這兩個關鍵變數外,還會涉及io_threads_op、io_threads_pending等變數,

(1)多執行緒IO初始化

在server.c的main函式InitServerLast()中,有初始化多執行緒函式initThreadedIO(),函式中呼叫了pthread_create 來創建執行緒,并且注冊了執行緒回呼函式IOThreadMain(),

void initThreadedIO(void) {
    server.io_threads_active = 0; 
    if (server.io_threads_num == 1) return;
    if (server.io_threads_num > IO_THREADS_MAX_NUM) {
        serverLog(LL_WARNING,"Fatal: too many I/O threads configured. "
        "The maximum number is %d.", IO_THREADS_MAX_NUM);
        exit(1);
    }
    /* Spawn and initialize the I/O threads. */
    for (int i = 0; i < server.io_threads_num; i++) {
        /* Things we do for all the threads including the main thread. */
        io_threads_list[i] = listCreate();
        if (i == 0) continue; 
        pthread_t tid;
        pthread_mutex_init(&io_threads_mutex[i],NULL);
        io_threads_pending[i] = 0;
        pthread_mutex_lock(&io_threads_mutex[i]); /* Thread will be stopped. */
        if (pthread_create(&tid,NULL,IOThreadMain,(void*)(long)i) != 0) {
            serverLog(LL_WARNING,"Fatal: Can't initialize IO thread.");
            exit(1);
        }
        io_threads[i] = tid;
    }
}

(2)多執行緒IO任務處理

以多執行緒讀為例,使用了handleClientsWithPendingReadsUsingThreads 函式進行多執行緒讀,步驟如下:

① 通過 io_threads_active 和io_threads_do_reads 兩個標志判斷是否開啟了多執行緒IO,其中 io_threads_do_reads為redis.conf配置引數,io_threads_active通過呼叫startThreadedIO()進行置1操作;

② 主執行緒給作業執行緒分配client物件策略即輪詢策略,io_threads_op指定讀型別,通過給io_threads_pending[id]賦值啟動作業執行緒,作業執行緒在處理完自己鏈表的 client 物件后會清空自己的鏈表并重置 io_threads_pending[id] 為0;

③ 主執行緒的利用while將自己鏈表中的 client 處理完畢;

④ 最后,執行緒的pending和為0,跳出回圈完成讀事件的處理,

int handleClientsWithPendingReadsUsingThreads(void) {
    if (!server.io_threads_active || !server.io_threads_do_reads) return 0;
    int processed = listLength(server.clients_pending_read);
    if (processed == 0) return 0;

    if (tio_debug) printf("%d TOTAL READ pending clients\n", processed);

    /* Distribute the clients across N different lists. */
    listIter li;
    listNode *ln;
    listRewind(server.clients_pending_read,&li);
    int item_id = 0;
    while((ln = listNext(&li))) {
        client *c = listNodeValue(ln);
        int target_id = item_id % server.io_threads_num;
        listAddNodeTail(io_threads_list[target_id],c);
        item_id++;
    }

    /* Give the start condition to the waiting threads, by setting the
     * start condition atomic var. */
    io_threads_op = IO_THREADS_OP_READ;
    for (int j = 1; j < server.io_threads_num; j++) {
        int count = listLength(io_threads_list[j]);
        io_threads_pending[j] = count;
    }

    /* Also use the main thread to process a slice of clients. */
    listRewind(io_threads_list[0],&li);
    while((ln = listNext(&li))) {
        client *c = listNodeValue(ln);
        readQueryFromClient(c->conn);
    }
    listEmpty(io_threads_list[0]);

    /* Wait for all the other threads to end their work. */
    while(1) {
        unsigned long pending = 0;
        for (int j = 1; j < server.io_threads_num; j++)
            pending += io_threads_pending[j];
        if (pending == 0) break;
    }
    if (tio_debug) printf("I/O READ All threads finshed\n");
    ...
    /* Update processed count on server */
    server.stat_io_reads_processed += processed;

    return processed;
}

3. 小結

本次主要對Redis6.0的三個新功能ACL權限控制、TLS加密管理及多執行緒IO進行了功能性能測驗及分析,測驗時也兼帶對redis-cli、redis-benchmark新功能進行了一定的測驗驗證:

  • ACL權限控制實作了Redis的賬號管理及權限分配,對Redis資料安全防止誤刪起到了一定的作用,
  • TLS加密管理可以明顯增加Redis的訪問安全性,但犧牲了一定的性能,Redis作為一款高并發kv資料庫,使用TLS時需考慮安全與性能間的取舍,
  • 多執行緒IO可明顯增加Redis并發量,在對Redis并發有較高需求時,可使用高頻多核CPU以提升Redis性能,
  • redis-benchmark在6.0版本支持了多執行緒,測驗時可以多個執行緒同時進行,無需通過多臺主機同時壓測,也是一個較大的優化點,方便了壓測進行,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/303866.html

標籤:java

上一篇:計算機畢業設計之微信小程式的商城 購物系統 app論文

下一篇:《on Java 中文版》讀后感(《JAVA編程思想》的原作者)(JAVA 小虛竹)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more