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上大受歡迎。程序員
Redis基於BSD開源協議,BSD開源協議是一個給於使用者很大自由的協議。能夠自由的使用,修改源代碼,也能夠將修改後的代碼做爲開源或者專有軟件再發布。當你發佈使用了BSD協議的代碼,或者以BSD協議代碼爲基礎作二次開發本身的產品時,須要知足三個條件:github
BSD代碼鼓勵代碼共享,但須要尊重代碼做者的著做權。BSD因爲容許使用者修改和從新發布代碼,也容許使用或在BSD代碼上開發商業軟件發佈和銷售,所以是對商業集成很友好的協議。golang
不少的公司企業在選用開源產品的時候都首選BSD協議,由於能夠徹底控制這些第三方的代碼,在必要的時候能夠修改或者 二次開發。redis
命令執行結構
sql
# 1. 客戶端發送命令後,Redis服務器將爲這個客戶端連接創造一個'輸入緩存',將命令放到裏面 # 2. 再由Redis服務器進行分配挨個執行,順序是隨機的,這將不會產生併發衝突問題,也就不須要事務了. # 3. 再將結果返回到客戶端的'輸出緩存'中,輸出緩存先存到'固定緩衝區',若是存滿了,就放入'動態緩衝區',客戶端再得到信息結果 # 若是數據時寫入命令,例如set name:1 zhangsan 方式添加一個字符串. # Redis將根據策略,將這對key:value來用內部編碼格式存儲,好處是改變內部編碼不會對外有影響,正常操做便可, # 同時不一樣狀況下存儲格式不同,發揮優點.
傳統數據庫在存儲數據時,須要先定義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,就特別適合將運行結果放入緩存,這樣,後面的請求就去緩存中讀取,使得請求可以迅速響應.
迅速響應的標準,根據交互效果的不一樣,這個響應時間沒有固定標準。不過曾經有人這麼告訴我:"在理想狀態下,咱們的頁面跳轉須要在瞬間解決,對於頁內操做則須要在剎那間解決。另外,超過一彈指的耗時操做要有進度提示,而且能夠隨時停止或取消,這樣才能給用戶最好的體驗。"
那麼瞬間、剎那、一彈指具體是多少時間呢?
一剎那者爲一念,二十念爲一瞬,二十瞬爲一彈指,二十彈指爲一羅預,二十羅預爲一須臾,一日一晚上有三十須臾。
通過周密的計算,一瞬間爲0.36秒,一剎那有0.018秒,一彈指changda7.2秒
併發
以下圖所示,在大併發的狀況下,全部的請求直接訪問數據庫,數據庫會出現鏈接異常,這個時候,就須要使用redis作一個緩衝操做,讓請求先訪問到redis,而不是直接訪問數據庫.
# 1. 基於內存的訪問,非阻塞I/O,Redis使用事件驅動模型epoll多路複用實現,鏈接,讀寫,關閉都轉換爲事件不在網絡I/O上浪費過多的時間. # 2. 單線程避免高併發的時候,多線程有鎖的問題和線程切換的CPU開銷問題.《雖然是單線程,但能夠開多實例彌補》 # 3. 使用C語言編寫,更好的發揮服務器性能,而且代碼簡介,性能高.
合理的使用 緩存 可以明顯加快訪問的速度,同時下降數據源的壓力。這也是
Redis
最經常使用的功能。Redis
提供了 鍵值過時時間(EXPIRE key seconds
)設置,而且也提供了靈活控制 最大內存 和 內存溢出 後的 淘汰策略。
每一個網站都有本身的排行榜,例如按照 熱度排名 的排行榜,發佈時間 的排行榜,答題排行榜 等等。
Redis
提供了 列表(list
)和 有序集合(zset
)數據結構,合理的使用這些數據結構,能夠很方便的構建各類排行榜系統。
計數器 在網站應用中很是重要。例如:點贊數加
1
,瀏覽數 加1
。還有經常使用的 限流操做,限制每一個用戶每秒 訪問系統的次數 等等。Redis
支持 計數功能(INCR key
),並且計數的 性能 也很是好,計數的同時也能夠設置 超時時間,這樣就能夠 實現限流。
贊/踩,粉絲,共同好友/喜愛,推送,下拉刷新等是社交網站必備的功能。因爲社交網站 訪問量一般比較大,並且 傳統的數據庫 不太適合保存這類數據,
Redis
提供的 數據結構 能夠相對比較容易實現這些功能。
Redis
提供的 發佈訂閱(PUB/SUB
)和 阻塞隊列 的功能,雖然和專業的消息隊列比,還 不夠強大,但對於通常的消息隊列功能基本知足。
此時只作介紹,數據類型具體介紹請看後面
一組動做,要麼所有執行,要麼所有不執行,例如在社交網站上用戶A關注了
用戶B, 那麼須要在用戶A的關注表中加入用戶B, 而且在用戶B的粉絲表中
添加用戶A, 這兩個行爲要麼所有執行, 要麼所有不執行, 不然會出現數據
不一致的狀況。Redis 提供了簡單的事務功能, 將一組須要一塊兒執行的命令放到
multi
和
exec
兩個命令之間。multi
命令表明事務開始,exec
命令表明事務結束, 它們
之間的命令是原子順序執行的, 例以下面操做實現了上述用戶關注問題。
之間執行命令將不執行,在緩衝中,等exec後裁真正開始執行
若是其中有語法錯誤,命令打錯了,那整個事務將結束.
若是把值寫錯了,多個字母,但語法正確,那事務是正確的,要手動恢復,不支持回滾.
在事務開始前,用watch key能夠檢要操做的key,若是key在事務開始後有變化,例如multi開始修改時,這個key被其餘客戶端修改,事務將不進行操做.
一個Redis從開始到執行會經歷如下階段
# 1. 事務開始 # 2. 命令入隊 # 3. 執行事務
命令 | 描述 |
---|---|
DISCARD | 取消事物,放棄執行事物內的全部命令 |
EXEC | 執行全部事物塊內的命令 EXEC{執行事務} |
MULTI | 標誌一個事務塊的開始 MULTI{啓動一個事務} |
UNWATCH | 取消WATCH命令多全部key的監視 |
| WAHCH key [key …] | 監視一個(或多個)key,若是在事務執行以前這個(或這些) key 被其餘命令所改動,那麼事務將被打斷。
# 1. Redis不支持回滾,即一條命令當作事務執行時,當有一箇中間的命令發生錯誤,mysql將會把以前的操做取消並結束事務. # 2. 可是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-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,內網 | 測試環境 |
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-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
在/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五種數據類型具體操做先不演示,只作一個簡單的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 : 查看配置文件中的監聽地址
# 集羣每一個節點獲取的信息不同 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
golang操做redis的客戶端包有多個好比redigo、go-redis,github上Star最多的莫屬redigo。
github地址:https://github.com/garyburd/redigo 目前已經遷移到:https://github.com/gomodule/redigo
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() }
經過使用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{}轉換爲對應類型
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) } }
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) } }
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) } }
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) } }