Go操做Redis

簡介

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

它一般被稱爲數據結構服務器,由於值(value)能夠是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等類型。mysql

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

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

2008年他在開發一個LLOOGG的網站時,須要一個高性能的隊列功能,最開始使用MySQL來實現,無奈性能老是提不上去,因此決定本身實現一個專屬於LLOOGG的數據庫,他就是Redis的前身。後臺 Sanfilippoj將 Redis1.0放到Github上大受歡迎。程序員

BSD協議

Redis基於BSD開源協議,BSD開源協議是一個給於使用者很大自由的協議。能夠自由的使用,修改源代碼,也能夠將修改後的代碼做爲開源或者專有軟件再發布。當你發佈使用了BSD協議的代碼,或者以BSD協議代碼爲基礎作二次開發本身的產品時,須要知足三個條件:github

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

BSD代碼鼓勵代碼共享,但須要尊重代碼做者的著做權。BSD因爲容許使用者修改從新發布代碼,也容許使用或在BSD代碼上開發商業軟件發佈和銷售,所以是對商業集成很友好的協議。golang

不少的公司企業在選用開源產品的時候都首選BSD協議,由於能夠徹底控制這些第三方的代碼,在必要的時候能夠修改或者 二次開發。redis

Redis原理

命令執行結構sql

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,因此會把增刪改的速度拖慢,因此索引不適合頻繁寫的表。vim

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

在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)
	}
}
相關文章
相關標籤/搜索