Redis之初探

*一炁化三清*發表於2024-05-29

Redis是什麼

  • Redis是一個開源,記憶體儲存的資料結構伺服器,可用作資料庫,快取記憶體和訊息佇列代理。
  • Key-Value格式的快取和儲存伺服器;
  • 資料結構伺服器,它支援字串、雜湊表、列表、集合、有序集合,點陣圖,hyperloglogs等資料型別;
  • 所有資料集必須在記憶體中使用,磁碟是用來持久資料的。
  • 是一種NoSQL資料庫

Redis和Memcached的比較

  • 效能和用途:Memcached優先考慮高效能和特殊響應時間,適用於簡單快取任務;Redis提供相當的效能,支援更高階的使用案例,包括非同步和非阻塞I/O操作。
  • 資料結構和功能:Redis支援更豐富的資料操作,而Memcached只是單一key-value記憶體Cache。
  • 可靠性和持久化:Redis對可靠性要求較高,支援資料持久化,而Memcached只是記憶體快取,對可靠性無要求。
  • 叢集模式:Redis原生支援叢集模式,而Memcached需要依靠客戶端來實現叢集。
  • 效能對比:Redis在儲存小資料時比Memcached效能更高,但在大資料中稍有遜色。

Redis資料持久化

Redis的資料全部在記憶體中,如果突然當機,資料就會全部丟失,因此必須有一種機制來保證redis的資料在發生突發狀況時不會丟失、或者只丟失少量,於是必須根據一些策略來把redis記憶體中的資料寫到磁碟中,這樣當redis服務重啟時,就會將硬碟中的資料恢復到記憶體中。Redis持久化的意義就是為了保證突然當機,記憶體資料不會全部丟失。

redis有兩種持久化機制
RDB和AOF。Redis4.0後支援RDB和AOF兩種持久化機制混合使用,所以存在三種持久化策略。

  • RDB是基於快照一次的全量備份,即週期性的把redis當前記憶體中的全量資料寫入到一個快照檔案中(週期時間可以透過配置來調整)。
  • AOF(Append-only file)日誌儲存的是redis伺服器的順序指令序列,即對記憶體中資料進行修改的指令記錄。當redis收到客戶端修改指令後,先進行引數校驗,如果校驗透過,先把該指令儲存到AOF日誌檔案中,也就是先存到磁碟,然後再執行該修改指令。

Redis是單執行緒還是多執行緒?

  • Redis5及之前是單執行緒版本

Redis5及之前的版本使用的是單執行緒,也就是說只有一個 worker佇列,所有的讀寫操作都要在這一個佇列進行操作,好處是不會有執行緒安全問題(因為它在讀寫時就只有一個執行緒,那對於讀寫操作肯定沒有執行緒安全問題啊!),但是讀寫 write、read 這些系統呼叫在Redis執行期間佔用了大部分的 CPU 時間,所以這就是單執行緒模式的缺點。
所以也就在Redis6引入了多執行緒版本,接著往下看。

  • Redis6開始引入多執行緒版本(實際上是 單執行緒+多執行緒 版本)

Redis6引入了多執行緒機制,但是不是說有多個worker執行緒同時併發讀寫, 而是它有 “一個 worker執行緒+多個IO子執行緒”,其實就是在 IO 就緒之後使用多執行緒提升讀寫解析資料的效率,而在 操作記憶體資料的時候還是用單執行緒。
利用這種單執行緒+多執行緒共同運作的機制,將CPU的效能顯著提升了。
同時,這種機制同樣不會產生執行緒安全問題,因為Redis在針對資料的記憶體操作時,是在一個公共的worker佇列中實現的,先進先出,所以不會有執行緒安全問題。
Redis6之所以保留worker單主執行緒是因為單執行緒機制使得Redis內部實現的複雜度大大降低,而且可以保證操作的執行緒安全。(如果整個過程全讓子執行緒做了,整個任務處理過程太重,就失去了原來單執行緒高效處理的優勢了)
簡單來說,就是 “請求是多執行緒的,但核心的記憶體讀寫操作(或者說讀寫計算)仍然是單執行緒的”。

Redis 為什麼單執行緒還這麼快?

誤區1:高效能的伺服器一定是多執行緒的!
誤區2:多執行緒(CPU上下文會切換!)一定比單執行緒效率高!

  • redis是將所有的資料全部放在記憶體中的,所以說使用單執行緒去操作效率就是最高的,多執行緒(CPU上下文會切換,耗時的操作!!!)。
  • 對於記憶體系統來說,如果沒有上下文切換效率就是最高的!多次讀寫都是在一個CPU上的,在記憶體情況下,這個就是最佳的方案!
  • 一條執行緒指的是程序中一個單一順序的控制流,一個程序中可以併發多個執行緒,每條執行緒並行執行不同的任務。多執行緒會進行CPU上下文會切換,造成時間消耗。

安裝部署

https://redis.io/
https://www.redis.net.cn/

因為Redis是C實現的,因此需要安裝如下依賴,其次,有可能需要安裝python3。

# yum install cpp
# yum install binutils
# yum install glibc
# yum install glibc-kernheaders
# yum install glibc-common
# yum install glibc-devel
# yum install gcc
# yum install make

# tar -zxvf  redis-7.0.15.tar.gz
# cd redis-7.0.15
# make && make install

Hint: It's a good idea to run 'make test' ;)

make[1]: 離開目錄“/data/apps/redis-7.0.15/src”
cd src && make install
make[1]: 進入目錄“/data/apps/redis-7.0.15/src”
    CC Makefile.dep
make[1]: 離開目錄“/data/apps/redis-7.0.15/src”
make[1]: 進入目錄“/data/apps/redis-7.0.15/src”

Hint: It's a good idea to run 'make test' ;)

    INSTALL redis-server
    INSTALL redis-benchmark
    INSTALL redis-cli
make[1]: 離開目錄“/data/apps/redis-7.0.15/src”
  • redis的預設安裝路徑:/usr/local/bin
[root@jgswy-pro bin]# ll
總用量 21556
-rwxr-xr-x. 1 root root  5205488 5月  28 21:03 redis-benchmark
lrwxrwxrwx. 1 root root       12 5月  28 21:03 redis-check-aof -> redis-server
lrwxrwxrwx. 1 root root       12 5月  28 21:03 redis-check-rdb -> redis-server
-rwxr-xr-x. 1 root root  5422896 5月  28 21:03 redis-cli
lrwxrwxrwx. 1 root root       12 5月  28 21:03 redis-sentinel -> redis-server
-rwxr-xr-x. 1 root root 11441032 5月  28 21:03 redis-server
  • redis預設不是後臺啟動,需要修改配置檔案可以後臺啟動。
# cp /data/apps/redis-7.0.15/redis.conf  /data/apps/redis-7.0.15/redis.conf.bak
# vi /data/apps/redis-7.0.15/redis.conf
daemonize yes  #守護程序,修改為yes後即可後臺執行
requirepass 123321  #密碼,設定後訪問Redis必須輸人密碼

# redis-server /data/apps/redis-7.0.15/redis.conf

# ss -tul
Netid State      Recv-Q Send-Q                     Local Address:Port                                      Peer Address:Port
udp   UNCONN     0      0                              127.0.0.1:323                                                  *:*
udp   UNCONN     0      0                                  [::1]:323                                               [::]:*
tcp   LISTEN     0      128                                    *:ssh                                                  *:*
tcp   LISTEN     0      100                            127.0.0.1:smtp                                                 *:*
tcp   LISTEN     0      128                            127.0.0.1:6379                                                 *:*
tcp   LISTEN     0      128                                 [::]:ssh                                               [::]:*
tcp   LISTEN     0      128                                 [::]:hbci                                              [::]:*
tcp   LISTEN     0      100                                [::1]:smtp                                              [::]:*
tcp   LISTEN     0      128                                 [::]:9115                                              [::]:*
tcp   LISTEN     0      128                                 [::]:websm                                             [::]:*
tcp   LISTEN     0      128                                [::1]:6379                                              [::]:*
tcp   LISTEN     0      128                                 [::]:jetdirect                                         [::]:*
[root@jgswy-pro ~]#

  • 其它常用配置
#監聽的埠
port 6379
#工作目錄,預設是當前目錄,也就是執行redis-server時的命今,日誌、持久化等檔案會儲存在這個目錄
dir .
#資料庫數量,設定為1,代表只使用1個庫,預設有16個庫,編號0~15
databases 1
#設定redis能夠使用的最大記憶體
maxmemory 512mb
#日誌檔案,預設為空,不記錄日誌,可以指定日誌檔名
logfile "redis.log"
  • 測試登入庫
# redis-cli -p 6379
127.0.0.1:6379> set name test
OK
127.0.0.1:6379> get name
"test"
127.0.0.1:6379>
  • redis預設有16個庫(0-15),預設使用的是0,可以用select進行切換。每個資料庫互相隔離。
# redis-cli -p 6379
127.0.0.1:6379> SELECT 6       #切換到資料庫6
OK
  • 常用命令
127.0.0.1:6379[6]> DBSIZE      #檢視資料庫大小
(integer) 0

127.0.0.1:6379[6]> KEYS *      #檢視資料庫所有的key
(empty array)

127.0.0.1:6379[6]> flushdb     #清空當前庫的內容
OK
127.0.0.1:6379[6]> select 0
OK

127.0.0.1:6379> KEYS *
1) "myhash"
2) "mylist"
3) "name"
4) "key:__rand_int__"
5) "counter:__rand_int__"
127.0.0.1:6379> select 2
OK
127.0.0.1:6379[2]> flushall    #清空所有庫的內容
OK
127.0.0.1:6379[2]> select 0
OK
127.0.0.1:6379> keys *
(empty array)

# redis-cli -p 6379
127.0.0.1:6379> SET name xiaoming
OK
127.0.0.1:6379> KEYS *
1) "name"
127.0.0.1:6379> SET age 1                  #插入資料
OK
127.0.0.1:6379> KEYS *
1) "name"
2) "age"
127.0.0.1:6379> EXISTS name                #判斷name是否存在,存在返回1,不存在返回0
(integer) 1
127.0.0.1:6379> EXISTS name1
(integer) 0
127.0.0.1:6379> move name 1                #將name從當前庫移動到庫1
(integer) 1

127.0.0.1:6379> set name xioaming
OK
127.0.0.1:6379> get name
"xioaming"
127.0.0.1:6379> expire name 10             #設定key的過期時間10秒,倒數計時10秒後資料過期
(integer) 1
127.0.0.1:6379> ttl name                   #檢視當前key的時間
(integer) 2
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> get name
(nil)

127.0.0.1:6379> set name xiaoming
OK
127.0.0.1:6379> type name                  #檢視當前key的資料型別
string

併發測試

redis提供了一款測試工具redis-benchmark

redis-benchmark 引數
-h:指定主機名
-p:指定埠,預設6379
-c:指定併發連線數
-n:指定請求數
-d:以位元組形式指定SET/GET值的資料大小

測試100個併發連線,每個併發100000個請求

# redis-benchmark -h localhost -p 6379 -c 100 -n 100000
====== PING_INLINE ======
  100000 requests completed in 1.36 seconds                          #100000個請求
  100 parallel clients                                               #100個並行客戶端
  3 bytes payload                                                    #每次寫入3個位元組
  keep alive: 1                                                      #一個伺服器處理請求
  host configuration "save": 3600 1 300 100 60 10000
  host configuration "appendonly": no
  multi-thread: no

Latency by percentile distribution:
0.000% <= 0.247 milliseconds (cumulative count 2)
50.000% <= 0.663 milliseconds (cumulative count 53433)
75.000% <= 0.687 milliseconds (cumulative count 78832)
87.500% <= 0.711 milliseconds (cumulative count 88427)
93.750% <= 1.015 milliseconds (cumulative count 93767)
96.875% <= 1.423 milliseconds (cumulative count 96926)
98.438% <= 1.623 milliseconds (cumulative count 98501)
99.219% <= 1.719 milliseconds (cumulative count 99234)
99.609% <= 1.783 milliseconds (cumulative count 99621)
99.805% <= 2.095 milliseconds (cumulative count 99805)
99.902% <= 2.599 milliseconds (cumulative count 99903)
99.951% <= 2.951 milliseconds (cumulative count 99952)
99.976% <= 3.135 milliseconds (cumulative count 99976)
99.988% <= 3.303 milliseconds (cumulative count 99988)
99.994% <= 3.391 milliseconds (cumulative count 99994)
99.997% <= 3.439 milliseconds (cumulative count 99997)
99.998% <= 3.479 milliseconds (cumulative count 99999)
99.999% <= 3.495 milliseconds (cumulative count 100000)
100.000% <= 3.495 milliseconds (cumulative count 100000)            #所有請求在3.495毫秒完成

Summary:
  throughput summary: 73367.57 requests per second                  #每秒處理73367.57個請求
  latency summary (msec):
          avg       min       p50       p95       p99       max
        0.716     0.240     0.663     1.175     1.695     3.495

...

====== SET ======
  100000 requests completed in 1.33 seconds
  100 parallel clients
  3 bytes payload
  keep alive: 1
  host configuration "save": 3600 1 300 100 60 10000
  host configuration "appendonly": no
  multi-thread: no

Latency by percentile distribution:
0.000% <= 0.303 milliseconds (cumulative count 3)
50.000% <= 0.671 milliseconds (cumulative count 57052)
75.000% <= 0.687 milliseconds (cumulative count 77451)
87.500% <= 0.703 milliseconds (cumulative count 92613)
93.750% <= 0.711 milliseconds (cumulative count 95469)
96.875% <= 0.719 milliseconds (cumulative count 97002)
98.438% <= 0.735 milliseconds (cumulative count 98636)
99.219% <= 0.767 milliseconds (cumulative count 99254)
99.609% <= 0.855 milliseconds (cumulative count 99635)
99.805% <= 1.023 milliseconds (cumulative count 99808)
99.902% <= 1.871 milliseconds (cumulative count 99903)
99.951% <= 2.175 milliseconds (cumulative count 99952)
99.976% <= 2.351 milliseconds (cumulative count 99976)
99.988% <= 2.479 milliseconds (cumulative count 99988)
99.994% <= 2.551 milliseconds (cumulative count 99994)
99.997% <= 2.583 milliseconds (cumulative count 99997)
99.998% <= 2.607 milliseconds (cumulative count 99999)
99.999% <= 2.623 milliseconds (cumulative count 100000)
100.000% <= 2.623 milliseconds (cumulative count 100000)

Cumulative distribution of latencies:
0.000% <= 0.103 milliseconds (cumulative count 0)
0.003% <= 0.303 milliseconds (cumulative count 3)
0.030% <= 0.407 milliseconds (cumulative count 30)
0.067% <= 0.503 milliseconds (cumulative count 67)
0.257% <= 0.607 milliseconds (cumulative count 257)
92.613% <= 0.703 milliseconds (cumulative count 92613)
99.412% <= 0.807 milliseconds (cumulative count 99412)
99.722% <= 0.903 milliseconds (cumulative count 99722)
99.794% <= 1.007 milliseconds (cumulative count 99794)
99.833% <= 1.103 milliseconds (cumulative count 99833)
99.850% <= 1.207 milliseconds (cumulative count 99850)
99.860% <= 1.303 milliseconds (cumulative count 99860)
99.866% <= 1.407 milliseconds (cumulative count 99866)
99.870% <= 1.503 milliseconds (cumulative count 99870)
99.874% <= 1.607 milliseconds (cumulative count 99874)
99.880% <= 1.703 milliseconds (cumulative count 99880)
99.893% <= 1.807 milliseconds (cumulative count 99893)
99.910% <= 1.903 milliseconds (cumulative count 99910)
99.928% <= 2.007 milliseconds (cumulative count 99928)
99.941% <= 2.103 milliseconds (cumulative count 99941)
100.000% <= 3.103 milliseconds (cumulative count 100000)

Summary:
  throughput summary: 75075.07 requests per second                  #每秒處理75075個請求
  latency summary (msec):
          avg       min       p50       p95       p99       max
        0.673     0.296     0.671     0.711     0.751     2.623

...

參考

https://blog.csdn.net/Awwwze/article/details/115396745
https://blog.csdn.net/Wong_H/article/details/126196774
https://blog.csdn.net/Awwwze/article/details/115396745

相關文章