Go操作Redis


簡介

Remote Dictionary Server, 翻譯為遠程字典服務, Redis是一個完全開源的基於Key-Value的NoSQL存儲系統,他是一個使用ANSIC語言編寫的,遵守BSD協議,支持網絡、可基於內存的可持久化的日志型、Key-Value數據庫,並提供多種語言的API.

它通常被稱為數據結構服務器,因為值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等類型。

# Redis架構主要有兩個程序:
# 	Redis客戶端  redis-cli
# 	Redis服務器  redis-server
起源

Redis作者是Salvatore Sanfilippo,來自意大利的西西里島.

2008年他在開發一個LLOOGG的網站時,需要一個高性能的隊列功能,最開始使用MySQL來實現,無奈性能總是提不上去,所以決定自己實現一個專屬於LLOOGG的數據庫,他就是Redis的前身。后台 Sanfilippoj將 Redis1.0放到Github上大受歡迎。

BSD協議

Redis基於BSD開源協議,BSD開源協議是一個給於使用者很大自由的協議。可以自由的使用,修改源代碼,也可以將修改后的代碼作為開源或者專有軟件再發布。當你發布使用了BSD協議的代碼,或者以BSD協議代碼為基礎做二次開發自己的產品時,需要滿足三個條件:

  • 如果再發布的產品中包含源代碼,則在源代碼中必須帶有原來代碼中的BSD協議。
  • 如果再發布的只是二進制類庫/軟件,則需要在類庫/軟件的文檔和版權聲明中包含原來代碼中的BSD協議。
  • 不可以用開源代碼的作者/機構名字和原來產品的名字做市場推廣。

BSD代碼鼓勵代碼共享,但需要尊重代碼作者的著作權。BSD由於允許使用者修改重新發布代碼,也允許使用或在BSD代碼上開發商業軟件發布和銷售,因此是對商業集成很友好的協議。

很多的公司企業在選用開源產品的時候都首選BSD協議,因為可以完全控制這些第三方的代碼,在必要的時候可以修改或者 二次開發。

Redis原理

命令執行結構

image.png

# 1. 客戶端發送命令后,Redis服務器將為這個客戶端鏈接創造一個'輸入緩存',將命令放到里面
# 2. 再由Redis服務器進行分配挨個執行,順序是隨機的,這將不會產生並發沖突問題,也就不需要事務了.
# 3. 再將結果返回到客戶端的'輸出緩存'中,輸出緩存先存到'固定緩沖區',如果存滿了,就放入'動態緩沖區',客戶端再獲得信息結果

# 如果數據時寫入命令,例如set name:1  zhangsan 方式添加一個字符串.
# Redis將根據策略,將這對key:value來用內部編碼格式存儲,好處是改變內部編碼不會對外有影響,正常操作即可,
# 同時不同情況下存儲格式不一樣,發揮優勢.
為什么需要Redis?

傳統數據庫在存儲數據時,需要先定義schema,以確定類型(字節寬度),並以行存儲,所以每行所占的字節寬度是一致的(便於索引數據)。數據庫內部數據分為多個datapage(一般是4kb)存儲,隨着數據量的增大,數據庫查詢速度會越來越慢,其主要瓶頸在於磁盤I/O:

# 尋址時間(ms)級別
# 帶寬(G/M)

由於數據量增大查找datapage的時間也會變長,所以索引出現了。索引是一個B+T,存儲在內存中,根據索引記錄的信息,可以快速定位到datapage的位置。索引雖然會大大加快查詢速度,但是因為增刪改需要維護索引的B+T,所以會把增刪改的速度拖慢,所以索引不適合頻繁寫的表。

此外,當數據庫高並發查詢的情況下,單位時間內所需的數據量也是很大的,此時可能會受到磁盤帶寬的影響,影響磁盤的查詢速度。

在I/O上,內存相比較於磁盤,擁有較好的性能;

# 尋址時間(ns級別,磁盤是其10w倍)
# 帶寬(雙通道DDR400的寬帶為6.4GBps)

所以,出現了一批基於內存的關系型數據庫,比如SAP HAHA數據庫,其物理機器內存2T,包含軟件以及服務,購買需要1億元,由於內存關系型數據庫的昂貴價格,所以大部分公司采用了折中的方案,使用磁盤關系型數據庫+內存緩存,比如 Oracle+Memcached,Mysql+Redis

為什么使用Redis?

個人覺得項目中使用redis,主要從兩個角度去考慮性能和並發,當然,redis還具備可以做分布式鎖等其他功能,但是如果只是為了分布式鎖這些其他功能,完全還有其他中間件(如zookeeper等)代替,並不是非要使用redis,因此,這個問題主要從性能和並發兩個角度去答.

性能

如下圖所示,我摩恩碰到需要執行耗時特別久,且結果不頻繁變動的SQL,就特別適合將運行結果放入緩存,這樣,后面的請求就去緩存中讀取,使得請求能夠迅速響應.

image.png

迅速響應的標准,根據交互效果的不同,這個響應時間沒有固定標准。不過曾經有人這么告訴我:"在理想狀態下,我們的頁面跳轉需要在瞬間解決,對於頁內操作則需要在剎那間解決。另外,超過一彈指的耗時操作要有進度提示,並且可以隨時中止或取消,這樣才能給用戶最好的體驗。"

那么瞬間、剎那、一彈指具體是多少時間呢?

一剎那者為一念,二十念為一瞬,二十瞬為一彈指,二十彈指為一羅預,二十羅預為一須臾,一日一夜有三十須臾。

經過周密的計算,一瞬間為0.36秒,一剎那有0.018秒,一彈指changda7.2秒

並發

如下圖所示,在大並發的情況下,所有的請求直接訪問數據庫,數據庫會出現連接異常,這個時候,就需要使用redis做一個緩沖操作,讓請求先訪問到redis,而不是直接訪問數據庫.

image.png

單線程的Redis為什么這么快?
# 1. 基於內存的訪問,非阻塞I/O,Redis使用事件驅動模型epoll多路復用實現,連接,讀寫,關閉都轉換為事件不在網絡I/O上浪費過多的時間.
# 2. 單線程避免高並發的時候,多線程有鎖的問題和線程切換的CPU開銷問題.《雖然是單線程,但可以開多實例彌補》
# 3. 使用C語言編寫,更好的發揮服務器性能,並且代碼簡介,性能高.
Redis的特點
  • 高性能: Redis將所有數據集存儲在內存中,可以在入門級Linux機器中每秒寫(SET)11萬次,讀(GET)8.1萬次。Redis支持Pipelining命令,可一次發送多條命令來提高吞吐率,減少通信延遲。
  • 持久化:當所有數據都存在於內存中時,可以根據自上次保存以來經過的時間和/或更新次數,使用靈活的策略將更改異步保存在磁盤上。Redis支持僅附加文件(AOF)持久化模式。
  • 數據結構: Redis支持各種類型的數據結構,例如字符串,散列,集合,列表,帶有范圍查詢的有序集,位圖,超級日志和帶有半徑查詢的地理空間索引。
  • 原子操作:處理不同數據類型的Redis操作是原子操作,因此可以安全地設置或增加鍵,添加和刪除組中的元素,增加計數器等。
  • 支持的語言: Redis支持許多語言,如ActionScript,C,C ++,C#,Clojure,Common Lisp,D,Dart,Erlang,Go,Haskell,Haxe,Io,Java,JavaScript(Node.js),Julia,Lua ,Objective-C,Perl,PHP,Python,R,Ruby,Rust,Scala,Smalltalk和Tcl。
  • 主/從復制: Redis遵循非常簡單快速的主/從復制。配置文件中只需要一行來設置它,而Slave在Amazon EC2實例上完成10 MM key集的初始同步需要21秒。
  • 分片: Redis支持分片。與其他鍵值存儲一樣,跨多個Redis實例分發數據集非常容易。
  • 可移植: Redis是用ANSI C編寫的,適用於大多數POSIX系統,如Linux,BSD,Mac OS X,Solaris等。
Redis與其他key-value存儲有什么不同
  • Redis有着更為復雜的數據結構並且提供對他們的原子性操作,這是一個不同於其他數據庫的進化路徑。Redis的數據類型都是基於基本數據結構的同時對程序員透明,無需進行額外的抽象。
  • Redis運行在內存中但是可以持久化到磁盤,所以在對不同數據集進行高速讀寫時需要權衡內存,因為數據量不能大於硬件內存。在內存數據庫方面的另一個優點是,相比在磁盤上相同的復雜的數據結構,在內存中操作起來非常簡單,這樣Redis可以做很多內部復雜性很強的事情。同時,因RDB和AOF兩種磁盤持久化方式是不適合隨機訪問,所以他們可以是緊湊的以追加的方式生成。

Redis應用場景

緩存

合理的使用 緩存 能夠明顯加快訪問的速度,同時降低數據源的壓力。這也是 Redis 最常用的功能。Redis 提供了 鍵值過期時間EXPIRE key seconds)設置,並且也提供了靈活控制 最大內存內存溢出 后的 淘汰策略

排行榜

每個網站都有自己的排行榜,例如按照 熱度排名 的排行榜,發布時間 的排行榜,答題排行榜 等等。Redis 提供了 列表list)和 有序集合zset)數據結構,合理的使用這些數據結構,可以很方便的構建各種排行榜系統。

計數器

計數器 在網站應用中非常重要。例如:點贊數1瀏覽數1。還有常用的 限流操作,限制每個用戶每秒 訪問系統的次數 等等。Redis 支持 計數功能INCR key),而且計數的 性能 也非常好,計數的同時也可以設置 超時時間,這樣就可以 實現限流

社交網絡

贊/踩,粉絲,共同好友/喜好,推送,下拉刷新等是社交網站必備的功能。由於社交網站 訪問量通常比較大,而且 傳統的數據庫 不太適合保存這類數據,Redis 提供的 數據結構 可以相對比較容易實現這些功能。

消息隊列

Redis 提供的 發布訂閱PUB/SUB)和 阻塞隊列 的功能,雖然和專業的消息隊列比,還 不夠強大,但對於一般的消息隊列功能基本滿足。

Redis五種數據類型應用場景

此時只做介紹,數據類型具體介紹請看后面

  • 對於string 數據類型,常規的set/get操作,因為string 類型是二進制安全的,可以用來存放圖片,視頻等內容,另外由於Redis的高性能讀寫功能,而string類型的value也可以是數字,一般做一些復雜的計數功能的緩存,還可以用作計數器(INCR,DECR),比如分布式環境中統計系統的在線人數,秒殺等。
  • 對於 hash 數據類型,value 存放的是鍵值對結構化后的對象,比較方便操作其中某個字段,比如可以做單點登錄存放用戶信息,以cookiele作為key,設置30分鍾為緩存過期時間,能很好的模擬出類似session的效果.
  • 對於 list 數據類型,可以實現簡單的消息隊列,另外可以利用lrange命令,做基於redis的分頁功能,性能極佳,用戶體驗好.
  • 對於 set 數據類型,由於底層是字典實現的,查找元素特別快,另外set 數據類型不允許重復,利用這兩個特性我們可以進行全局去重,比如在用戶注冊模塊,判斷用戶名是否注冊;另外就是利用交集、並集、差集等操作,可以計算共同喜好,全部的喜好,自己獨有的喜好等功能。
  • 對於 zset 數據類型,有序的集合,可以做范圍查找,排行榜應用,取 TOP N 操作等,還可以做延時任務.

Redis事務

事務表示

一組動作,要么全部執行,要么全部不執行,例如在社交網站上用戶A關注了
用戶B, 那么需要在用戶A的關注表中加入用戶B, 並且在用戶B的粉絲表中
添加用戶A, 這兩個行為要么全部執行, 要么全部不執行, 否則會出現數據
不一致的情況。

Redis 提供了簡單的事務功能, 將一組需要一起執行的命令放到 multi
exec 兩個命令之間。 multi 命令代表事務開始, exec 命令代表事務結束, 它們
之間的命令是原子順序執行的, 例如下面操作實現了上述用戶關注問題。

之間執行命令將不執行,在緩沖中,等exec后裁真正開始執行

如果其中有語法錯誤,命令打錯了,那整個事務將結束.
如果把值寫錯了,多個字母,但語法正確,那事務是正確的,要手動恢復,不支持回滾.
在事務開始前,用watch key可以檢要操作的key,如果key在事務開始后有變化,例如multi開始修改時,這個key被其他客戶端修改,事務將不進行操作.

一個Redis從開始到執行會經歷以下階段

# 1. 事務開始
# 2. 命令入隊
# 3. 執行事務
Redis事務相關命令
命令 描述
DISCARD 取消事物,放棄執行事物內的所有命令
EXEC 執行所有事物塊內的命令 EXEC{執行事務}
MULTI 標志一個事務塊的開始 MULTI{啟動一個事務}
UNWATCH 取消WATCH命令多所有key的監視

| WAHCH key [key …] | 監視一個(或多個)key,如果在事務執行之前這個(或這些) key 被其他命令所改動,那么事務將被打斷。

Redis事務與Mysql事務區別
# 1. Redis不支持回滾,即一條命令當做事務執行時,當有一個中間的命令發生錯誤,mysql將會把之前的操作取消並結束事務.

# 2. 但是Redis不會,Redis還會繼續把剩下的命令執行下去,忽略發生錯誤的命令.

Redis的過期策略以及內存淘汰機制

比如Redis只能存5G數據,可是你寫了10G,那會刪5G的數據。怎么刪的,這個問題思考過么?還有,你的數據已經設置了過期時間,但是時間到了,內存占用率還是比較高,有思考過原因么?

Redis采用的是定期刪除 + 惰性刪除策略

為什么不用定時刪除策略?

定時刪除,用一個定時器來負責監視key,過期則自動刪除,雖然內存及時釋放,但是十分消耗CPU資源,大並發情況下,CPU要將時間應用在處理請求,而不是刪除key,因此沒有采用這一策略.

定期刪除+惰性刪除是如何工作的?

定期刪除,redis默認每個100ms檢查,是否有過期的key,有過期key則刪除。需要說明的是,redis不是每個100ms將所有的key檢查一次,而是隨機抽取進行檢查(如果每隔100ms,全部key進行檢查,redis豈不是卡死)。因此,如果只采用定期刪除策略,會導致很多key到時間沒有刪除。

於是,惰性刪除派上用場,也就是說你獲取某個key的時候,redis會檢查一下,這個key如果設置了過期時間那么是否過期了?如果過期了此時就會刪除.

采用定期刪除+惰性刪除就沒其他問題了嗎?
並不是,如果定期刪除刪除沒刪除key。然后你也沒即時去請求key,也就是說惰性刪除也沒生效。這樣,redis的內存會越來越高。那么就應該采用內存淘汰機制.
在redis.conf中有一行配置

# maxmemory-policy volatile-lru

#該配置就是配內存淘汰策略的
# 1. noeviction: 當內存不足以容納寫入數據時,新寫入會報錯,應該沒人用.
# 2. allkeys-lru: 當內存不足以容納新寫入數據時,在鍵空間中,移除最少使用的那個key.推薦使用
# 3. allkeys-random: 當內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個key,應該也沒人用,不刪最少使用,隨機刪?
# 4. volatile-lru: 當內存不足以容納寫入數據時,在設置了過期時間的鍵空間中,移除最少使用的key,
# 一般是吧redis既當緩存又當持久化存儲才用,不推薦.
# 5. volatile-random: 當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,隨機移除某個key,依然不推薦.
# 6. volatile-ttl: 當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,有更早過期的key優先移除,不推薦.

# 如果沒有設置expire的key,不滿足先決條件(prerequisites);
# 那么volatile-lru,volatile-random和volatile-ttl策略行為和noeviction(不刪)基本一致

Redis單機部署

此篇文章只做單機服務器搭建,高可用哨兵和集群請看下一篇

環境
[Redis-Server]
	主機名 = redis-master-1
	系統 = CentOS7.6.1810
	地址 = 121.36.43.223
	軟件 = redis-4.0.14

# 版本
# Redis的奇數版本為非穩定版本,例如2.7.3.1,如果為偶數則為穩定版本,例如3.2,3.4;
節點名 IP 軟件版本 硬件 網絡 說明
redis-master 192.168.171.136 list 里面都有 2C4G Nat,內網 測試環境
下載解壓Redis源碼包
yum -y install gcc
wget http://download.redis.io/releases/redis-4.0.14.tar.gz
tar xvf redis-4.0.14.tar.gz -C /opt/
cd /opt/redis-4.0.14
編譯安裝
# Redis的編譯,只將命令文件編譯,將會在當前目錄生成bin目錄
make && make install  PREFIX=/usr/local/redis
cd ..
mv redis-4.0.14/* /usr/local/redis/

# 創建環境變量
echo 'PATH=$PATH:/usr/local/redis/src/' >> /etc/profile
source /etc/profile

# 此時在任何目錄位置都可以是用redis-server等相關命令
[root@redis1 ~]# redis-
redis-benchmark  redis-check-rdb  redis-sentinel   redis-trib.rb    
redis-check-aof  redis-cli        redis-server 
Redis可執行文件
可執行文件 作用
redis-server 啟動redis服務
redis-cli redis 命令行客戶端
redis-benchmark Redis基准測試工具
redis-check-aof redis AOF持久化文件檢測和修復工具
redis-check-dump redis RDB持久化文件檢測和修復工具
redis-sentinel 啟動redis sentinel
修改配置文件
# redis進程是否以守護進程的方式運行,yes為是,no為否(不以守護進程的方式運行會占用一個終端)
daemonize no


# 指定redis進程的PID文件存放位置
pidfile /var/run/redis.pid


# redis進程的端口號
port 6379


# 綁定的主機地址
bind 127.0.0.1


# 客戶端閑置多長時間后關閉連接,默認此參數為0即關閉此功能
timeout 300


# redis日志級別,可用的級別有debug.verbose.notice.warning
loglevel verbose


# log文件輸出位置,如果進程以守護進程的方式運行,此處又將輸出文件設置為stdout的話,就會將日志信息輸出到/dev/null里面去了
logfile stdout


# 設置數據庫的數量,默認為0可以使用select <dbid>命令在連接上指定數據庫id
databases 16


# 指定在多少時間內刷新次數達到多少的時候會將數據同步到數據文件
save <seconds> <changes>


# 指定存儲至本地數據庫時是否壓縮文件,默認為yes即啟用存儲
rdbcompression yes


# 指定本地數據庫文件名
dbfilename dump.db


# 指定本地數據問就按存放位置
dir ./


# 指定當本機為slave服務時,設置master服務的IP地址及端口,在redis啟動的時候他會自動跟master進行數據同步
slaveof <masterip> <masterport>


# 當master設置了密碼保護時,slave服務連接master的密碼
masterauth <master-password>


# 設置redis連接密碼,如果配置了連接密碼,客戶端在連接redis是需要通過AUTH<password>命令提供密碼,默認關閉
requirepass footbared


# 設置同一時間最大客戶連接數,默認無限制。redis可以同時連接的客戶端數為redis程序可以打開的最大文件描述符,如果設置 maxclients 0,表示不作限制。當客戶端連接數到達限制時,Redis會關閉新的連接並向客戶端返回 max number of clients reached 錯誤信息
maxclients 128


# 指定Redis最大內存限制,Redis在啟動時會把數據加載到內存中,達到最大內存后,Redis會先嘗試清除已到期或即將到期的Key。當此方法處理后,仍然到達最大內存設置,將無法再進行寫入操作,但仍然可以進行讀取操作。Redis新的vm機制,會把Key存放內存,Value會存放在swap區
maxmemory<bytes>


# 指定是否在每次更新操作后進行日志記錄,Redis在默認情況下是異步的把數據寫入磁盤,如果不開啟,可能會在斷電時導致一段時間內的數據丟失。因為redis本身同步數據文件是按上面save條件來同步的,所以有的數據會在一段時間內只存在於內存中。默認為no。
appendonly no


# 指定跟新日志文件名默認為appendonly.aof
appendfilename appendonly.aof


# 指定更新日志的條件,有三個可選參數 - no:表示等操作系統進行數據緩存同步到磁盤(快),always:表示每次更新操作后手動調用fsync()將數據寫到磁盤(慢,安全), everysec:表示每秒同步一次(折衷,默認值);
appendfsync everysec

我們需要修改的配置

# 設置后台啟動
# 由於Redis默認是前台啟動,不建議使用.可以修改為后台
daemonize yes


# 禁止protected-mode yes/no(保護模式,是否只允許本地訪問)
protected-mode


# 設置遠程訪問
# Redis默認只允許本機訪問,把bind修改為bind 0.0.0.0 此設置會變成允許所有遠程訪問,如果指定限制訪問,可設置對應IP。
# bind指定是redis所在服務器網卡的IP,不指定本機網卡IP,可能導致你的Redis實例無法啟動
# 如果想限制IP訪問,內網的話通過網絡接口(網卡限定),讓客戶端訪問固定網卡鏈接redis
# 如果是公網,通過iptables指定某個IP允許訪問
bind 0.0.0.0

# 配置Redis日志記錄
# 找到logfile,默認為logfile "",改為自定義日志格式
logfile  /var/log/redis_6379.log

# 把requirepass修改為123456,修改之后重啟下服務
requirepass "123456"
# 不重啟Redis設置密碼
# 在配置文件中配置requirepass的密碼(當Redis重啟時密碼依然生效)
127.0.0.1:6379> config set requirepass test123
# 查詢密碼
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "test123"

# 密碼驗證
127.0.0.1:6379> auth test123
OK
127.0.0.1:6379> set name flying
OK
127.0.0.1:6379> get name
"flying"

# 遠程主機連接
# redis-cli  -h  redis_ip -p redis_port -a password
啟動測試
# 放到后台輸出,redis自帶日志了,可以輸出到黑洞
nohup redis-server /usr/local/redis/redis.conf &> /usr/local/redis/redis.log &

# 關閉命令
redis-cli -h 127.0.0.1 -p 6379 -a 123456 shutdown
# 注意:不建議使用 kill -9,這種方式不但不會做持久化操作,還會造成緩沖區等資源不能優雅關閉。極端情況下造成 AOF 和 復制丟失數據 的情況。
# shutdown 還有一個參數,代表是否在關閉 redis 前,生成 持久化文件,命令為 redis-cli shutdown nosave|save。


# 設置開機自啟動
echo "redis-server /usr/local/redis.conf" >> /etc/rc.local
注冊Redis為系統服務

在/etc/init.d目錄添加Redis服務的啟動,暫停和重啟腳本

vim /etc/init.d/redis
#!/usr/bin/env bash
# chkconfig: 2345 10 90  
# description: Start and Stop redis 
PORT=6379
EXEC=/usr/local/redis/src/redis-server
CLIEXEC=/usr/local/redis/src/redis-cli

PIDFILE=/var/run/redis_${PORT}.pid
CONF="/etc/redis/${PORT}.conf"

case "$1" in
    start)
        if [ -f $PIDFILE ]
        then
                echo "$PIDFILE exists, process is already running or crashed"
        else
                echo "Starting Redis server..."
                $EXEC $CONF &>/dev/null &
        fi
        ;;
    stop)
	PID=$(cat $PIDFILE)
        if [ ! -f $PIDFILE ]
        then
                echo "$PIDFILE does not exist, process is not running"
        else
                echo "Stopping ..."
                $CLIEXEC -p $PORT shutdown
                while [ -d /proc/${PID} ]
                do
                    echo "Waiting for Redis to shutdown ..."
                    sleep 1
                done
                echo "Redis stopped"
        fi
        ;;
    restart)
        "$0" stop
        sleep 3
        "$0" start
        ;;
    *)
        echo "Please use start or stop as first argument"
        ;;
esac



chmod +x /etc/init.d/redis  
mkdir /etc/redis  
cp /usr/local/redis/redis.conf /etc/redis/6379.conf  
chkconfig --add redis  
chkconfig redis on  
  
service redis start  
service redis restart

Redis常用命令

Redis-value操作

此處Redis五種數據類型具體操作先不演示,只做一個簡單的key操作,具體操作等Python操作Redis再寫詳細,前面主要以運維為主

# 添加一個key[name]為youmen
127.0.0.1:6379> set name youmen
OK

# 獲取key
127.0.0.1:6379> get name
"youmen"

# 查看當前數據庫里面的key
127.0.0.1:6379> keys *
1) "name"

# 判斷key是否存在
127.0.0.1:6379> exists name
(integer) 1

# 刪除key值,也就刪除了這條數據
127.0.0.1:6379> del name
(integer) 1
# 查找不到對應的key返回值就會使(integer)0
127.0.0.1:6379> exists name
(integer) 0



# 設置key的過期時間,過期后key自動刪除
127.0.0.1:6379> set name youmen
OK
127.0.0.1:6379> set name youmen ex 2
127.0.0.1:6379> expire name 10
(integer) 1
127.0.0.1:6379> ttl name
(integer) 6
127.0.0.1:6379> ttl name
(integer) 2
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> exists name
(integer) 0
持久化命令
# save:將數據同步保存到磁盤
# bgsave:將數據異步保存到磁盤
# lastsave:返回上次成功將數據保存到磁盤的Unix時戳
# shundown:將數據同步保存到磁盤,然后關閉服務
遠程服務控制
# info:提供服務器的信息和統計
# info clients:  查看客戶端信息.
# monitor:實時轉儲收到的請求
# slaveof:改變復制策略設置
# config:在運行時配置Redis服務器
# client list: 查看鏈接的客戶端有哪些
# chient kill 127.0.0.1:50390:  殺掉客戶端鏈接
# config get dir: 查看存儲文件目錄
# config get *:  查看所有配置
# config set requirepass 123 : 修改配置,即時生效
# config get bind :  查看配置文件中的監聽地址
Redis info參數介紹
# 集群每個節點獲取的信息不一樣
127.0.0.1:6379> info
# Server
redis_version:4.0.14
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:5ad4d17d599d7e92
redis_mode:standalone			# 運行模式,單機或集群
os:Linux 3.10.0-1062.1.1.el7.x86_64 x86_64	# 服務器的宿主操作系統
arch_bits:64					# 架構
multiplexing_api:epoll			# redis所使用的事件循環機制
atomicvar_api:atomic-builtin	# 原子處理api
gcc_version:4.8.5				# 編譯Redis時所使用的GCC版本
process_id:19955				# 服務器進程的PID
run_id:3cd8b85e4c852fc93adbbb51eaee051a0a6a788d	# 標識redis server的隨機值
tcp_port:6379
uptime_in_seconds:4272			# redis server啟動的時間(單位s)
uptime_in_days:0				# redis server啟動的時間(單位d)
hz:10								
# hz:10  edis內部調度(進行關閉timeout的客戶端,刪除過期key等)頻率,程序規定serverCron每秒運行十次.
lru_clock:1935465						
# 自增的時鍾,用於LRU管理,該時鍾ms(hz=10,因此每1000ms/10=100ms執行一次定時任務)更新一次
executable:/usr/local/redis/src/redis-server	# 執行文件
config_file:/etc/redis/6379.conf  # 配置文件路徑
# Clients
connected_clients:1				# 已連接客戶端的數量(不包括通過從屬服務器連接的客戶端)
client_longest_output_list:0			# 當前連接的客戶端中,最長的輸出列表
client_biggest_input_buf:0			# 當前連接的客戶端中,最大輸入緩存
blocked_clients:0				# 正在等待阻塞命令(BLPOP、BRPOP、Brpoplpush)的客戶端的數量

# Memory
used_memory:849400				# 由Redis分配器分配的內存總量,以字節(byte)為單位
used_memory_human:829.49K			# 以人類可讀的格式返回Redis分配的內存總量
used_memory_rss:8278016				
# 從操作系統的角度,返回Redis已分配的內存總量(俗稱常駐集大小),這個值和top,ps等命令輸出一致
used_memory_rss_human:7.89M			
# 以人類可讀的格式,從操作系統角度,返回Redis已分配的內存總量(俗稱常駐集大小),這個值和top,ps等命令輸出一致

used_memory_peak:849472				# redis的內存消耗峰值(以字節為單位)
used_memory_peak_human:829.56K			# 以人類可讀的格式返回redis的內存消耗峰值
used_memory_peak_perc:99.99%			# (used_memory/ used_memory_peak) *100%

used_memory_overhead:836622
# Redis為了維護數據集的內部機制所需的內存開銷,
# 包括所有客戶端輸出緩沖區、查詢緩沖區、AOF重寫緩沖區和主從復制的backlog

used_memory_startup:786608			# Redis服務器啟動時消耗的內存
used_memory_dataset:12778			# used_memory—used_memory_overhead
used_memory_dataset_perc:20.35%			
# 100%*(used_memory_dataset/(used_memory—used_memory_startup))

total_system_memory:1926860800			# 整個系統內存
total_system_memory_human:1.79G			# 以人類可讀格式,顯示整個系統內存
used_memory_lua:37888				# lua腳本存儲占用的內存
used_memory_lua_human:37.00K			# 以人類可讀的格式,顯示lua腳本占用的內存
maxmemory:0					# Redis實例的最大內存配置
maxmemory_human:0B				# 以人類可讀格式,顯示Redis實例的最大內存配置
maxmemory_policy:noeviction			# 當達到maxmemory時的淘汰策略
mem_fragmentation_ratio:9.74			# used_memory_rss/used_memory
mem_allocator:jemalloc-4.0.3			# 內存分配器
active_defrag_running:0				# 表示沒有活動的defrag任務正在運行(defrag: 表示內存碎片整理)
lazyfree_pending_objects:0			# 0表示不存在延遲釋放(也有資料翻譯末惰性刪除)的掛起對象
# Persistence
loading:0					# 服務器是否正在載入持久化文件
rdb_changes_since_last_save:0			
# 離最近一次成功生成rdb文件,寫入命令的個數,即有多少個寫入命令沒有持久化

rdb_bgsave_in_progress:0			# 服務器是否正在創建rdb文件
rdb_last_save_time:1578992739			
# 離最近一次成功創建rdb文件的時間戳,當前時間戳-rdb_last_save_time=多少秒未成功生成rdb文件
rdb_last_bgsave_status:ok			# 最近一次rdb持久化是否成功
rdb_last_bgsave_time_sec:0			# 最近一次成功生成rdb文件耗時總數
rdb_current_bgsave_time_sec:-1		
# 如果服務器正在創建rdb文件,那么這個域記錄的就是當前創建操作已經耗費的秒數
rdb_last_cow_size:6537216			 
# RDB過程中父進程與子進程相比執行了多少修改(包括讀緩沖區,寫緩沖區,數據修改等)
aof_enabled:0					# 是否開啟了aof
aof_rewrite_in_progress:0			# 標識aof的rewrite操作是否正在進行中
aof_rewrite_scheduled:0			
# rewrite任務計划,當客戶端發送bgrewriteaof指令,如果當前rewrite子進程正在執行,
# 那么將客戶端請求的bgrewriteaof變為計划任務,待aof子進程結束后執行rewrite
aof_last_rewrite_time_sec:-1			# 最近一次aof rewrite耗費的時長
aof_current_rewrite_time_sec:-1			# 如果rewrite操作正在進行,則記錄所使用的時間,單位秒
aof_last_bgrewrite_status:ok			# 上次bgrewriteaof操作的狀態
aof_last_write_status:ok			# 上次aof寫入狀態
aof_last_cow_size:0				
# AOF過程中父進程與子進程相比執行了多少次修改(包括讀緩沖區,寫緩沖區,數據修改等)

# Stats
total_connections_received:4
# 新創建連接個數,如果新創建連接過多,過度地創建和銷毀連接對性能有影響,
# 說明短連接嚴重或連接池使用有問題,需調研代碼的連接設置
total_commands_processed:87			# Redis處理的命令數
instantaneous_ops_per_sec:0			# redis當前的qps,redis內部較實時的每秒執行的命令數
total_net_input_bytes:2757			# redis網絡入口流量字節數
total_net_output_bytes:53214			# redis網絡出口流量字節數
instantaneous_input_kbps:0.00			# redis網絡入口kps
instantaneous_output_kbps:0.00			# redis網絡入口kps
rejected_connections:0				
# 拒絕的連接的個數,redis連接個數達到maxclients限制,拒絕新連接個數
sync_full:0					# 主從完全同步成功次數
sync_partial_ok:0				# 主從部分同步成功次數
sync_partial_err:0				# 主從部分失敗次數
expired_keys:1					# 運行以來過期的key的數量
expired_stale_perc:0.00				# 過期的比率
expired_time_cap_reached_count:0		# 過期計數
evicted_keys:0					# 運行以來剔除(超過maxmemory后)的key的數量
keyspace_hits:26				# 命中次數
keyspace_misses:10				# 未命中次數
pubsub_channels:0				# 當前使用中的頻道數量
pubsub_patterns:0				# 當前使用的模式的數量
latest_fork_usec:578				# 最近一次fork操作阻塞redis進程的耗時數,單位微妙
migrate_cached_sockets:0			# 是否已經緩存到了改地址的鏈接
slave_expires_tracked_keys:0			# 從實例到期key的數量
active_defrag_hits:0				# 主動碎片整理命中次數
active_defrag_misses:0				# 主動碎片整理未命中次數
active_defrag_key_hits:0			# 主動碎片整理key命中次數
active_defrag_key_misses:0			# 主動碎片整理key未命中次數.
# Replication
role:master					# 實例的角色,是master or slave
connected_slaves:0				# 連接的slave實例個數
master_replid:54da017499c5257de9b00d168dd49c04b8bbe7ef	# 主實例啟動隨機字符串
master_replid2:0000000000000000000000000000000000000000	# 主實例啟動隨機字符串
master_repl_offset:0			
# 主從同步偏移量,此值如果和上面的offset相同說明主從一致沒延遲,與master_replid可被用來標識主實例復制流的位置.
second_repl_offset:-1 				# 主從同步偏移量2,此值如果和上面的offset相同說明主從一致沒延遲
repl_backlog_active:0				# 復制積壓緩沖區是否開啟
repl_backlog_size:1048576			# 復制積壓緩沖大小
repl_backlog_first_byte_offset:0		# 復制緩沖區偏移量的大小
repl_backlog_histlen:0
# 此值等於 master_repl_offset - repl_backlog_first_byte_offset,該值不會超過repl_backlog_size的大小

# CPU
used_cpu_sys:1.90				# 將所有redis主進程在核心態所占用的CPU時求和累積起來
used_cpu_user:1.14				# 將所有redis主進程在用戶態所占用CPU時求和累計起來
used_cpu_sys_children:0.01		# 將后台進程在核心態所占用的CPU時求和累計起來
used_cpu_user_children:0.00		# 將后台進程在用戶態所占用CPU時求和累計起來

# Cluster
cluster_enabled:0

# Keyspace
db0:keys=7,expires=0,avg_ttl=0

部署Go操作Redis客戶端

安裝

golang操作redis的客戶端包有多個比如redigo、go-redis,github上Star最多的莫屬redigo。

github地址:https://github.com/garyburd/redigo 目前已經遷移到:https://github.com/gomodule/redigo

文檔:https://godoc.org/github.com/garyburd/redigo/redis

go get github.com/garyburd/redigo/redis
import "github.com/garyburd/redigo/redis"
連接

Conn接口是與Redis協作的主要接口,可以使用Dial,DialWithTimeout或者NewConn函數來創建連接,當任務完成時,應用程序必須調用Close函數來完成操作。

package main

import (
	"fmt"
	"github.com/garyburd/redigo/redis"
)

func main() {
	conn,err := redis.Dial("tcp","121.36.43.223:6379")
	if err != nil{
		fmt.Println("connect redis server:",err)
		return
	}
	fmt.Println(conn)
	defer conn.Close()
}

Go常用Redis命令操作

命令操作

通過使用Conn接口中的do方法執行redis命令,redis命令大全參考:http://doc.redisfans.com/

go中發送與響應對應類型:

Do函數會必要時將參數轉化為二進制字符串

Go Type Conversion
[]byte Sent as is
string Sent as is
int, int64 strconv.FormatInt(v)
float64 strconv.FormatFloat(v, 'g', -1, 64)
bool true -> "1", false -> "0"
nil ""
all other types fmt.Print(v)

Redis命令響應會用一下Go類型表示

Redis type Go type
error redis.Error
integer int64
simple string string
bulk string []byte or nil if value not present.
array []interface{} or nil if value not present

可以使用GO的類型斷言或者reply輔助函數將返回的interface{}轉換為對應類型

SET,GET
package main

import (
	"fmt"
	"github.com/garyburd/redigo/redis"
)

func main()  {
	conn,err := redis.Dial("tcp","121.36.43.223:6379")
	if err != nil{
		fmt.Println("connect redis error:",err)
		return
	}
	defer conn.Close()

	_,err = conn.Do("SET","youmen","18")
	if err != nil{
		fmt.Println("redis set error:",err)
	}
	name, err := redis.String(conn.Do("GET","youmen"))
	if err != nil{
		fmt.Println("redis get error:",err)
	}else {
		fmt.Printf("Get name: %s \n",name)
	}
}
設置key過期時間
package main

import (
	"fmt"
	"github.com/garyburd/redigo/redis"
)

func main()  {
	conn,err := redis.Dial("tcp","121.36.43.223:6379")
	if err != nil{
		fmt.Println("connect redis error:",err)
		return
	}
	defer conn.Close()
	_, err = conn.Do("SET", "name", "youmen")
	if err != nil {
		fmt.Println("redis set error:", err)
	}
	_,err = conn.Do("expire","name",10)
	if err != nil{
		fmt.Println("set expire error:",err)
		return
	}
	name,err := redis.String(conn.Do("GET","name"))
	if err != nil{
		fmt.Println("redis get error:",err)
	} else {
		fmt.Printf("GET name: %s \n",name)
	}
}
批量獲取mget,批量設置mset
package main

import (
	"fmt"
	"github.com/garyburd/redigo/redis"
	"reflect"
)

func main()  {
	conn,err := redis.Dial("tcp","121.36.43.223:6379")
	if err != nil{
		fmt.Println("connect redis error:",err)
		return
	}
	defer conn.Close()

	_,err = conn.Do("MSET","name","youmen","age","22")
	if err != nil{
		fmt.Println("redis mset error:",err)
	}
	res,err := redis.Strings(conn.Do("MGET","name","age"))
	if err != nil{
		fmt.Println("redis get error",err)
	} else {
		res_type := reflect.TypeOf(recover())
		fmt.Printf("res type : %s \n", res_type)
		fmt.Printf("MGET name: %s \n", res)
		fmt.Println(len(res))
	}
}
列表操作
package main

import (
	"fmt"
	"github.com/garyburd/redigo/redis"
	"reflect"
)

func main()  {
	conn,err := redis.Dial("tcp","121.36.43.223:6379")
	if err != nil{
		fmt.Println("connect redis error:",err)
		return
	}
	defer conn.Close()
	_, err = conn.Do("LPUSH", "list1", "l1","l2","l3")
	if err != nil {
		fmt.Println("redis mset error:", err)
	}
	res, err := redis.String(conn.Do("LPOP", "list1"))
	if err != nil {
		fmt.Println("redis POP error:", err)
	} else {
		res_type := reflect.TypeOf(res)
		fmt.Printf("res type : %s \n", res_type)
		fmt.Printf("res  : %s \n", res)
	}
}
hash操作
package main

import (
	"fmt"
	"github.com/garyburd/redigo/redis"
	"reflect"
)

func main()  {
	conn,err := redis.Dial("tcp","121.36.43.223:6379")
	if err != nil{
		fmt.Println("connect redis error:",err)
		return
	}
	defer conn.Close()

	_, err = conn.Do("HSET", "student","name", "wd","age",22)
	if err != nil {
		fmt.Println("redis mset error:", err)
	}
	res, err := redis.Int64(conn.Do("HGET", "student","age"))
	if err != nil {
		fmt.Println("redis HGET error:", err)
	} else {
		res_type := reflect.TypeOf(res)
		fmt.Printf("res type : %s \n", res_type)
		fmt.Printf("res  : %d \n", res)
	}
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM