redis入門(一)

  • 2019 年 11 月 1 日
  • 筆記

redis入門(一)

前言

  1. Redis是什麼?
    redis是一種基於鍵值對(key-value)的NoSQL資料庫。Redis會將所有數據都存放在記憶體中,所以它的讀寫性能非常驚人。不僅如此,Redis還可以將記憶體的數據利用快照和日誌的形式保存到硬碟上,這樣在發生類似斷電或者機器故障的時候,記憶體中的數據不會「丟失」。Redis還提供了鍵過期、發布訂閱、事務、流水線、Lua腳本等附加功能。

  2. Redis能做什麼
    在談為什麼需要redis之前,先要清楚redis可以做什麼。
  • 快取。通過引入快取加快數據的訪問速度,降低後端數據源的壓力。
  • 排行榜。redis提供給列表和有序幾何數據結構可以很方便的構建各種排行榜系統。
  • 計數器。redis原生支援高性能的計數功能,可以為影片播放量、網頁瀏覽數等提供支援。
  • 消息隊列。redis提供發布訂閱功能。

特性

我們為什麼選擇redis?

速度快

官方給出的讀寫速度可以達到10W/s,以下是我本機雙核四執行緒低壓i7上測試的對字元串的讀寫速度。

C:UsersDm_ca> redis-benchmark -n 100000  -t set,get -q -a test1  SET: 11993.28 requests per second  GET: 57603.69 requests per second  

若使用redis管道技術可以得到更高的讀寫速度

C:UsersDm_ca> redis-benchmark -n 100000 -t set,get -q -a test1 -P 2  SET: 19466.62 requests per second  GET: 133155.80 requests per second

Redis 管道技術可以在服務端未響應時,客戶端可以繼續向服務端發送請求,並最終一次性讀取所有服務端的響應。

下表是Google公司給出的各層級硬體執行速度,記憶體的響應速度是100ns,redis將數據全部從記憶體載入可以更快的讀寫數據。
20191024172138.png

簡單穩定

早期版本程式碼在2W行左右,3.0添加了集群等特性增值5W行。相比其他NoSQL資料庫來說程式碼量少得多。

豐富的功能

支援發布訂閱、持久化、集群及管道等其他常用的功能。

歷史

2008年,Redis的作者Salvatore Sanfilippo在開發一個叫LLOOGG的網站時,需要實現一個高性能的隊列功能,最開始是使用MySQL來實現的,但後來發現無論怎麼優化SQL語句都不能使網站的性能提高上去,於是他決定自己做一個專屬於LLOOGG的資料庫,這個就是Redis的前身。後來,Salvatore Sanfilippo將Redis1.0的源碼開放到GitHub上,可能連他自己都沒想到,Redis後來如此受歡迎。

歷史版本

Redis借鑒了Linux作業系統對於版本號的命名規則:版本號第二位如果是奇數,則為非穩定版本(例如2.7、2.9、3.1),如果是偶數,則為穩定版本(例如2.6、2.8、3.0、3.2)。當前奇數版本就是下一個穩定版本的開發版本。

  1. Redis 2.6
    Redis 2.6在2012年正式發布,經歷了17個版本,到 2.6.17版本,相比於Redis2.4,主要特性如下:
    • 服務端支援Lua腳本。
    • 從節點提供只讀功能。
    • 重構了大量的核心程式碼,所有集群相關的程式碼都去掉了,cluster功能將會是3.0版本最大的亮點。
    • 其他若干修復與優化
  2. Redis 2.8
    Redis2.8在2013年11月22日正式發布,經歷了24個版本,到2.8.24版本,相比於Redis2.6,主要特性如下:
    • 添加部分主從複製的功能,在一定程度上降低了由於網路問題,造成頻繁全量複製生成RDB對系統造成的壓力。
    • 可以通過config set命令設置maxclients。
    • 可以用bind命令綁定多個IP地址。
    • configre write命令可以將config set持久化到Redis配置文件中。
    • 其他若干修復與優化
  3. Redis 3.0
    • Redis Cluster:Redis的官方分散式實現。
    • 全新的embedded string對象編碼結果,優化小對象記憶體訪問,在特定的工作負載下速度大幅提升。
    • config set設置maxmemory時候可以設置不同的單位單位(之前只能是位元組)
    • incr命令性能提升。
    • 其他若干修復與優化
  4. Redis 3.2
    • 新的List編碼類型:quicklist。
    • 從節點讀取過期數據保證一致性。
    • 新的RDB格式,但是仍然兼容舊的RDB。
    • 加速RDB的載入速度。
    • 其他若干修復與優化
  5. Redis 4.0
    • 提供了模組系統,方便第三方開發者拓展Redis的功能
    • PSYNC2.0:優化了之前版本中,主從節點切換必然引起全量複製的問題。
    • 提供了RDB-AOF混合持久化格式,充分利用了AOF和RDB各自優勢。
    • Redis Cluster兼容NAT和Docker。
    • 其他若干修復與優化

更多細節可以查看Redis版本歷史介紹

安裝與啟動

redis編譯後,有許多可執行文件,我們先了解一下各個文件的用途。在windows版本和官方redis版的文件都是差不多的。

可執行文件 作用
redis-server 啟動redis服務
redis-cli redis命令行客戶端
redis-benchmark redis基準測試工具
redis-check-aof redis AOF持久化文件檢測和修復工具
redis-check-dump redis RDB持久化文件檢測和修復工具
redis-sentinel 啟動redis哨兵服務

windows版本是沒有redis-sentinel,在啟動的時候可以通過--sentinel參數以哨兵模式啟動。

安裝

windows版本

  1. 下載

    Redis官方並不支援Windows作業系統,但是Redis作為一款優秀的開源技術吸引到了微軟的注意,微軟的開源技術組在GitHub上維護一個Redis的分支
    目前在windows版本最新的redis是3.2.100,可以到這裡下載

    下載的壓縮文件內容如下圖所示。
    1.png

    windows版本的redis可以以2種方式運行,一種是通過cmd命令框啟動redis服務進程。另一種是將redis安裝為windows服務,並以windows服務運行。

    .config是redis配置文件。在服務安裝的時候我們可以指定配置文件,若沒有指定,則使用redis默認配置。

  2. 直接啟動

    通過redis-server 配置名可以直接啟動redis服務。
    20191029092007.png

  3. 安裝服務

    生產環境建議將redis安裝為windows服務,避免cmd框不小心被關掉。
    打開cmd窗口,通過redis-server --service-install 配置文件路徑 --service-name 服務名安裝服務。如redis-server --service-install redis.windows-service.conf --service-name redis-test
    若配置格式沒有問題,安裝成功後再windows服務中則會有名為redis-test的服務。

    安裝windows服務時必須指定配置文件
    若沒有指定服務名,則使用Redis作為默認的服務名稱。

  4. 啟動服務

    通過redis-server --service-start --service-name 服務名啟動指定的redis服務。

  5. 停止服務

    通過redis-server --service-stop --service-name 服務名停止指定的redis服務。

  6. 卸載

    通過redis-server --service-install --service-name 服務名卸載Redis服務。卸載服務前需要先停止服務。

linux版本

  1. 下載

    在Linux安裝軟體通常有兩種方法,第一種是通過各個作業系統的軟體管理軟體進行安裝,例如CentOS有yum管理工具,Ubuntu有 apt。但是由於Redis的更新速度相對較快,而這些管理工具不一定能更新到最新的版本,同時Redis的安裝本身不是很複雜,所以一推薦使用第二種方式:源碼的方式進行安裝,整個安裝只需以下四步即可完成。
    1) 下載Redis指定版本的源碼壓縮包到當前目錄。
    2) 解壓縮Redis源碼壓縮包。
    3) 編譯(編譯之前確保作業系統已經安裝gcc)。
    4) 安裝。

    wget http://download.redis.io/releases/redis-5.0.5.tar.gz  tar xzf redis-5.0.5.tar.gz  cd redis-5.0.5  make
  2. 編譯

    我本機是在windows的Linux子系統上運行,安裝的是Ubuntu,windows商店中的ubuntu是最小化安裝,因此許多必要的開發包都是沒有的,比如make,因此需要安裝make,而編譯redis源碼還要依賴gcc,因此確保自己本地的linux已經安裝了gccmake.

    在ubuntu下可以使用sudo apt-get install build-essential安裝gcc相關的包,使用sudo apt-get install make安裝make包。
    若都安裝完成,則可以在redis目錄下通過make命令進行源碼編譯

  3. 啟動

    通過src/redis-server啟動redis-server,通過src/redis-server 配置名以指定的配置文件啟動。若直接啟動默認以前台服務進程執行,將會阻塞命令行。修改配置文件daemonize no改為daemonize yes以守護進程的方式執行。

    守護進程(Daemon Process),也就是通常說的 Daemon 進程(精靈進程),是 Linux 中的後台服務進程。它是一個生存期較長的進程,通常獨立於控制終端並且周期性地執行某種任務或等待處理某些發生的事件。
    可以通過修改配置文件中的port修改綁定指定埠

  4. 客戶端連接

    通過src/redis-cli連接redis服務,通過src/redis-server -vsrc/redis-cli -v可以查看redis的版本號。

數據類型與內部編碼

數據結構

redis可以保存string(字元串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)。在後面的版本逐步又添加了Bitmaps(點陣圖)、HyperLogLog、GEO(地理資訊定位)等數據結構。

通過type命令可以查看當前鍵的數據類型。

127.0.0.1:26379> type key1  string

20191029103621.png

內部編碼

通過type返回的僅僅是對外的數據結構,實際上每種數據結構都有自己底層的內部編碼實現,而且是多種實現。

redis通過構建簡單動態字元串、鏈表、壓縮列表、整數集合、哈希表(字典)、跳躍表等內部編碼構造出進行組合實現了各種數據結構,通過這種方式 ,Redis會在合適的場景選擇合適的內部編碼。從而優化不同場景下的使用效率。

20191029103659.png

  • 如果一個字元串對象保存的是整數值,並且這個整數值可以用long類型來表示,需要注意的是long或double類型表示的浮點數在Redis中也是作為字元串值來保存的。如果我們要保存一個浮點數到字元串對象裡面,那麼程式會先將這個浮點數轉換成字元串值,然後再保存轉換所得的字元串值。

  • embstr編碼是專門用於保存短字元串的一種優化編碼方式,embstr編碼通過調用一次記憶體分配函數來分配一塊連續的空間,而raw編碼會調用兩次記憶體分配函數。同理釋放embstr編碼的字元串只需要調用一次記憶體釋放函數來分配一塊連續的空間,而釋放raw編碼的字元串會調用兩次記憶體釋放函數。

    embstr編碼的字元串對象實際上是只讀的。當我們對embstr編碼的字元串對象執行任何修改命令時,程式會先將對象的編碼從embstr轉換成raw,然後再執行修改命令。因為這個原因,embstr編碼的字元串對象在執行修改命令之後,總會變成一個raw編碼的字元串對象。

  • Redis的字典使用哈希表作為底層實現,一個哈希表裡面可以有多個哈希表節點,而每個哈希表節點就保存了字典中的一個鍵值對。
  • 壓縮列表是Redis為了節約記憶體而開發的,是由一系列特殊編碼的連續記憶體塊組成的順序型(sequential)數據結構。一個壓縮列表可以包含任意多個節點(entry),每個節點可以保存一個位元組數組或者一個整數值。
  • 鏈表提供了高效的節點重排能力,以及順序性的節點訪問方式,並且可以通過增刪節點來靈活地調整鏈表的長度。
  • 整數集合(intset)是Redis用於保存整數值的集合抽象數據結構,它可以保存類型為int16_t、int32_t或者int64_t的整數值,並且保證集合中不會出現重複元素。
  • 跳躍表(skiplist)是一種有序數據結構,它通過在每個節點中維持多個指向其他節點的指針,從而達到快速訪問節點的目的。

常用API與使用場景

常用命令

  1. 查看所有鍵:keys *

    127.0.0.1:26379> set key1 1  OK  127.0.0.1:26379> set key2 2  OK  127.0.0.1:26379> set key3 3  OK  127.0.0.1:26379> set key22 22  OK  127.0.0.1:26379> keys *  1) "key22"  2) "key3"  2) "key2"  3) "key1"

    該命令還支援模糊查詢
    shell 127.0.0.1:26379> keys *2* 1) "key22" 2) "key2"

  2. 鍵總數:dbsize

    127.0.0.1:26379> dbsize  (integer) 3
  3. 檢查鍵是否存在:exists key,可以傳入多個key,返回存在key的個數

    127.0.0.1:26379> exists key1  (integer) 1  127.0.0.1:26379> exists key4  (integer) 0  127.0.0.1:26379> exists key1 key2  (integer) 2
  4. 刪除鍵:del key,可以同時刪除多個key,返回成功刪除key的數量

    127.0.0.1:26379> del key1 key2  (integer) 2
  5. 鍵過期:expire key seconds

    127.0.0.1:26379> expire key3 2  (integer) 1
  6. 查詢鍵剩餘過期時間:ttl key。大於等於0則是鍵剩餘的過期時間,-1表示未設置過期時間。

    127.0.0.1:26379> ttl key3  (integer) 2  127.0.0.1:26379> ttl key1  (integer) -1
  7. 查詢redis服務狀態:info [section]。可以通過info查詢redis所有資訊,或者通過info section查詢指定的部分資訊。

    127.0.0.1:26379> info  # Server  redis_version:5.0.6  redis_git_sha1:00000000  redis_git_dirty:0  redis_build_id:6f31570182dc95d9  redis_mode:standalone  ...  # Keyspace  db0:keys=3,expires=0,avg_ttl=0    127.0.0.1:26379> info keyspace  # Keyspace  db0:keys=3,expires=0,avg_ttl=

字元串

字元串類型是Redis最基礎的數據結構。首先鍵都是字元串類型,而且其他幾種數據結構都是在字元串類型基礎上構建的,所以字元串類型能為其他四種數據結構的學習奠定基礎。字元串類型的值實際可以是字元串(簡單的字元串、複雜的字元串(例如JSON、XML))、數字(整數、浮點數),甚至是二進位(圖片、音頻、影片),但是值最大不能超過512MB。

常用API

  1. 設置鍵set key value [ex seconds] [px milliseconds] [nx| xx]
    • ex seconds:為鍵設置秒級過期時間。
    • px milliseconds:為鍵設置毫秒級過期時間。
    • nx:鍵必須不存在,才可以設置成功,用於添加。
    • xx:與nx相反,鍵必須存在,才可以設置成功,用於更新。

    由於nx和xx的特性,只有一個客戶端能設置成功,因此可以做為分散式鎖的一種實現。

  2. 讀取鍵:get key
  3. 批量設置:mset key value [key value ...]
  4. 批量讀取:mget key [key ...]

內部編碼

字元串類型的內部編碼有3種:

使用場景

  1. 快取

    20191031120713.png

    與關係型資料庫不同的是,Redis沒有命令空間,而且也沒有對鍵名有強制要求(除了不能使用一些特殊字元)。但設計合理的鍵名,有利於防止鍵衝突和項目的可維護性,比較推薦的方式是使用業務名:對象名:id:[屬性]作為鍵名(也可以不是分號)。例如資料庫名為vs,用戶表名為user,那麼對應的鍵可以用vs:user:1vs:user:1:name來表示,如果當前Redis只被一個業務使用,甚至可以去掉vs:。如果鍵名比較長,例如user:{uid}:friends:messages:{mid},可以在能描述鍵含義的前提下適當減少鍵的長度,例如變為u:{uid}:fr:m:{mid},從而減少由於鍵過長的記憶體浪費。

  2. 計數

    通過incr key對計數進行累加,可用於播放量,訪問量等場景。

  3. 限速

    通過set 和 incr組合使用實現1分鐘內最多發送5條簡訊。

    127.0.0.1:26379> set 182XXXXXXXX 1 ex 60 nx  OK  127.0.0.1:26379> set 182XXXXXXXX 1 ex 60 nx  (nil)  127.0.0.1:26379> incr 182XXXXXXXX  (integer) 2

    可以看到通過nx參數只有在不存在的時候才會設置成功。

  4. 分散式鎖

    可以通過set加nx或xx參數實現分散式鎖。關於Redis實現分散式鎖可以參考Distributed locks with Redis

列表

列表(list)類型是用來存儲多個有序的字元串,一個列表最多可以存儲2^32^-1個元素。在Redis中,可以對列表兩端插入(push)和彈出(pop),還可以獲取指定範圍的元素列表、獲取指定索引下標的元素等。列表是有序的,且可以插入重複數據。

常用API

  1. 從右邊插入數據:rpush key value [value ...]
  2. 從左邊插入數據:lpush key value [value ...]
  3. 從右邊彈出數據:rpop key
  4. 從左邊彈入數據:lpop key
  5. 從左側阻塞彈出:blpop key [key ...] timeout
  6. 從右側阻塞彈出:brpop key [key ...] timeout

    當使用阻塞彈出時,若列表為空,則將會阻塞指定的時間,若傳遞的timeout為0,會一致阻塞;若個客戶端同時阻塞時,有數據插入列表時,先阻塞的先可以獲取到值。

  7. 獲取指定範圍內的元素列表:lrange key start end
  8. 獲取列表指定索引下標的元素:lindex key index
  9. 獲取列表長度:llen key
  10. 修改指定索引下標的元素:lset key index newValue

內部編碼

列表類型的內部編碼有兩種:

  • ziplist(壓縮列表):當列表的元素個數小於list-max-ziplist-entries配置(默認512個),同時列表中每個元素的值都小於list-max-ziplist-value配置時(默認64位元組),Redis會選用ziplist來作為列表的內部實現來減少記憶體的使用。
  • linkedlist(鏈表):當列表類型無法滿足ziplist的條件時,Redis會使用linkedlist作為列表的內部實現。

    Redis3.2版本提供了quicklist內部編碼,簡單地說它是以一個ziplist為節點的linkedlist,它結合了ziplist和linkedlist兩者的優勢,為列表類型提供了一種更為優秀的內部編碼實現

使用場景

  1. 消息隊列
    Redis的lpush+brpop命令組合即可實現阻塞隊列,生產者客戶端使用lrpush從列表左側插入元素,多個消費者客戶端使用brpop命令阻塞式的「搶」列表尾部的元素,多個客戶端保證了消費的負載均衡和高可用性。
  • lpush+lpop=Stack(棧)
  • lpush+rpop=Queue(隊列)
  • lpsh+ltrim=CappedCollection(有限集合)
  • lpush+brpop=MessageQueue(消息隊列)

哈希

在Redis中,哈希類型是指鍵值本身又是一個鍵值對結構,形如value={{field1,value1},...{fieldN,valueN}}

常用API

  1. 設置值:hset key field value
  2. 獲取指定鍵的field值:hget key field
  3. 刪除指定鍵的field:hdel key field [field ...]
  4. 計算指定鍵的field的個數:hlen key
  5. 批量獲取:hmget key field [field ...]
  6. 批量設置:hmset key field value [field value ...]
  7. 判斷field是否存在:hexists key field
  8. 獲取指定鍵的所有內容:hexists key field

內部編碼

哈希類型的內部編碼有兩種:

  • ziplist(壓縮列表):當哈希類型元素個數小於hash-max-ziplist-entries配置(默認512個)、同時所有值都小於hash-max-ziplist-value配置(默認64位元組)時,Redis會使用ziplist作為哈希的內部實現,ziplist使用更加緊湊的結構實現多個元素的連續存儲,所以在節省記憶體方面比hashtable更加優秀。
  • hashtable(哈希表):當哈希類型無法滿足ziplist的條件時,Redis會使用hashtable作為哈希的內部實現,因為此時ziplist的讀寫效率會下降,而hashtable的讀寫時間複雜度為O(1)。

使用場景

  1. 快取關係型資料庫的用戶資訊

    • 關係型資料庫保存方式
      20191029163219.png

    • hash類型保存方式
      20191029163253.png

    需要注意的是,關係型資料庫是結構化的,每一列都要為其設置值(即使未空也可能包括NULL或特殊的標識表示NULL),而NOSQL則是稀疏的,有欄位的才需要設置值。因此關係型資料庫需要佔用更大的記憶體空間。
    Redis不適合去模擬複雜的查詢關係。

  2. 保存關係型數據的常用方式。
    • 每個記錄每條屬性一個鍵。
      • 優點:簡單直觀,每個屬性都可以更新,互不影響。
      • 缺點:佔用了太多的key。查找一個用戶的資訊比較麻煩,要獲取n次key。
    • 將用戶序列化後保存到一個鍵中。
      • 優點:合理的序列化提高redis記憶體使用效率。
      • 缺點:序列化和反序列化有一定的開銷。同時每次更新一個屬性都需要獲取全部數據反序列化更新後再重新序列化保存到redis。
    • 每個用戶欄位用對應一個field-value。
      • 優點:簡單直觀,合理使用可以減少redis記憶體使用。每個屬性互不影響。
      • 缺點:要控制hash的內部編碼轉換,若使用hashtable,會消耗更多記憶體。

集合

集合(set)類型也是用來保存多個的字元串元素,但和列表類型不一樣的是,集合中不允許有重複元素,並且集合中的元素是無序的,不能通過索引下標獲取元素。

常用API

  1. 添加元素:sadd key element [element ...]
  2. 刪除元素:srem key element [element ...]

    添加和刪除可以對多個元素進行操作,返回的是執行成功的數量。

  3. 計算元素個數:scard key
  4. 計算指定鍵的field的個數:hlen key
  5. 判斷元素是否在集合中:sismember key element
  6. 還有一些集合的操作這裡不做具體講解

內部編碼

集合類型的內部編碼有兩種:

  • intset(整數集合):當集合中的元素都是整數且元素個數小於set-max-intset-entries配置(默認512個)時,Redis會選用intset來作為集合的內部實現,從而減少記憶體的使用。
  • hashtable(哈希表):當集合類型無法滿足intset的條件時,Redis會使用hashtable作為集合的內部實現。

使用場景

保存去重後的用戶資訊,比如IP白名單等

有序集合

有序集合它保留了集合不能有重複成員的特性,但不同的是,有序集合中的元素可以排序。但是它和列表使用索引下標作為排序依據不同的是,它給每個元素設置一個分數(score)作為排序的依據。有序集合提供了獲取指定分數和元素範圍查詢、計算成員排名等功能,合理的利用有序集合,能幫助我們在實際開發中解決很多問題。

常用API

有序集合在集合基礎上多了一個分值,並通過分支排序。

  1. 添加成員:zadd key score member [score member ...]。Redis3.2為zadd命令添加了nx、xx、ch、incr四個選項:
    • nx:member必須不存在,才可以設置成功,用於添加。
    • xx:member必須存在,才可以設置成功,用於更新。
    • ch:返回此次操作後,有序集合元素和分數發生變化的個數。
    • incr:對score做增加,相當於後面介紹的zincrby。
  2. 計算成員個數:zcard key
  3. 計算 某個 成員 的 分數 zscore key member
  4. 計算 成員 的 排名 zrank key member zrevrank key member
    • zrank是從分數從低到高返回排名。
    • zrevrank是從分數從高到低返回排名。
  5. 刪除成員:zrem key member [member ...]
  6. 增加成員的分數:zincrby key increment member
  7. 返回指定排名範圍的成員:zrange key start end [withscores]zrevrange key start end [withscores]
  8. 返回指定分數範圍的成員:zrangebyscore key min max [withscores] [limit offset count]zrevrangebyscore key max min [withscores] [limit offset count]
  9. 返回指定分數範圍成員個數:zcount key min max
  10. 刪除指定排名內的升序元素:zremrangebyrank key start end
  11. 刪除指定分數範圍的成員:zremrangebyscore key min max

內部編碼

有序集合類型的內部編碼有兩種:

  • ziplist(壓縮列表):當有序集合的元素個數小於zset-max-ziplist-entries配置(默認128個),同時每個元素的值都小於zset-max-ziplist-value配置(默認64位元組)時,Redis會用ziplist來作為有序集合的內部實現,ziplist可以有效減少記憶體的使用。
  • skiplist(跳躍表):當ziplist條件不滿足時,有序集合會使用skiplist作為內部實現,因為此時ziplist的讀寫效率會下降。

使用場景

根據某個資訊排序,比如一些排行榜功能,外賣的根據距離或綜合評分排序等。

總結

本節簡單介紹了redis歷史。同時介紹了redis的安裝部署的相關知識,最後介紹了開發常用的一些API和使用場景。


參考文檔

  1. redis
  2. redis開發與運維
  3. Redis版本歷史介紹
  4. redis配置文件詳解
  5. linux下/var/run目錄下.pid文件的作用
  6. Redis的embstr與raw編碼方式不再以39位元組為界了
  7. Distributed locks with Redis

本文地址:https://www.cnblogs.com/Jack-Blog/p/11776146.html
作者部落格:傑哥很忙
歡迎轉載,請在明顯位置給出出處及鏈接