日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

Redis 数据库入门教程

發(fā)布時間:2024/7/23 数据库 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Redis 数据库入门教程 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

?

From:http://www.jb51.net/article/56448.htm

Redis 菜鳥教程:http://www.runoob.com/redis/redis-tutorial.html

Redis 設(shè)計與實現(xiàn):http://redisbook.com/

Redis基礎(chǔ)、高級特性與性能調(diào)優(yōu):https://www.jianshu.com/p/2f14bc570563

?

?

Redis 官方文檔

?

Redis 英文官網(wǎng):https://redis.io/? ?

Redis 中文官網(wǎng):

  • http://www.redis.cn/
  • http://www.redis.net.cn/

?

Redis 使用

  • Redis命令?redis完整的命令列表,以及他們的說明文檔。
  • 管道(Pipelining):學(xué)習(xí)如何一次發(fā)送多個命令,節(jié)省往返時間。
  • Redis 發(fā)布/訂閱(Pub/Sub):redis是一個快速、穩(wěn)定的發(fā)布/訂閱的信息系統(tǒng)。
  • Redis Lua 腳本:Redis 2.6 Lua 腳本相關(guān)文檔。
  • Lua 腳本調(diào)試:Redis 3.2 Lua 腳本調(diào)試相關(guān)文檔。
  • 內(nèi)存優(yōu)化:了解如何使用內(nèi)存和學(xué)習(xí)一些使用技巧。
  • 過期(Expires):Redis允許為每一個key設(shè)置不同的過期時間,當它們到期時將自動從服務(wù)器上刪除。
  • 將Redis當做使用LRU算法的緩存來使用:如何配置并且將Redis當做緩存來使用,通過限制內(nèi)存及自動回收鍵。
  • Redis 事務(wù):將一組命令放在同一個事務(wù)中進行處理。
  • 大量插入數(shù)據(jù):如何在短時間里向Redis寫入大量數(shù)據(jù)。
  • 從文件中批量插入數(shù)據(jù):將文件中的指令批量執(zhí)行。
  • 分區(qū)(Partitioning):如何將你的數(shù)據(jù)分布在多個Redis里面。
  • 分布式鎖(Distributed locks):用Redis實現(xiàn)分布式鎖管理器。
  • key事件通知(Redis keyspace notifications):通過發(fā)布/訂閱獲得key事件的通知(版本2.8或更高)。
  • 創(chuàng)建二級索引(Creating secondary indexes with Redis):使用redis的數(shù)據(jù)結(jié)構(gòu)創(chuàng)建二級索引。

Redis modules API

  • Introduction to Redis modules. A good place to start learing about Redis 4.0 modules programming.
  • Implementing native data types. Modules scan implement new data types (data structures and more) that look like built-in data types. This documentation covers the API to do so.
  • Blocking operations?with modules. This is still an experimental API, but a very powerful one to write commands that can block the client (without blocking Redis) and can execute tasks in other threads.
  • Redis modules API reference. Directly generated from the top comments in the source code inside?src/module.c. Contains many low level details about API usage.

Redis 管理

  • Redis-Cli:學(xué)習(xí)怎么通過命令行使用redis。
  • 配置(Configuration):怎么配置 redis。
  • 復(fù)制(Replication):你需要知道怎么設(shè)置主從復(fù)制。
  • 持久化(Persistence):了解如何配置redis的持久化。
  • Redis 管理(Redis Administration):學(xué)習(xí)redis管理方面的知識。
  • 安全性(Security):概述Redis的安全。
  • 加密(encryption):如何加密redis的客戶端與服務(wù)端通信。。
  • 信號處理(Signals Handling):如何處理Redis信號。
  • 連接處理(Connections Handling):如何處理Redis客戶端連接。
  • 高可用性(High Availability):Redis Sentinel是Redis官方的高可用性解決方案。目前工作進展情況(beta階段,積極發(fā)展),已經(jīng)可用。
  • 延遲監(jiān)控(Latency monitoring):redis集成的延遲監(jiān)控和報告功能對于為低延遲應(yīng)用場景優(yōu)化redis很有幫助。
  • 基準(Benchmarks):看看Redis在不同平臺上跑得有多快。
  • Redis Releases:Redis的開發(fā)周期和版本編號。

Embedded and IoT

  • Redis on ARM and Raspberry Pi:Starting with Redis 4.0 ARM and the Raspberry Pi are officially supported platforms. This page contains general information and benchmarks.

Redis Cluster

  • Redis 集群教程:入門級的Redis集群使用指南。
  • Redis 集群規(guī)范:進階版的Redis集群使用規(guī)范。

教程 & FAQ

  • FAQ:關(guān)于Redis的一些常見問題。
  • 數(shù)據(jù)類型(Data types):Redis支持不同類型值的摘要。
  • Redis Stream(streams)介紹:詳細介紹Redis5的一種新的數(shù)據(jù)類型stream。
  • 15分鐘快速了解Redis的數(shù)據(jù)結(jié)構(gòu)
  • 用PHP+Redis編寫一個簡單的Twitter
  • 用Redis實現(xiàn)自動完成

?

?

前言

?

? ? ? ? NoSQL數(shù)據(jù)庫?(?非關(guān)系型數(shù)據(jù)庫?) 一度成為高并發(fā)、海量數(shù)據(jù)存儲解決方案的代名詞,與之相應(yīng)的產(chǎn)品也呈現(xiàn)出雨后春筍般的生機。然而在眾多產(chǎn)品中能夠脫穎而出的卻屈指可數(shù),如 Redis、MongoDB、BerkeleyDB 和 CouchDB 等。由于每種產(chǎn)品所擁有的特征不同,因此它們的應(yīng)用場景也存在著一定的差異,下面僅給出簡單的說明:

  • 1). BerkeleyDB 是一種極為流行的開源嵌入式數(shù)據(jù)庫,在更多情況下可用于存儲引擎,比如 BerkeleyDB 在被 Oracle 收購之前曾作為 MySQL 的存儲引擎,由此可以預(yù)見,該產(chǎn)品擁有極好的并發(fā)伸縮性,支持事務(wù)及嵌套事務(wù),海量數(shù)據(jù)存儲等重要特征,在用于存儲實時數(shù)據(jù)方面具有極高的可用價值。然而需要指出的是,該產(chǎn)品的 Licence 為 GPL,這就意味著它并不是在所有情況下都是免費使用的。
  • 2). 對 MongoDB 的定義為 Oriented-Document 數(shù)據(jù)庫服務(wù)器,和 BerkeleyDB 不同的是該數(shù)據(jù)庫可以像其他關(guān)系型數(shù)據(jù)庫服務(wù)器那樣獨立的運行并提供相關(guān)的數(shù)據(jù)服務(wù)。從該產(chǎn)品的官方文檔中我們可以獲悉,MongoDB 主要適用于高并發(fā)的論壇或博客網(wǎng)站,這些網(wǎng)站具有的主要特征是并發(fā)訪問量高、多讀少寫、數(shù)據(jù)量大、邏輯關(guān)系簡單,以及文檔數(shù)據(jù)作為主要數(shù)據(jù)源等。和 BerkeleyDB 一樣,該產(chǎn)品的 License 同為 GPL。
  • 3). Redis,典型的 NoSQL 數(shù)據(jù)庫服務(wù)器,和 BerkeleyDB 相比,它可以作為服務(wù)程序獨立運行于自己的服務(wù)器主機。在很多時候,人們只是將 Redi s視為 Key/Value 數(shù)據(jù)庫服務(wù)器,然而事實并非如此,在目前的版本中,Redis 除了Key/Value之外還支持 List、Hash、Set 和 Ordered Set 等數(shù)據(jù)結(jié)構(gòu),因此它的用途也更為寬泛。對于此種誤解,Redis官網(wǎng)也進行了相應(yīng)的澄清。和以上兩種產(chǎn)品不同的是,Redis 的 License 是 Apache License,就目前而言,它是完全免費。
  • 4). memcached,數(shù)據(jù)緩存服務(wù)器。為什么在這里要給出該產(chǎn)品的解釋呢?很簡單,因為筆者認為它在使用方式上和 Redis 最為相似。畢竟這是一篇關(guān)于 Redis 的技術(shù)系列博客,有鑒于此,我們將簡要的對比一下這兩個產(chǎn)品。首先說一下它們之間的最大區(qū)別,memcached 只是提供了數(shù)據(jù)緩存服務(wù),一旦服務(wù)器宕機,之前在內(nèi)存中緩存的數(shù)據(jù)也將全部消失,因此可以看出 memcached 沒有提供任何形式的數(shù)據(jù)持久化功能,而 Redis 則提供了這樣的功能。再有就是 Redis 提供了更為豐富的數(shù)據(jù)存儲結(jié)構(gòu),如 Hash 和 Set。至于它們的相同點,主要有兩個,一是完全免費,再有就是它們的提供的命令形式極為接近。

?

?

redis 概述

?

redis 是 Remote Dictionary Server 的簡稱,是一個由意大利人Salvatore Sanfilippo開發(fā)的key-value存儲系統(tǒng),具有極高的讀寫性能,讀的速度可達110000次/s,寫的速度可達81000次/s 。與Redis類似的產(chǎn)品還有memcache,同樣是一個基于內(nèi)存的key-value存儲系統(tǒng),但是由于memcache數(shù)據(jù)結(jié)構(gòu)單一,數(shù)據(jù)安全性低下等原因,大有被Redis取而代之的趨勢。redis是一個開源的、使用C語言編寫的、支持網(wǎng)絡(luò)交互的、可基于內(nèi)存也可持久化的Key-Value數(shù)據(jù)庫。目前,Vmware在資助著redis項目的開發(fā)和維護。

redis 的作者何許人也?開門見山,先看照片:

是不是出乎了你的意料,嗯,高手總會有些地方與眾不同的。這位便是 redis 的作者,他叫 Salvatore Sanfilippo,來自意大利的西西里島,現(xiàn)在居住在卡塔尼亞。目前供職于 Pivotal 公司。他使用的網(wǎng)名是 antirez,如果你有興趣,可以去他的博客逛逛,地址是 antirez.com,當然也可以去 follow 他的 github,地址:http://github.com/antirez

Redis 與其他 key - value 緩存產(chǎn)品相比,有以下特點:

  • 1、Redis 支持數(shù)據(jù)的持久化,周期性的把更新的數(shù)據(jù)寫入磁盤或者把修改操作寫入追加的記錄文件,重啟的時候可以再次加載進行使用。
  • 2、Redis 不僅僅支持簡單的 key-value 類型的數(shù)據(jù),同時還提供 list,set,zset,hash 等數(shù)據(jù)結(jié)構(gòu)的存儲。
  • 3、Redis 支持數(shù)據(jù)的備份,即 master-slave 模式的數(shù)據(jù)備份。
  • 4、Redis 的所有操作都是原子性的,同時 Redis 還支持對幾個操作合并后的原子性執(zhí)行。
  • 5、Redis 還支持 publish / subscribe ( 發(fā)布/訂閱?)通知 key 過期 等高級特性。

互聯(lián)網(wǎng)發(fā)展到現(xiàn)在,僅靠傳統(tǒng)的關(guān)系型數(shù)據(jù)庫已經(jīng)遠不能應(yīng)對各種變態(tài)的需求,一個大型的互聯(lián)網(wǎng)應(yīng)用往往需要各類數(shù)據(jù)庫相互合作,才能達到高可用、高性能的標準。

比如,使用 mysql/oracle/DB2 管理核心數(shù)據(jù),使用 Memcache/Redis 管理熱點數(shù)據(jù),使用 Hbase/Hadoop 管理海量數(shù)據(jù)……總之,在合適的地方選擇合適的數(shù)據(jù)庫。

?

誰在使用 redis

Blizzard、digg、stackoverflow、github、flickr …

?

?

Redis 管理工具

?

Redis 安裝好之后,進入安裝目錄的 src 文件夾,會發(fā)現(xiàn)里面有6個可執(zhí)行文件:

它們對應(yīng)著6個管理 Redis 的工具:

?

redis-server

該工具用于啟動Redis服務(wù)器,處理與客戶端的對話,一個服務(wù)器可以與多個客戶端連接。
在終端輸入該命令,如果啟動成功,就會看到Redis那幅標志性的圖片:

?

Redis 提示說:

Warning: no config file specified, using the defaultconfig. In order to specify a config file use Redis-server /path/to/Redis.conf

沒有指定配置文件,當前使用的是默認配置。假如想使用位于路徑 "/winner/setting/"?下的 Redis.conf 作為配置文件,使用命令?Redis-server /winner/setting/Redis.conf?即可。

最后一行,Redis 提示服務(wù)器已經(jīng)準備好在端口 6379 接受客戶端的連接,6379 是 Redis 的默認端口

至于為什么使用 6379 當做默認端口還有段傳聞。寫 Redis 的大神 Antirez 是意大利人,在意大利有個歌女名為 Alessia Merz,長期以來被 Antirez 及其朋友當作愚蠢的代名詞,MERZ 對應(yīng)的手機按鍵就是 6379,后來 Antirez 開發(fā) Redis 時就使用這四個數(shù)字來當做默認端口。

?

redis-cli

該工具用于啟動 Redis 客戶端,發(fā)起與服務(wù)器的對話。可以使用參數(shù)來指定目標服務(wù)器的詳細信息,例如:

  • -h? 指定服務(wù)器IP地址
  • -p??指定服務(wù)器端口
  • -a??指定登錄密碼等

示例:Redis-cli -h?192.168.1.2 -p?6300

如不指定任何參數(shù),則默認連接 127.0.0.1 的 6379 端口。與 Redis 服務(wù)器取得連接后,就通過指令進行數(shù)據(jù)的存取、更改等操作了:

?

redis-benchmark

?

該工具用于測試 Redis 在本機的性能,類似于魯大師的跑分程序。運行之后會得到一組數(shù)據(jù)存取效率的報告:

?

redis-check-aof

Redis 雖然是基于內(nèi)存的數(shù)據(jù)庫,但是會創(chuàng)建并更新在硬盤上的備份,備份有兩種方式,一個是把內(nèi)存的數(shù)據(jù)導(dǎo)入dump.rdb文件中,另一中就是將每個執(zhí)行過的命令記錄到 AOF 文件中。該工具用于檢查、修復(fù) AOF 文件。

?

redis-check-dump

與 Redis-check-aof 類似,該工具用于檢查 dump.rdb 文件。

?

redis-sentinel

該工具提供 Redis 實例的監(jiān)控管理、通知和實例失效備援服務(wù),是 Redis 集群的管理工具,監(jiān)控各個其他節(jié)點的工作情況并且進行故障恢復(fù),來提高集群的高可用性。

?

Redis GUI 工具

Redis Desktop Manager:https://redisdesktop.com/download

?

?

學(xué)會安裝 redis

?

下載地址http://redis.io/download,下載最新 Redis 版本。

$ wget http://download.redis.io/releases/redis-2.8.17.tar.gz $ tar xzf redis-2.8.17.tar.gz $ cd redis-2.8.17 $ sudo make && sudo make install

安裝非常簡單。make 成功后會在 src 文件夾下產(chǎn)生一些二進制可執(zhí)行文件,包括 redis-server、redis-cli 等等:

$ find . -type f -executable ./redis-benchmark //用于進行redis性能測試的工具 ./redis-check-dump //用于修復(fù)出問題的dump.rdb文件 ./redis-cli //redis的客戶端 ./redis-server //redis的服務(wù)端 ./redis-check-aof //用于修復(fù)出問題的AOF文件 ./redis-sentinel //用于集群管理

??

啟動 redis

下面啟動 redis 服務(wù)

$?cd?src?? $?./redis-server??

注意:這種方式啟動 redis 使用的是默認配置。也可以通過啟動參數(shù)告訴 redis 使用指定配置文件使用下面命令啟動。

$?cd?src?? $?./redis-server?redis.conf??

redis.conf 是一個默認的配置文件。我們可以根據(jù)需要使用自己的配置文件。啟動 redis 服務(wù)進程后,就可以使用測試客戶端程序 redis-cli 和 redis 服務(wù)交互了。 比如:

$?cd?src?? $?./redis-cli?? redis>?set?foo?bar?? OK?? redis>?get?foo?? "bar" ?

?

Ubuntu 系統(tǒng)安裝 Redi?

$sudo?apt-get?update?? $sudo?apt-get?install?redis-server??

啟動 Redis:redis-server

查看 redis 是否啟動:redis-cli? ,執(zhí)行完這個命令后,會打開:redis?127.0.0.1:6379>??

127.0.0.1 是本機 IP ,6379 是 redis 服務(wù)端口。現(xiàn)在我們輸入 PING 命令:redis?127.0.0.1:6379>?ping??

以上說明我們已經(jīng)成功安裝了 redis。

?

檢查 Redis 服務(wù)器程序

# 檢查Redis服務(wù)器系統(tǒng)進程 ~ ps -aux|grep redis redis 4162 0.1 0.0 10676 1420 ? Ss 23:24 0:00 /usr/bin/redis-server /etc/redis/redis.conf conan 4172 0.0 0.0 11064 924 pts/0 S+ 23:26 0:00 grep --color=auto redis# 通過啟動命令檢查Redis服務(wù)器狀態(tài) ~ netstat -nlt|grep 6379 tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN# 通過啟動命令檢查Redis服務(wù)器狀態(tài) ~ sudo /etc/init.d/redis-server status redis-server is running

?

Windows 安裝 Redis

redis-windows安裝+配置介紹:https://www.cnblogs.com/sxdcgaq8080/p/7204878.html

win7x64 下的 redis 安裝與使用:https://www.cnblogs.com/koal/p/5484916.html

在 Windows 系統(tǒng)上安裝 Redis 數(shù)據(jù)庫是件非常簡單的事情,下載可執(zhí)行安裝文件(exe),雙擊安裝即可。下載地址:https://github.com/rgl/redis/downloads

  • Redis 服務(wù)器運行命令:Redis 安裝目錄 /redis-server.exe
  • Redis 客戶端運行命令:Redis 安裝目錄 /redis-cli.exe

?

?

Redis 命令總結(jié) 和 Redis 客戶端

?

概述

Redis 在設(shè)計之初就被定義為長時間不間斷運行的服務(wù)進程,因此大多數(shù)系統(tǒng)配置參數(shù)都可以在不重新啟動進程的情況下立即生效。即便是將當前的持久化模式從AOF切換到RDB也無需重啟。在 Redis 中,提供了一組和服務(wù)器管理相關(guān)的命令,其中就包含和參數(shù)設(shè)置有關(guān)的 CONFIG SET/GET command。

?

命令行 訪問 Redis

安裝 Redis 服務(wù)器,會自動地一起安裝 Redis 命令行客戶端程序。在本機輸入redis-cli 命令就可以啟動,客戶端程序訪問 Redis 服務(wù)器。

~ redis-cli redis 127.0.0.1:6379># 命令行的幫助 redis 127.0.0.1:6379> help redis-cli 2.2.12 Type: "help @" to get a list of commands in "help " for help on "help " to get a list of possible help topics"quit" to exit# 查看所有的key列表 redis 127.0.0.1:6379> keys * (empty list or set)

?

修改 Redis 的配置

使用 Redis 的訪問賬號

默認情況下,訪問 Redis 服務(wù)器是不需要密碼的,為了增加安全性我們需要設(shè)置 Redis 服務(wù)器的訪問密碼。設(shè)置訪問密碼為 redisredis。用 vi 打開 Redis 服務(wù)器的配置文件 redis.conf

~ sudo vi /etc/redis/redis.conf#取消注釋requirepass requirepass redisredis

?

讓 Redis 服務(wù)器被遠程訪問

默認情況下,Redis服務(wù)器不允許遠程訪問,只允許本機訪問,所以我們需要設(shè)置打開遠程訪問的功能。用 vi 打開 Redis 服務(wù)器的配置文件 redis.conf

~ sudo vi /etc/redis/redis.conf#注釋bind #bind 127.0.0.1

修改后,重啟 Redis 服務(wù)器。

~ sudo /etc/init.d/redis-server restart Stopping redis-server: redis-server. Starting redis-server: redis-server.

未使用密碼登陸 Redis 服務(wù)器

~ redis-cliredis 127.0.0.1:6379> keys * (error) ERR operation not permitted 發(fā)現(xiàn)可以登陸,但無法執(zhí)行命令了。

登陸 Redis 服務(wù)器,輸入密碼

~ redis-cli -a redisredisredis 127.0.0.1:6379> keys * 1) "key2" 2) "key3" 3) "key4" 登陸后,一切正常。

檢查 Redis 服務(wù)器占用端口

~ netstat -nlt|grep 6379 tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN 我們看到從之間的網(wǎng)絡(luò)監(jiān)聽從 127.0.0.1:3306 變成 0 0.0.0.0:3306,表示Redis已經(jīng)允許遠程登陸訪問。

我們在遠程的另一臺 Linux 訪問 Redis 服務(wù)器

~ redis-cli -a redisredis -h 192.168.1.199redis 192.168.1.199:6379> keys * 1) "key2" 2) "key3" 3) "key4" 遠程訪問正常。

?

創(chuàng)建Redis配置目錄 /etc/redismkdir /etc/redis 拷貝配置文件:cp /opt/redis/redis-3.2.4/redis.conf/ /etc/redis通過指定配置文件啟動;redis-server /etc/redis/redis.conf通過命令redis-server 啟動,可在命令后加上`&`號使redis以后臺程序方式運行;redis-server & 客戶端登陸 redis-cli 關(guān)閉Redis服務(wù) redis-cli 進入 Redis ,然后輸入 shutdown 即可。修改redis.conf(/etc/redis下)#打開后臺運行選項 daemonize yes #設(shè)置日志文件路徑 logfile "/var/log/redis/redis.log"

?

管理 命令 總結(jié)

(1)ping: 測定連接是否存活 (2)echo: 在命令行打印一些內(nèi)容 (3)select:選擇數(shù)據(jù)庫 (4)quit: 退出連接 (5)dbsize:返回當前數(shù)據(jù)庫中key的數(shù)目 (6)info: 獲取服務(wù)器的信息和統(tǒng)計 (7)monitor:實時轉(zhuǎn)儲收到的請求 (8)config get 配置項 : 獲取服務(wù)器配置的信息config set 配置項 值 : 設(shè)置配置項信息 (9)flushdb: 刪除當前選擇數(shù)據(jù)庫中所有的key (10)flushall:刪除所有數(shù)據(jù)庫中的所有的key (11)time: 顯示服務(wù)器時間,時間戳(秒),微秒數(shù) (12)bgrewriteaof:后臺保存rdb快照 (13)bgsave: 后臺保存rdb快照 (14)save: 保存rdb快照 (15)lastsave: 上次保存時間 (16)shutdown [save/nosave]注意:如果不小心運行了flushall,立即shutdown nosave,關(guān)閉服務(wù)器,然后手工編輯aof文件,去掉文件中的flushall相關(guān)行,然后開啟服務(wù)器,就可以倒回原來是數(shù)據(jù)。如果 flushall 之后,系統(tǒng)恰好 bgwriteaof 了,那么 aof 就清空了,數(shù)據(jù)丟失。 (17)showlog:顯示慢查詢問:多慢才叫慢?答:由slowlog-log-slower-than 10000,來指定(單位為微秒)問:服務(wù)器存儲多少條慢查詢記錄答:由slowlog-max-len 128,來做限制  CONFIG GET parameter 主要用于讀取服務(wù)器的運行時參數(shù),但是并不是所有的配置參數(shù)都可以通過該命令進行讀取。其中該命令的參數(shù)接受glob風(fēng)格的模式匹配規(guī)則,因此如果參數(shù)中包含模式元字符,那么所有匹配的參數(shù)都將以key/value方式被列出。如果參數(shù)是*,那么該命令支持的所有參數(shù)都將被列出。最后需要指出的是,和redis.conf中不同的是,在命令中不能使用數(shù)量縮寫格式,如GB、KB等,只能使用表示字節(jié)數(shù)量的整數(shù)值。 CONFIG SET parameter value 該命令用于重新配置Redis服務(wù)器的運行時參數(shù),在設(shè)置成功之后無需重啟便可生效。然而并非所有的參數(shù)都可以通過該命令進行動態(tài)設(shè)置,如果需要獲悉該命令支持哪些參數(shù),可以查看CONFIG GET * 命令的執(zhí)行結(jié)果。如果想在一個命令中設(shè)置多個同類型參數(shù),如redis.conf配置文件中的save參數(shù):save 900 1/save 300 10。在該命令中我們可以將多個key/value用雙引號括起,并用空格符隔開,如:config set save "900 1 300 10"。 返回值中 OK表示設(shè)置成功,否則返回相關(guān)的錯誤信息。 CONFIG RESETSTAT Reset INFO命令給出的統(tǒng)計數(shù)字。 始終返回OK。 DBSIZE 返回當前打開的數(shù)據(jù)庫中Keys的數(shù)量。 Key的數(shù)量。 FLUSHALL 清空當前服務(wù)器管理的數(shù)據(jù)庫中的所有Keys,不僅限于當前打開的數(shù)據(jù)庫。 FLUSHDB 清空當前數(shù)據(jù)庫中的所有Keys。 INFO 獲取和服務(wù)器運行狀況相關(guān)的一些列統(tǒng)計數(shù)字。 SAVE 設(shè)置RDB持久化模式的保存策略。 SHUTDOWN 停止所有的客戶端,同時以阻塞的方式執(zhí)行內(nèi)存數(shù)據(jù)持久化。如果AOF模式被啟用,則將緩存中的數(shù)據(jù)flush到AOF文件。退出服務(wù)器。 SLAVEOF host port 該命令用于修改SLAVE服務(wù)器的復(fù)制設(shè)置。如果一個Redis服務(wù)器已經(jīng)處于SLAVE狀態(tài),SLAVEOF NO ONE命令將關(guān)閉當前服務(wù)器的被復(fù)制狀態(tài),與此同時將該服務(wù)器切換到MASTER狀態(tài)。該命令的參數(shù)將指定MASTER服務(wù)器的監(jiān)聽IP和端口。還有一種情況是,當前服務(wù)器已經(jīng)是另外一臺MASTER的SLAVE了,在執(zhí)行該命令后,當前服務(wù)器將終止和之前MASTER之間的復(fù)制關(guān)系,而將成為新MASTER的SLAVE,之前MASTER中的數(shù)據(jù)也將被清空,改為新MASTER中的數(shù)據(jù)。然而如果在當前SLAVE服務(wù)器上執(zhí)行的是SLAVEOF NO ONE命令,那么該服務(wù)器只是中斷與當前MASTER的復(fù)制關(guān)系,并升級為獨立的MASTER,其中的數(shù)據(jù)也不會被清空。 SLOWLOG subcommand [argument] 該命令主要用于讀取執(zhí)行時間較長的命令。其中執(zhí)行時間的評判標準僅為命令本身的執(zhí)行時間,并不包括網(wǎng)絡(luò)交互時間。和該命令相關(guān)的配置參數(shù)主要有兩個,第一個就是執(zhí)行之間的閾值(以微秒為單位),即執(zhí)行時間超過該值的命令都會被存入slowlog隊列,以供該命令讀取。第二個是slowlog隊列的長度,如果當前命令在存入之前,該隊列中的命令已經(jīng)等于該參數(shù),在命令進入之前,需要將隊列中最老的命令移出隊列。這樣可以保證該隊列所占用的內(nèi)存總量保持在一個相對恒定的大小。由于slowlog隊列不會被持久化到磁盤,因此Redis在收集命令時不會對性能產(chǎn)生很大的影響。通常我們可以將參數(shù)"slowlog-log-slower-than"設(shè)置為0,以便收集所有命令的執(zhí)行時間。該命令還包含以下幾個子命令:1). SLOWLOG GET N: 從slowlog隊列中讀取命令信息,N表示最近N條命令的信息。2). SLOWLOG LEN:獲取slowlog隊列的長度。3). SLOWLOG RESET:清空slowlog中的內(nèi)容。最后給出SLOWLOG GET命令返回信息的解釋。redis 127.0.0.1:6379> slowlog get 101) 1) (integer) 5 #唯一表示符,在Redis重啟之前,該值保證唯一。2) (integer) 1330369320 #Unix Timestamp格式表示的命令執(zhí)行時間。3) (integer) 13 #命令執(zhí)行所用的微秒數(shù)。4) 1) "slowlog" #以字符串數(shù)組的格式輸出收集到的命令及其參數(shù)。2) "reset"

?

Redis 客戶端

直接看一個例子:

$ ./redis-cli // 啟動 redis 客戶端 127.0.0.1:6379> set name "roc" // 用set指令來設(shè)置key、value OK 127.0.0.1:6379> get name // 來獲取name的值 "roc" 127.0.0.1:6379> shutdown // 通過客戶端來關(guān)閉redis服務(wù)端 127.0.0.1:6379>

?

一、key pattern 查詢相應(yīng)的 key

(1)keys key_pattern 查詢滿足表達式的所有key。redis 允許模糊查詢key,有3個通配符 *、?、[] (2)randomkey: 返回隨機key   (3)type key: 返回key存儲的類型 (4)exists key: 判斷某個key是否存在 (5)del key: 刪除 key (6)rename key newkey: 改名 (7)renamenx key newkey:如果 newkey 不存在則修改成功 (8)move key 1: 將 key 移動到1數(shù)據(jù)庫 (9)ttl key: 查詢 key 的生命周期(秒) (10)expire key 整數(shù)值: 設(shè)置 key 的生命周期以秒為單位 (11)pexpire key 整數(shù)值:設(shè)置 key 的生命周期以毫秒為單位 (12)pttl key: 查詢 key 的生命周期(毫秒) (13)perisist key: 把指定 key 設(shè)置為永久有效

?

二、字符串類型 的操作

(1)set key value [ex 秒數(shù)] [px 毫秒數(shù)] [nx/xx]  如果ex和px同時寫,則以后面的有效期為準nx:如果key不存在則建立xx:如果key存在則修改其值 (2)get key:取值 (3)mset key1 value1 key2 value2 一次設(shè)置多個值 (4)mget key1 key2 : 一次獲取多個值 (5)setrange key offset value:把字符串的offset偏移字節(jié)改成value如果偏移量 > 字符串長度,該字符自動補0x00 (6)append key value :把value追加到key 的原值上 (7)getrange key start stop:獲取字符串中[start, stop]范圍的值對于字符串的下標,左數(shù)從0開始,右數(shù)從-1開始注意:當start>length,則返回空字符串當stop>=length,則截取至字符串尾如果start所處位置在stop右邊,則返回空字符串 (8)getset key nrevalue: 獲取并返回舊值,在設(shè)置新值 (9)incr key: 自增,返回新值,如果incr一個不是int的value則返回錯誤,incr一個不存在的key,則設(shè)置key為1 (10)incrby key 2: 跳2自增 (11)incrbyfloat by 0.7: 自增浮點數(shù)  (12)setbit key offset value:設(shè)置offset對應(yīng)二進制上的值,返回該位上的舊值注意:如果offset過大,則會在中間填充0offset最大到多少2^32-1,即可推出最大的字符串為512M (13)bitop operation destkey key1 [key2..] 對key1 key2做opecation并將結(jié)果保存在destkey上opecation可以是AND OR NOT XOR (14)strlen key: 取指定key的value值的長度 (15)setex key time value:設(shè)置key對應(yīng)的值value,并設(shè)置有效期為time秒

?

三、鏈表 操作

  Redis 的 list 類型其實就是一個每個子元素都是 string 類型的雙向鏈表,鏈表的最大長度是2^32。list 既可以用做棧,也可以用做隊列。

  list 的 pop 操作還有阻塞版本,主要是為了避免輪詢

  (1)lpush key value:把值插入到鏈表頭部(2)rpush key value:把值插入到鏈表尾部(3)lpop key :返回并刪除鏈表頭部元素(4)rpop key: 返回并刪除鏈表尾部元素(5)lrange key start stop:返回鏈表中[start, stop]中的元素(6)lrem key count value:從鏈表中刪除value值,刪除count的絕對值個value后結(jié)束count > 0 從表頭刪除  count < 0 從表尾刪除  count=0 全部刪除(7)ltrim key start stop:剪切key對應(yīng)的鏈接,切[start, stop]一段并把改制重新賦給key(8)lindex key index:返回index索引上的值(9)llen key:計算鏈表的元素個數(shù)(10)linsert key after|before search value:在key 鏈表中尋找search,并在search值之前|之后插入value(11)rpoplpush source dest:把source 的末尾拿出,放到dest頭部,并返回單元值應(yīng)用場景: task + bak 雙鏈表完成安全隊列業(yè)務(wù)邏輯: rpoplpush task bak接收返回值并做業(yè)務(wù)處理如果成功則rpop bak清除任務(wù),如果不成功,下次從bak表取任務(wù)(12)brpop,blpop key timeout:等待彈出key的尾/頭元素timeout為等待超時時間,如果timeout為0則一直等待下去應(yīng)用場景:長輪詢ajax,在線聊天時能用到

?

四、hashes 類型及操作

? ? ? ? Redis hash 是一個 string 類型的 field 和 value 的映射表,它的添加、刪除操作都是O(1)(平均)。

? ? ? ? hash 特別適用于存儲對象,將一個對象存儲在 hash 類型中會占用更少的內(nèi)存,并且可以方便的存取整個對象。
? ? ? ? 配置: hash_max_zipmap_entries 64? ?# 配置字段最多64個
? ? ? ? ? ? ? ? ? ? hash_max_zipmap_value 512? ?# 配置value最大為512字節(jié)

(1)hset myhash field value: 設(shè)置myhash的field為value (2)hsetnx myhash field value: 不存在的情況下設(shè)置myhash的field為value (3)hmset myhash field1 value1 field2 value2: 同時設(shè)置多個field (4)hget myhash field: 獲取指定的hash field (5)hmget myhash field1 field2:一次獲取多個field (6)hincrby myhash field 5: 指定的hash field加上給定的值 (7)hexists myhash field: 測試指定的field是否存在 (8)hlen myhash: 返回hash的field數(shù)量 (9)hdel myhash field: 刪除指定的field (10)hkeys myhash: 返回hash所有的field (11)hvals myhash: 返回hash所有的value (12)hgetall myhash: 獲取某個hash中全部的field及value 

?

五、集合結(jié)構(gòu)操作

特點:無序性、確定性、唯一性

(1)sadd key value1 value2: 往集合里面添加元素 (2)smembers key: 獲取集合所有的元素 (3)srem key value: 刪除集合某個元素 (4)spop key: 返回并刪除集合中1個隨機元素(可以坐抽獎,不會重復(fù)抽到某人)    (5)srandmember key: 隨機取一個元素 (6)sismember key value: 判斷集合是否有某個值 (7)scard key: 返回集合元素的個數(shù) (8)smove source dest value: 把 source的value移動到dest集合中 (9)sinter key1 key2 key3: 求 key1 key2 key3的交集 (10)sunion key1 key2: 求 key1 key2 的并集 (11)sdiff key1 key2: 求 key1 key2的差集 (12)sinterstore res key1 key2:求 key1 key2的交集并存在res里 

?

六、有序集合

  概念:它是在 set 的基礎(chǔ)上增加了一個順序?qū)傩?#xff0c;這一屬性在添加修改元素的時候可以指定,每次指定后,zset 會自動按新的值調(diào)整順序。可以理解為有兩列的 mysql 表,一列存儲value,一列存儲順序,操作中 key 理解為 zset 的名字。
  和 set 一樣 sorted sets 也是 string 類型元素的集合,不同的是每個元素都會關(guān)聯(lián)一個 double 型的 score。sorted set 的實現(xiàn)是 skip list 和 hash table 的混合體。
  當元素被添加到集合中時,一個元素到score的映射被添加到hash table中,所以給定一個元素獲取score的開銷是O(1)。另一個score到元素的映射被添加的skip list,并按照score排序,所以就可以有序地獲取集合中的元素。添加、刪除操作開銷都是O(logN)和skip list的開銷一致,redis的skip list 實現(xiàn)是雙向鏈表,這樣就可以逆序從尾部去元素。sorted set最經(jīng)常使用方式應(yīng)該就是作為索引來使用,我們可以把要排序的字段作為score存儲,對象的ID當元素存儲。

(1)zadd key score1 value1: 添加元素 (2)zrange key start stop [withscore]:把集合排序后,返回名次[start,stop]的元素 默認是升續(xù)排列 withscores 是把score也打印出來 (3)zrank key member: 查詢 member 的排名(升序0名開始) (4)zrangebyscore key min max [withscores] limit offset N:集合(升序)排序后取score在[min, max]內(nèi)的元素,并跳過offset個,取出N個 (5)zrevrank key member: 查詢member排名(降序 0名開始) (6)zremrangebyscore key min max: 按照score來刪除元素,刪除score在[min, max]之間 (7)zrem key value1 value2: 刪除集合中的元素 (8)zremrangebyrank key start end: 按排名刪除元素,刪除名次在[start, end]之間的 (9)zcard key: 返回集合元素的個數(shù) (10)zcount key min max: 返回[min, max]區(qū)間內(nèi)元素數(shù)量 (11)zinterstore dest numkeys key1[key2..] [WEIGHTS weight1 [weight2...]] [AGGREGATE SUM|MIN|MAX]求key1,key2的交集,key1,key2的權(quán)值分別是weight1,weight2,聚合方法用 sum|min|max聚合結(jié)果 保存子dest集合內(nèi),注意:weights,aggregate如何理解?答:如果有交集,交集元素又有score,score怎么處理?aggregate num->score相加,min最小score,max最大score,另外可以通過weights設(shè)置不同的key的權(quán)重,交集時 score*weight

?

Redis pipeline?詳解

請求應(yīng)答協(xié)議和RTT:

Redis 是一種典型的基于 C/S 模型的 TCP 服務(wù)器。在客戶端與服務(wù)器的通訊過程中,通常都是客戶端率先發(fā)起請求,服務(wù)器在接收到請求后執(zhí)行相應(yīng)的任務(wù),最后再將獲取的數(shù)據(jù)或處理結(jié)果以應(yīng)答的方式發(fā)送給客戶端。在此過程中,客戶端都會以阻塞的方式等待服務(wù)器返回的結(jié)果。見如下命令序列:

Client: INCR XServer: 1Client: INCR XServer: 2Client: INCR XServer: 3Client: INCR XServer: 4

在每一對請求與應(yīng)答的過程中,我們都不得不承受網(wǎng)絡(luò)傳輸所帶來的額外開銷。我們通常將這種開銷稱為RTT(Round Trip Time)。現(xiàn)在我們假設(shè)每一次請求與應(yīng)答的RTT為250毫秒,而我們的服務(wù)器可以在一秒內(nèi)處理100k的數(shù)據(jù),可結(jié)果則是我們的服務(wù)器每秒至多處理4條請求。要想解決這一性能問題,我們該如何進行優(yōu)化呢?

?

管線 (pipelining):

Redis 在很早的版本中就已經(jīng)提供了對命令管線的支持。在給出具體解釋之前,我們先將上面的同步應(yīng)答方式的例子改造為基于命令管線的異步應(yīng)答方式,這樣可以讓大家有一個更好的感性認識。

Client: INCR X Client: INCR X Client: INCR X Client: INCR X Server: 1 Server: 2 Server: 3 Server: 4

從以上示例可以看出,客戶端在發(fā)送命令之后,不用立刻等待來自服務(wù)器的應(yīng)答,而是可以繼續(xù)發(fā)送后面的命令。在命令發(fā)送完畢后,再一次性的讀取之前所有命令的應(yīng)答。這樣便節(jié)省了同步方式中RTT的開銷。
最后需要說明的是,如果Redis服務(wù)器發(fā)現(xiàn)客戶端的請求是基于管線的,那么服務(wù)器端在接受到請求并處理之后,會將每條命令的應(yīng)答數(shù)據(jù)存入隊列,之后再發(fā)送到客戶端。

?

Benchmark:

以下是來自 Redis 官網(wǎng)的測試用例和測試結(jié)果。需要說明的是,該測試是基于loopback(127.0.0.1)的,因此RTT所占用的時間相對較少,如果是基于實際網(wǎng)絡(luò)接口,那么管線機制所帶來的性能提升就更為顯著了。?

下面 Ruby 代碼示例:

require 'rubygems'require 'redis'def bench(descr)start = Time.nowyieldputs "#{descr} #{Time.now-start} seconds"enddef without_pipeliningr = Redis.new10000.times {r.ping}enddef with_pipeliningr = Redis.newr.pipelined {10000.times {r.ping}}endbench("without pipelining") {without_pipelining}bench("with pipelining") {with_pipelining}//without pipelining 1.185238 seconds//with pipelining 0.250783 seconds

?

?

redis 數(shù)據(jù)結(jié)構(gòu) – 簡介

?

redis 是一種高級的 key:value 存儲系統(tǒng),其中 value 支持五種數(shù)據(jù)類型:

  • 1.字符串(strings)
  • 2.字符串列表(lists)
  • 3.字符串集合(sets)
  • 4.有序字符串集合(sorted sets)
  • 5.哈希(hashes)

redis 定義了豐富的原語命令,可以直接與 Redis 服務(wù)器交互。實際應(yīng)用中,我們不太會直接使用這些原語命令,Redis提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang 等客戶端,大多情況下我們是通過各式各樣的客戶端來操作 Redis。但是,任何語言的客戶端實際上都是對 Redis 原語命令的封裝,了解原語命令有助于理解客戶端的設(shè)計原理,知其然,知其所以然。

而關(guān)于 key,有幾個點要提醒大家:

  • 1. key 不要太長,盡量不要超過1024字節(jié),這不僅消耗內(nèi)存,而且會降低查找的效率;
  • 2. key 也不要太短,太短的話,key 的可讀性會降低;
  • 3. 在一個項目中,key 最好使用統(tǒng)一的命名模式,例如 user:10000:passwd

?

Redis 的 Key 操作命令詳解

命令原型 時間復(fù)雜度 返回值 KEYS pattern O(N) 匹配模式的鍵列表。時間復(fù)雜度中的N表示數(shù)據(jù)庫中Key的數(shù)量。獲取所有匹配pattern參數(shù)的Keys。需要說明的是,在我們的正常操作中應(yīng)該盡量避免對該命令的調(diào)用,因為對于大型數(shù)據(jù)庫而言,該命令是非常耗時的,對Redis服務(wù)器的性能打擊也是比較大的。pattern支持glob-style的通配符格式,如*表示任意一個或多個字符,?表示任意字符,[abc]表示方括號中任意一個字母。 DEL key [key ...] O(N) 實際被刪除的Key數(shù)量。時間復(fù)雜度中的N表示刪除的Key數(shù)量。從數(shù)據(jù)庫刪除中參數(shù)中指定的keys,如果指定鍵不存在,則直接忽略。還需要另行指出的是,如果指定的Key關(guān)聯(lián)的數(shù)據(jù)類型不是String類型,而是List、Set、Hashes和Sorted Set等容器類型,該命令刪除每個鍵的時間復(fù)雜度為O(M),其中M表示容器中元素的數(shù)量。而對于String類型的Key,其時間復(fù)雜度為O(1)。 EXISTS key O(1) 1表示存在,0表示不存在。判斷指定鍵是否存在。 MOVE key db O(1) 移動成功返回1,否則0。將當前數(shù)據(jù)庫中指定的鍵Key移動到參數(shù)中指定的數(shù)據(jù)庫中。如果該Key在目標數(shù)據(jù)庫中已經(jīng)存在,或者在當前數(shù)據(jù)庫中并不存在,該命令將不做任何操作并返回0。 RENAME key newkey O(1) 如果newKey已經(jīng)存在,則直接覆蓋。 為指定指定的鍵重新命名,如果參數(shù)中的兩個Keys的命令相同,或者是源Key不存在,該命令都會返回相關(guān)的錯誤信息。RENAMENX key newkey O(1) 1表示修改成功,否則0。如果新值不存在,則將參數(shù)中的原值修改為新值。其它條件和RENAME一致。 PERSIST key O(1) 1表示Key的過期時間被移出,0表示該Key不存在或沒有過期時間。如果Key存在過期時間,該命令會將其過期時間消除,使該Key不再有超時,而是可以持久化存儲。 EXPIRE key seconds O(1) 1表示超時被設(shè)置,0則表示Key不存在,或不能被設(shè)置。該命令為參數(shù)中指定的Key設(shè)定超時的秒數(shù),在超過該時間后,Key被自動的刪除。如果該Key在超時之前被修改,與該鍵關(guān)聯(lián)的超時將被移除。 EXPIREAT key timestamp O(1) 1表示超時被設(shè)置,0則表示Key不存在,或不能被設(shè)置。該命令的邏輯功能和EXPIRE完全相同,唯一的差別是該命令指定的超時時間是絕對時間,而不是相對時間。該時間參數(shù)是Unix timestamp格式的,即從1970年1月1日開始所流經(jīng)的秒數(shù)。 TTL key O(1) 返回所剩描述,如果該鍵不存在或沒有超時設(shè)置,則返回-1。獲取該鍵所剩的超時描述。 RANDOMKEY O(1) 返回的隨機鍵,如果該數(shù)據(jù)庫是空的則返回nil。從當前打開的數(shù)據(jù)庫中隨機的返回一個Key。 TYPE key O(1) 返回的字符串為string、list、set、hash和zset,如果key不存在返回none。獲取與參數(shù)中指定鍵關(guān)聯(lián)值的類型,該命令將以字符串的格式返回。 SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination] O(N+M*log(M)) 這個命令相對來說是比較復(fù)雜的,因此我們這里只是給出最基本的用法,有興趣的網(wǎng)友可以去參考redis的官方文檔。 返回排序后的原始列表。

命令示例

1. KEYS/RENAME/DEL/EXISTS/MOVE/RENAMENX:#在Shell命令行下啟動Redis客戶端工具。/> redis-cli#清空當前選擇的數(shù)據(jù)庫,以便于對后面示例的理解。redis 127.0.0.1:6379> flushdbOK#添加String類型的模擬數(shù)據(jù)。redis 127.0.0.1:6379> set mykey 2OKredis 127.0.0.1:6379> set mykey2 "hello"OK#添加Set類型的模擬數(shù)據(jù)。redis 127.0.0.1:6379> sadd mysetkey 1 2 3(integer) 3#添加Hash類型的模擬數(shù)據(jù)。redis 127.0.0.1:6379> hset mmtest username "stephen"(integer) 1#根據(jù)參數(shù)中的模式,獲取當前數(shù)據(jù)庫中符合該模式的所有key,從輸出可以看出,該命令在執(zhí)行時并不區(qū)分與Key關(guān)聯(lián)的Value類型。redis 127.0.0.1:6379> keys my*1) "mysetkey"2) "mykey"3) "mykey2"#刪除了兩個Keys。redis 127.0.0.1:6379> del mykey mykey2(integer) 2#查看一下剛剛刪除的Key是否還存在,從返回結(jié)果看,mykey確實已經(jīng)刪除了。redis 127.0.0.1:6379> exists mykey(integer) 0#查看一下沒有刪除的Key,以和上面的命令結(jié)果進行比較。redis 127.0.0.1:6379> exists mysetkey(integer) 1#將當前數(shù)據(jù)庫中的mysetkey鍵移入到ID為1的數(shù)據(jù)庫中,從結(jié)果可以看出已經(jīng)移動成功。redis 127.0.0.1:6379> move mysetkey 1(integer) 1#打開ID為1的數(shù)據(jù)庫。redis 127.0.0.1:6379> select 1OK#查看一下剛剛移動過來的Key是否存在,從返回結(jié)果看已經(jīng)存在了。redis 127.0.0.1:6379[1]> exists mysetkey(integer) 1#在重新打開ID為0的缺省數(shù)據(jù)庫。redis 127.0.0.1:6379[1]> select 0OK#查看一下剛剛移走的Key是否已經(jīng)不存在,從返回結(jié)果看已經(jīng)移走。redis 127.0.0.1:6379> exists mysetkey(integer) 0#準備新的測試數(shù)據(jù)。 redis 127.0.0.1:6379> set mykey "hello"OK#將mykey改名為mykey1redis 127.0.0.1:6379> rename mykey mykey1OK#由于mykey已經(jīng)被重新命名,再次獲取將返回nil。redis 127.0.0.1:6379> get mykey(nil)#通過新的鍵名獲取。redis 127.0.0.1:6379> get mykey1"hello"#由于mykey已經(jīng)不存在了,所以返回錯誤信息。redis 127.0.0.1:6379> rename mykey mykey1(error) ERR no such key#為renamenx準備測試keyredis 127.0.0.1:6379> set oldkey "hello"OKredis 127.0.0.1:6379> set newkey "world"OK#由于newkey已經(jīng)存在,因此該命令未能成功執(zhí)行。redis 127.0.0.1:6379> renamenx oldkey newkey(integer) 0#查看newkey的值,發(fā)現(xiàn)它也沒有被renamenx覆蓋。redis 127.0.0.1:6379> get newkey"world"2. PERSIST/EXPIRE/EXPIREAT/TTL: #為后面的示例準備的測試數(shù)據(jù)。redis 127.0.0.1:6379> set mykey "hello"OK#將該鍵的超時設(shè)置為100秒。redis 127.0.0.1:6379> expire mykey 100(integer) 1#通過ttl命令查看一下還剩下多少秒。redis 127.0.0.1:6379> ttl mykey(integer) 97#立刻執(zhí)行persist命令,該存在超時的鍵變成持久化的鍵,即將該Key的超時去掉。redis 127.0.0.1:6379> persist mykey(integer) 1#ttl的返回值告訴我們,該鍵已經(jīng)沒有超時了。redis 127.0.0.1:6379> ttl mykey(integer) -1#為后面的expire命令準備數(shù)據(jù)。redis 127.0.0.1:6379> del mykey(integer) 1redis 127.0.0.1:6379> set mykey "hello"OK#設(shè)置該鍵的超時被100秒。redis 127.0.0.1:6379> expire mykey 100(integer) 1#用ttl命令看一下當前還剩下多少秒,從結(jié)果中可以看出還剩下96秒。redis 127.0.0.1:6379> ttl mykey(integer) 96#重新更新該鍵的超時時間為20秒,從返回值可以看出該命令執(zhí)行成功。redis 127.0.0.1:6379> expire mykey 20(integer) 1#再用ttl確認一下,從結(jié)果中可以看出果然被更新了。redis 127.0.0.1:6379> ttl mykey(integer) 17#立刻更新該鍵的值,以使其超時無效。redis 127.0.0.1:6379> set mykey "world"OK#從ttl的結(jié)果可以看出,在上一條修改該鍵的命令執(zhí)行后,該鍵的超時也無效了。redis 127.0.0.1:6379> ttl mykey(integer) -13. TYPE/RANDOMKEY/SORT:#由于mm鍵在數(shù)據(jù)庫中不存在,因此該命令返回none。redis 127.0.0.1:6379> type mmnone#mykey的值是字符串類型,因此返回string。redis 127.0.0.1:6379> type mykeystring#準備一個值是set類型的鍵。redis 127.0.0.1:6379> sadd mysetkey 1 2(integer) 2#mysetkey的鍵是set,因此返回字符串set。redis 127.0.0.1:6379> type mysetkeyset#返回數(shù)據(jù)庫中的任意鍵。redis 127.0.0.1:6379> randomkey"oldkey"#清空當前打開的數(shù)據(jù)庫。redis 127.0.0.1:6379> flushdbOK#由于沒有數(shù)據(jù)了,因此返回nil。redis 127.0.0.1:6379> randomkey(nil)

?

redis 數(shù)據(jù)結(jié)構(gòu) ---?strings

有人說,如果只使用 redis 中的字符串類型,且不使用 redis 的持久化功能,那么,redis 就和 memcache 非常非常的像了。這說明 strings 類型是一個很基礎(chǔ)的數(shù)據(jù)類型,也是任何存儲系統(tǒng)都必備的數(shù)據(jù)類型。 字符串類型是 Redis 中最為基礎(chǔ)的數(shù)據(jù)存儲類型,它在 Redis 中是二進制安全的,這便意味著該類型可以接受任何格式的數(shù)據(jù),如JPEG圖像數(shù)據(jù)或Json對象描述信息等。在 Redis 中字符串類型的 Value 最多可以容納的數(shù)據(jù)長度是512M。
看一個最簡單的例子:

set mystr "hello world!" //設(shè)置字符串類型 get mystr //讀取字符串類型

字符串類型的用法就是這么簡單,因為是二進制安全的,所以你完全可以把一個圖片文件的內(nèi)容作為字符串來存儲。
另外,我們還可以通過字符串類型進行數(shù)值操作:

127.0.0.1:6379> set mynum "2" OK 127.0.0.1:6379> get mynum "2" 127.0.0.1:6379> incr mynum (integer) 3 127.0.0.1:6379> get mynum "3"

看,在遇到數(shù)值操作時,redis會將字符串類型轉(zhuǎn)換成數(shù)值。由于INCR等指令本身就具有原子操作的特性,所以我們完全可以利用redis的INCR、INCRBY、DECR、DECRBY等指令來實現(xiàn)原子計數(shù)的效果,假如,在某種場景下有3個客戶端同時讀取了mynum的值(值為2),然后對其同時進行了加1的操作,那么,最后mynum的值一定是5。不少網(wǎng)站都利用redis的這個特性來實現(xiàn)業(yè)務(wù)上的統(tǒng)計計數(shù)需求。

命令原型 時間復(fù)雜度 返回值 APPEND key value O(1) 追加后Value的長度。如果該Key已經(jīng)存在,APPEND命令將參數(shù)Value的數(shù)據(jù)追加到已存在Value的末尾。如果該Key不存在,APPEND命令將會創(chuàng)建一個新的Key/Value。DECR key O(1) 遞減后的Value值。將指定Key的Value原子性的遞減1。如果該Key不存在,其初始值為0,在decr之后其值為-1。如果Value的值不能轉(zhuǎn)換為整型值,如Hello,該操作將執(zhí)行失敗并返回相應(yīng)的錯誤信息。注意:該操作的取值范圍是64位有符號整型。INCR key O(1) 遞增后的Value值。 將指定Key的Value原子性的遞增1。如果該Key不存在,其初始值為0,在incr之后其值為1。如果Value的值不能轉(zhuǎn)換為整型值,如Hello,該操作將執(zhí)行失敗并返回相應(yīng)的錯誤信息。注意:該操作的取值范圍是64位有符號整型。 DECRBY key decrement O(1) 減少后的Value值。將指定Key的Value原子性的減少decrement。如果該Key不存在,其初始值為0,在decrby之后其值為-decrement。如果Value的值不能轉(zhuǎn)換為整型值,如Hello,該操作將執(zhí)行失敗并返回相應(yīng)的錯誤信息。注意:該操作的取值范圍是64位有符號整型。 INCRBY key increment O(1) 增加后的Value值。將指定Key的Value原子性的增加increment。如果該Key不存在,其初始值為0,在incrby之后其值為increment。如果Value的值不能轉(zhuǎn)換為整型值,如Hello,該操作將執(zhí)行失敗并返回相應(yīng)的錯誤信息。注意:該操作的取值范圍是64位有符號整型。 GET key O(1) 與該Key相關(guān)的Value,如果該Key不存在,返回nil。獲取指定Key的Value。如果與該Key關(guān)聯(lián)的Value不是string類型,Redis將返回錯誤信息,因為GET命令只能用于獲取string Value。 SET key value O(1) 總是返回"OK"。設(shè)定該Key持有指定的字符串Value,如果該Key已經(jīng)存在,則覆蓋其原有值。 GETSET key value O(1) 返回該Key的原有值,如果該Key之前并不存在,則返回nil。原子性的設(shè)置該Key為指定的Value,同時返回該Key的原有值。和GET命令一樣,該命令也只能處理string Value,否則Redis將給出相關(guān)的錯誤信息。 STRLEN key O(1) 返回指定Key的Value字符長度,如果該Key不存在,返回0。返回指定Key的字符值長度,如果Value不是string類型,Redis將執(zhí)行失敗并給出相關(guān)的錯誤信息。 SETEX key seconds value O(1) 原子性完成兩個操作,一是設(shè)置該Key的值為指定字符串,同時設(shè)置該Key在Redis服務(wù)器中的存活時間(秒數(shù))。該命令主要應(yīng)用于Redis被當做Cache服務(wù)器使用時。 SETNX key value O(1) 1表示設(shè)置成功,否則0。如果指定的Key不存在,則設(shè)定該Key持有指定字符串Value,此時其效果等價于SET命令。相反,如果該Key已經(jīng)存在,該命令將不做任何操作并返回。 SETRANGE key offset value O(1) 修改后的字符串Value長度。替換指定Key的部分字符串值。從offset開始,替換的長度為該命令第三個參數(shù)value的字符串長度,其中如果offset的值大于該Key的原有值Value的字符串長度,Redis將會在Value的后面補齊(offset - strlen(value))數(shù)量的0x00,之后再追加新值。如果該鍵不存在,該命令會將其原值的長度假設(shè)為0,并在其后添補offset個0x00后再追加新值。鑒于字符串Value的最大長度為512M,因此offset的最大值為536870911。最后需要注意的是,如果該命令在執(zhí)行時致使指定Key的原有值長度增加,這將會導(dǎo)致Redis重新分配足夠的內(nèi)存以容納替換后的全部字符串,因此就會帶來一定的性能折損。 GETRANGE key start end O(1) 子字符串 如果截取的字符串長度很短,我們可以該命令的時間復(fù)雜度視為O(1),否則就是O(N),這里N表示截取的子字符串長度。該命令在截取子字符串時,將以閉區(qū)間的方式同時包含start(0表示第一個字符)和end所在的字符,如果end值超過Value的字符長度,該命令將只是截取從start開始之后所有的字符數(shù)據(jù)。 SETBIT key offset value O(1) 在指定Offset上的BIT原有值。設(shè)置在指定Offset上BIT的值,該值只能為1或0,在設(shè)定后該命令返回該Offset上原有的BIT值。如果指定Key不存在,該命令將創(chuàng)建一個新值,并在指定的Offset上設(shè)定參數(shù)中的BIT值。如果Offset大于Value的字符長度,Redis將拉長Value值并在指定Offset上設(shè)置參數(shù)中的BIT值,中間添加的BIT值為0。最后需要說明的是Offset值必須大于0。 GETBIT key offset O(1) 在指定Offset上的BIT值。返回在指定Offset上BIT的值,0或1。如果Offset超過string value的長度,該命令將返回0,所以對于空字符串始終返回0。 MGET key [key ...] O(N) 返回一組指定Keys的Values的列表。N表示獲取Key的數(shù)量。返回所有指定Keys的Values,如果其中某個Key不存在,或者其值不為string類型,該Key的Value將返回nil。MSET key value [key value ...] O(N) 該命令不會失敗,始終返回OK。N表示指定Key的數(shù)量。該命令原子性的完成參數(shù)中所有key/value的設(shè)置操作,其具體行為可以看成是多次迭代執(zhí)行SET命令。 MSETNX key value [key value ...] O(N) 1表示所有Keys都設(shè)置成功,0則表示沒有任何Key被修改。N表示指定Key的數(shù)量。該命令原子性的完成參數(shù)中所有key/value的設(shè)置操作,其具體行為可以看成是多次迭代執(zhí)行SETNX命令。然而這里需要明確說明的是,如果在這一批Keys中有任意一個Key已經(jīng)存在了,那么該操作將全部回滾,即所有的修改都不會生效。

?

命令示例:

1. SET / GET / APPEND / STRLEN :

/> redis-cli #執(zhí)行Redis客戶端工具。 redis 127.0.0.1:6379> exists mykey #判斷該鍵是否存在,存在返回1,否則返回0。 (integer) 0 redis 127.0.0.1:6379> append mykey "hello" #該鍵并不存在,因此append命令返回當前Value的長度。 (integer) 5 redis 127.0.0.1:6379> append mykey " world" #該鍵已經(jīng)存在,因此返回追加后Value的長度。 (integer) 11 redis 127.0.0.1:6379> get mykey #通過get命令獲取該鍵,以判斷append的結(jié)果。 "hello world" redis 127.0.0.1:6379> set mykey "this is a test" #通過set命令為鍵設(shè)置新值,并覆蓋原有值。 OK redis 127.0.0.1:6379> get mykey "this is a test" redis 127.0.0.1:6379> strlen mykey #獲取指定Key的字符長度,等效于C庫中strlen函數(shù)。 (integer) 14

?

2. INCR / DECR / INCRBY / DECRBY :

redis 127.0.0.1:6379> set mykey 20 #設(shè)置Key的值為20 OK redis 127.0.0.1:6379> incr mykey #該Key的值遞增1 (integer) 21 redis 127.0.0.1:6379> decr mykey #該Key的值遞減1 (integer) 20 redis 127.0.0.1:6379> del mykey #刪除已有鍵。 (integer) 1 redis 127.0.0.1:6379> decr mykey #對空值執(zhí)行遞減操作,其原值被設(shè)定為0,遞減后的值為-1 (integer) -1 redis 127.0.0.1:6379> del mykey (integer) 1 redis 127.0.0.1:6379> incr mykey #對空值執(zhí)行遞增操作,其原值被設(shè)定為0,遞增后的值為1 (integer) 1 redis 127.0.0.1:6379> set mykey hello #將該鍵的Value設(shè)置為不能轉(zhuǎn)換為整型的普通字符串。 OK redis 127.0.0.1:6379> incr mykey #在該鍵上再次執(zhí)行遞增操作時,Redis將報告錯誤信息。 (error) ERR value is not an integer or out of range redis 127.0.0.1:6379> set mykey 10 OK redis 127.0.0.1:6379> decrby mykey 5 (integer) 5 redis 127.0.0.1:6379> incrby mykey 10 (integer) 15

?

3. GETSET

redis 127.0.0.1:6379> incr mycounter #將計數(shù)器的值原子性的遞增1 (integer) 1 #在獲取計數(shù)器原有值的同時,并將其設(shè)置為新值,這兩個操作原子性的同時完成。 redis 127.0.0.1:6379> getset mycounter 0 "1" redis 127.0.0.1:6379> get mycounter #查看設(shè)置后的結(jié)果。 "0"

?

4. SETEX:

redis 127.0.0.1:6379> setex mykey 10 "hello" #設(shè)置指定Key的過期時間為10秒。 OK #通過ttl命令查看一下指定Key的剩余存活時間(秒數(shù)),0表示已經(jīng)過期,-1表示永不過期。 redis 127.0.0.1:6379> ttl mykey (integer) 4 redis 127.0.0.1:6379> get mykey #在該鍵的存活期內(nèi)我們?nèi)匀豢梢垣@取到它的Value。 "hello" redis 127.0.0.1:6379> ttl mykey #該ttl命令的返回值顯示,該Key已經(jīng)過期。 (integer) 0 redis 127.0.0.1:6379> get mykey #獲取已過期的Key將返回nil。 (nil)

?

5. SETNX:

redis 127.0.0.1:6379> del mykey #刪除該鍵,以便于下面的測試驗證。 (integer) 1 redis 127.0.0.1:6379> setnx mykey "hello" #該鍵并不存在,因此該命令執(zhí)行成功。 (integer) 1 redis 127.0.0.1:6379> setnx mykey "world" #該鍵已經(jīng)存在,因此本次設(shè)置沒有產(chǎn)生任何效果。 (integer) 0 redis 127.0.0.1:6379> get mykey #從結(jié)果可以看出,返回的值仍為第一次設(shè)置的值。 "hello"

?

6. SETRANGE / GETRANGE :

redis 127.0.0.1:6379> set mykey "hello world" #設(shè)定初始值。 OK redis 127.0.0.1:6379> setrange mykey 6 dd #從第六個字節(jié)開始替換2個字節(jié)(dd只有2個字節(jié)) (integer) 11 redis 127.0.0.1:6379> get mykey #查看替換后的值。 "hello ddrld" redis 127.0.0.1:6379> setrange mykey 20 dd #offset已經(jīng)超過該Key原有值的長度了,該命令將會在末尾補0。 (integer) 22 redis 127.0.0.1:6379> get mykey #查看補0后替換的結(jié)果。 "hello ddrld\x00\x00\x00\x00\x00\x00\x00\x00\x00dd" redis 127.0.0.1:6379> del mykey #刪除該Key。 (integer) 1 redis 127.0.0.1:6379> setrange mykey 2 dd #替換空值。 (integer) 4 redis 127.0.0.1:6379> get mykey #查看替換空值后的結(jié)果。 "\x00\x00dd" redis 127.0.0.1:6379> set mykey "0123456789" #設(shè)置新值。 OK redis 127.0.0.1:6379> getrange mykey 1 2 #截取該鍵的Value,從第一個字節(jié)開始,到第二個字節(jié)結(jié)束。 "12" redis 127.0.0.1:6379> getrange mykey 1 20 #20已經(jīng)超過Value的總長度,因此將截取第一個字節(jié)后面的所有字節(jié)。 "123456789"

?

7. SETBIT / GETBIT :

redis 127.0.0.1:6379> del mykey (integer) 1 redis 127.0.0.1:6379> setbit mykey 7 1 #設(shè)置從0開始計算的第七位BIT值為1,返回原有BIT值0 (integer) 0 redis 127.0.0.1:6379> get mykey #獲取設(shè)置的結(jié)果,二進制的0000 0001的十六進制值為0x01 "\x01" redis 127.0.0.1:6379> setbit mykey 6 1 #設(shè)置從0開始計算的第六位BIT值為1,返回原有BIT值0 (integer) 0 redis 127.0.0.1:6379> get mykey #獲取設(shè)置的結(jié)果,二進制的0000 0011的十六進制值為0x03 "\x03" redis 127.0.0.1:6379> getbit mykey 6 #返回了指定Offset的BIT值。 (integer) 1 redis 127.0.0.1:6379> getbit mykey 10 #Offset已經(jīng)超出了value的長度,因此返回0。 (integer) 0

?

8. MSET / MGET / MSETNX :

redis 127.0.0.1:6379> mset key1 "hello" key2 "world" #批量設(shè)置了key1和key2兩個鍵。 OK redis 127.0.0.1:6379> mget key1 key2 #批量獲取了key1和key2兩個鍵的值。 1) "hello" 2) "world" #批量設(shè)置了key3和key4兩個鍵,因為之前他們并不存在,所以該命令執(zhí)行成功并返回1。 redis 127.0.0.1:6379> msetnx key3 "stephen" key4 "liu" (integer) 1 redis 127.0.0.1:6379> mget key3 key4 1) "stephen" 2) "liu" #批量設(shè)置了key3和key5兩個鍵,但是key3已經(jīng)存在,所以該命令執(zhí)行失敗并返回0。 redis 127.0.0.1:6379> msetnx key3 "hello" key5 "world" (integer) 0 #批量獲取key3和key5,由于key5沒有設(shè)置成功,所以返回nil。 redis 127.0.0.1:6379> mget key3 key5 1) "stephen" 2) (nil)

?

redis 數(shù)據(jù)結(jié)構(gòu) ---?list

?

redis 的另一個重要的數(shù)據(jù)結(jié)構(gòu)叫做 lists,翻譯成中文叫做“列表”。
首先要明確一點,redis中的lists在底層實現(xiàn)上并不是數(shù)組,而是鏈表,也就是說對于一個具有上百萬個元素的lists來說,在頭部和尾部插入一個新元素,其時間復(fù)雜度是常數(shù)級別的,比如用LPUSH在10個元素的lists頭部插入新元素,和在上千萬元素的lists頭部插入新元素的速度應(yīng)該是相同的。雖然lists有這樣的優(yōu)勢,但同樣有其弊端,那就是,鏈表型lists的元素定位會比較慢,而數(shù)組型lists的元素定位就會快得多。lists的常用操作包括LPUSH、RPUSH、LRANGE等。我們可以用LPUSH在lists的左側(cè)插入一個新元素,用RPUSH在lists的右側(cè)插入一個新元素,用LRANGE命令從lists中指定一個范圍來提取元素。

在Redis中,List類型是按照插入順序排序的字符串鏈表。和數(shù)據(jù)結(jié)構(gòu)中的普通鏈表一樣,我們可以在其頭部(left)和尾部(right)添加新的元素。在插入時,如果該鍵并不存在,Redis將為該鍵創(chuàng)建一個新的鏈表。與此相反,如果鏈表中所有的元素均被移除,那么該鍵也將會被從數(shù)據(jù)庫中刪除。List中可以包含的最大元素數(shù)量是4294967295。
從元素插入和刪除的效率視角來看,如果我們是在鏈表的兩頭插入或刪除元素,這將會是非常高效的操作,即使鏈表中已經(jīng)存儲了百萬條記錄,該操作也可以在常量時間內(nèi)完成。然而需要說明的是,如果元素插入或刪除操作是作用于鏈表中間,那將會是非常低效的。相信對于有良好數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)的開發(fā)者而言,這一點并不難理解。

我們來看幾個例子:

//新建一個list叫做mylist,并在列表頭部插入元素"1" 127.0.0.1:6379> lpush mylist "1" //返回當前mylist中的元素個數(shù) (integer) 1 //在mylist右側(cè)插入元素"2" 127.0.0.1:6379> rpush mylist "2" (integer) 2 //在mylist左側(cè)插入元素"0" 127.0.0.1:6379> lpush mylist "0" (integer) 3 //列出mylist中從編號0到編號1的元素 127.0.0.1:6379> lrange mylist 0 1 1) "0" 2) "1" //列出mylist中從編號0到倒數(shù)第一個元素 127.0.0.1:6379> lrange mylist 0 -1 1) "0" 2) "1" 3) "2"

lists 的應(yīng)用相當廣泛,隨便舉幾個例子:

  • 1. 我們可以利用 lists 來實現(xiàn)一個消息隊列,而且可以確保先后順序,不必像 MySQL 那樣還需要通過 ORDER BY 來進行排序。
  • 2. 利用 LRANGE 還可以很方便的實現(xiàn)分頁的功能。
  • 3. 在博客系統(tǒng)中,每片博文的評論也可以存入一個單獨的 list 中。

相關(guān)命令

命令原型 時間復(fù)雜度 返回值 LPUSHkey value [value ...] O(1) 插入后鏈表中元素的數(shù)量。在指定Key所關(guān)聯(lián)的List Value的頭部插入?yún)?shù)中給出的所有Values。如果該Key不存在,該命令將在插入之前創(chuàng)建一個與該Key關(guān)聯(lián)的空鏈表,之后再將數(shù)據(jù)從鏈表的頭部插入。如果該鍵的Value不是鏈表類型,該命令將返回相關(guān)的錯誤信息。 LPUSHX key value O(1) 插入后鏈表中元素的數(shù)量。 僅有當參數(shù)中指定的Key存在時,該命令才會在其所關(guān)聯(lián)的List Value的頭部插入?yún)?shù)中給出的Value,否則將不會有任何操作發(fā)生。 LRANGE key start stop O(S+N) 返回指定范圍內(nèi)元素的列表。時間復(fù)雜度中的S為start參數(shù)表示的偏移量,N表示元素的數(shù)量。該命令的參數(shù)start和end都是0-based。即0表示鏈表頭部(leftmost)的第一個元素。其中start的值也可以為負值,-1將表示鏈表中的最后一個元素,即尾部元素,-2表示倒數(shù)第二個并以此類推。該命令在獲取元素時,start和end位置上的元素也會被取出。如果start的值大于鏈表中元素的數(shù)量,空鏈表將會被返回。如果end的值大于元素的數(shù)量,該命令則獲取從start(包括start)開始,鏈表中剩余的所有元素。 LPOPkey O(1) 如果該Key不存,返回nil。 鏈表頭部的元素。返回并彈出指定Key關(guān)聯(lián)的鏈表中的第一個元素,即頭部元素。 LLENkey O(1) 鏈表中元素的數(shù)量。返回指定Key關(guān)聯(lián)的鏈表中元素的數(shù)量,如果該Key不存在,則返回0。如果與該Key關(guān)聯(lián)的Value的類型不是鏈表,則返回相關(guān)的錯誤信息。 LREMkey count value O(N) 返回被刪除的元素數(shù)量。時間復(fù)雜度中N表示鏈表中元素的數(shù)量。在指定Key關(guān)聯(lián)的鏈表中,刪除前count個值等于value的元素。如果count大于0,從頭向尾遍歷并刪除,如果count小于0,則從尾向頭遍歷并刪除。如果count等于0,則刪除鏈表中所有等于value的元素。如果指定的Key不存在,則直接返回0。 LSETkey index value O(N) 時間復(fù)雜度中N表示鏈表中元素的數(shù)量。但是設(shè)定頭部或尾部的元素時,其時間復(fù)雜度為O(1)。設(shè)定鏈表中指定位置的值為新值,其中0表示第一個元素,即頭部元素,-1表示尾部元素。如果索引值Index超出了鏈表中元素的數(shù)量范圍,該命令將返回相關(guān)的錯誤信息。 LINDEX key index O(N) 返回請求的元素,如果index超出范圍,則返回nil。時間復(fù)雜度中N表示在找到該元素時需要遍歷的元素數(shù)量。對于頭部或尾部元素,其時間復(fù)雜度為O(1)。該命令將返回鏈表中指定位置(index)的元素,index是0-based,表示頭部元素,如果index為-1,表示尾部元素。如果與該Key關(guān)聯(lián)的不是鏈表,該命令將返回相關(guān)的錯誤信息。 LTRIMkey start stop O(N) N表示被刪除的元素數(shù)量。該命令將僅保留指定范圍內(nèi)的元素,從而保證鏈接中的元素數(shù)量相對恒定。start和stop參數(shù)都是0-based,0表示頭部元素。和其他命令一樣,start和stop也可以為負值,-1表示尾部元素。如果start大于鏈表的尾部,或start大于stop,該命令不錯報錯,而是返回一個空的鏈表,與此同時該Key也將被刪除。如果stop大于元素的數(shù)量,則保留從start開始剩余的所有元素。 LINSERT key BEFORE|AFTER pivot value O(N) 成功插入后鏈表中元素的數(shù)量,如果沒有找到pivot,返回-1,如果key不存在,返回0。時間復(fù)雜度中N表示在找到該元素pivot之前需要遍歷的元素數(shù)量。這樣意味著如果pivot位于鏈表的頭部或尾部時,該命令的時間復(fù)雜度為O(1)。該命令的功能是在pivot元素的前面或后面插入?yún)?shù)中的元素value。如果Key不存在,該命令將不執(zhí)行任何操作。如果與Key關(guān)聯(lián)的Value類型不是鏈表,相關(guān)的錯誤信息將被返回。 RPUSH key value [value ...] O(1) 插入后鏈表中元素的數(shù)量。 在指定Key所關(guān)聯(lián)的List Value的尾部插入?yún)?shù)中給出的所有Values。如果該Key不存在,該命令將在插入之前創(chuàng)建一個與該Key關(guān)聯(lián)的空鏈表,之后再將數(shù)據(jù)從鏈表的尾部插入。如果該鍵的Value不是鏈表類型,該命令將返回相關(guān)的錯誤信息。 RPUSHX key value O(1) 插入后鏈表中元素的數(shù)量。 僅有當參數(shù)中指定的Key存在時,該命令才會在其所關(guān)聯(lián)的List Value的尾部插入?yún)?shù)中給出的Value,否則將不會有任何操作發(fā)生。 RPOPkey O(1) 如果該Key不存,返回nil。 鏈表尾部的元素。 返回并彈出指定Key關(guān)聯(lián)的鏈表中的最后一個元素,即尾部元素。 RPOPLPUSHsource destination O(1) 返回彈出和插入的元素。原子性的從與source鍵關(guān)聯(lián)的鏈表尾部彈出一個元素,同時再將彈出的元素插入到與destination鍵關(guān)聯(lián)的鏈表的頭部。如果source鍵不存在,該命令將返回nil,同時不再做任何其它的操作了。如果source和destination是同一個鍵,則相當于原子性的將其關(guān)聯(lián)鏈表中的尾部元素移到該鏈表的頭部。

命令示例

1. LPUSH/LPUSHX/LRANGE:/> redis-cli #在Shell提示符下啟動redis客戶端工具。redis 127.0.0.1:6379> del mykey(integer) 1#mykey鍵并不存在,該命令會創(chuàng)建該鍵及與其關(guān)聯(lián)的List,之后在將參數(shù)中的values從左到右依次插入。redis 127.0.0.1:6379> lpush mykey a b c d(integer) 4#取從位置0開始到位置2結(jié)束的3個元素。redis 127.0.0.1:6379> lrange mykey 0 21) "d"2) "c"3) "b"#取鏈表中的全部元素,其中0表示第一個元素,-1表示最后一個元素。redis 127.0.0.1:6379> lrange mykey 0 -11) "d"2) "c"3) "b"4) "a"#mykey2鍵此時并不存在,因此該命令將不會進行任何操作,其返回值為0。redis 127.0.0.1:6379> lpushx mykey2 e(integer) 0#可以看到mykey2沒有關(guān)聯(lián)任何List Value。redis 127.0.0.1:6379> lrange mykey2 0 -1(empty list or set)#mykey鍵此時已經(jīng)存在,所以該命令插入成功,并返回鏈表中當前元素的數(shù)量。redis 127.0.0.1:6379> lpushx mykey e(integer) 5#獲取該鍵的List Value的頭部元素。redis 127.0.0.1:6379> lrange mykey 0 01) "e"2. LPOP/LLEN:redis 127.0.0.1:6379> lpush mykey a b c d(integer) 4redis 127.0.0.1:6379> lpop mykey"d"redis 127.0.0.1:6379> lpop mykey"c"#在執(zhí)行l(wèi)pop命令兩次后,鏈表頭部的兩個元素已經(jīng)被彈出,此時鏈表中元素的數(shù)量是2redis 127.0.0.1:6379> llen mykey(integer) 23. LREM/LSET/LINDEX/LTRIM:#為后面的示例準備測試數(shù)據(jù)。redis 127.0.0.1:6379> lpush mykey a b c d a c(integer) 6#從頭部(left)向尾部(right)變量鏈表,刪除2個值等于a的元素,返回值為實際刪除的數(shù)量。redis 127.0.0.1:6379> lrem mykey 2 a(integer) 2#看出刪除后鏈表中的全部元素。redis 127.0.0.1:6379> lrange mykey 0 -11) "c"2) "d"3) "c"4) "b"#獲取索引值為1(頭部的第二個元素)的元素值。redis 127.0.0.1:6379> lindex mykey 1"d"#將索引值為1(頭部的第二個元素)的元素值設(shè)置為新值e。redis 127.0.0.1:6379> lset mykey 1 eOK#查看是否設(shè)置成功。redis 127.0.0.1:6379> lindex mykey 1"e"#索引值6超過了鏈表中元素的數(shù)量,該命令返回nil。redis 127.0.0.1:6379> lindex mykey 6(nil)#設(shè)置的索引值6超過了鏈表中元素的數(shù)量,設(shè)置失敗,該命令返回錯誤信息。redis 127.0.0.1:6379> lset mykey 6 hh(error) ERR index out of range#僅保留索引值0到2之間的3個元素,注意第0個和第2個元素均被保留。redis 127.0.0.1:6379> ltrim mykey 0 2OK#查看trim后的結(jié)果。redis 127.0.0.1:6379> lrange mykey 0 -11) "c"2) "e"3) "c"4. LINSERT:#刪除該鍵便于后面的測試。redis 127.0.0.1:6379> del mykey(integer) 1#為后面的示例準備測試數(shù)據(jù)。redis 127.0.0.1:6379> lpush mykey a b c d e(integer) 5#在a的前面插入新元素a1。redis 127.0.0.1:6379> linsert mykey before a a1(integer) 6#查看是否插入成功,從結(jié)果看已經(jīng)插入。注意lindex的index值是0-based。redis 127.0.0.1:6379> lindex mykey 0"e"#在e的后面插入新元素e2,從返回結(jié)果看已經(jīng)插入成功。redis 127.0.0.1:6379> linsert mykey after e e2(integer) 7#再次查看是否插入成功。redis 127.0.0.1:6379> lindex mykey 1"e2"#在不存在的元素之前或之后插入新元素,該命令操作失敗,并返回-1。redis 127.0.0.1:6379> linsert mykey after k a(integer) -1#為不存在的Key插入新元素,該命令操作失敗,返回0。redis 127.0.0.1:6379> linsert mykey1 after a a2(integer) 05. RPUSH/RPUSHX/RPOP/RPOPLPUSH:#刪除該鍵,以便于后面的測試。redis 127.0.0.1:6379> del mykey(integer) 1#從鏈表的尾部插入?yún)?shù)中給出的values,插入順序是從左到右依次插入。redis 127.0.0.1:6379> rpush mykey a b c d(integer) 4#通過lrange的可以獲悉rpush在插入多值時的插入順序。redis 127.0.0.1:6379> lrange mykey 0 -11) "a"2) "b"3) "c"4) "d"#該鍵已經(jīng)存在并且包含4個元素,rpushx命令將執(zhí)行成功,并將元素e插入到鏈表的尾部。redis 127.0.0.1:6379> rpushx mykey e(integer) 5#通過lindex命令可以看出之前的rpushx命令確實執(zhí)行成功,因為索引值為4的元素已經(jīng)是新元素了。redis 127.0.0.1:6379> lindex mykey 4"e"#由于mykey2鍵并不存在,因此該命令不會插入數(shù)據(jù),其返回值為0。redis 127.0.0.1:6379> rpushx mykey2 e(integer) 0#在執(zhí)行rpoplpush命令前,先看一下mykey中鏈表的元素有哪些,注意他們的位置關(guān)系。redis 127.0.0.1:6379> lrange mykey 0 -11) "a"2) "b"3) "c"4) "d"5) "e"#將mykey的尾部元素e彈出,同時再插入到mykey2的頭部(原子性的完成這兩步操作)。redis 127.0.0.1:6379> rpoplpush mykey mykey2"e"#通過lrange命令查看mykey在彈出尾部元素后的結(jié)果。redis 127.0.0.1:6379> lrange mykey 0 -11) "a"2) "b"3) "c"4) "d"#通過lrange命令查看mykey2在插入元素后的結(jié)果。redis 127.0.0.1:6379> lrange mykey2 0 -11) "e"#將source和destination設(shè)為同一鍵,將mykey中的尾部元素移到其頭部。redis 127.0.0.1:6379> rpoplpush mykey mykey"d"#查看移動結(jié)果。redis 127.0.0.1:6379> lrange mykey 0 -11) "d"2) "a"3) "b"4) "c"

鏈表結(jié)構(gòu)的小技巧:

針對鏈表結(jié)構(gòu)的Value,Redis在其官方文檔中給出了一些實用技巧,如RPOPLPUSH命令,下面給出具體的解釋。
Redis鏈表經(jīng)常會被用于消息隊列的服務(wù),以完成多程序之間的消息交換。假設(shè)一個應(yīng)用程序正在執(zhí)行LPUSH操作向鏈表中添加新的元素,我們通常將這樣的程序稱之為"生產(chǎn)者(Producer)",而另外一個應(yīng)用程序正在執(zhí)行RPOP操作從鏈表中取出元素,我們稱這樣的程序為"消費者(Consumer)"。如果此時,消費者程序在取出消息元素后立刻崩潰,由于該消息已經(jīng)被取出且沒有被正常處理,那么我們就可以認為該消息已經(jīng)丟失,由此可能會導(dǎo)致業(yè)務(wù)數(shù)據(jù)丟失,或業(yè)務(wù)狀態(tài)的不一致等現(xiàn)象的發(fā)生。然而通過使RPOPLPUSH命令,消費者程序在從主消息隊列中取出消息之后再將其插入到備份隊列中,直到消費者程序完成正常的處理邏輯后再將該消息從備份隊列中刪除。同時我們還可以提供一個守護進程,當發(fā)現(xiàn)備份隊列中的消息過期時,可以重新將其再放回到主消息隊列中,以便其它的消費者程序繼續(xù)處理。

?

redis數(shù)據(jù)結(jié)構(gòu) ---?集合

?

在Redis中,我們可以將Set類型看作為沒有排序的字符集合,和List類型一樣,我們也可以在該類型的數(shù)據(jù)值上執(zhí)行添加、刪除或判斷某一元素是否存在等操作。需要說明的是,這些操作的時間復(fù)雜度為O(1),即常量時間內(nèi)完成次操作。Set可包含的最大元素數(shù)量是4294967295。
和List類型不同的是,Set集合中不允許出現(xiàn)重復(fù)的元素,這一點和C++標準庫中的set容器是完全相同的。換句話說,如果多次添加相同元素,Set中將僅保留該元素的一份拷貝。和List類型相比,Set類型在功能上還存在著一個非常重要的特性,即在服務(wù)器端完成多個Sets之間的聚合計算操作,如unions、intersections和differences。由于這些操作均在服務(wù)端完成,因此效率極高,而且也節(jié)省了大量的網(wǎng)絡(luò)IO開銷。

redis的集合,是一種無序的集合,集合中的元素沒有先后順序。
集合相關(guān)的操作也很豐富,如添加新元素、刪除已有元素、取交集、取并集、取差集等。我們來看例子:

//向集合myset中加入一個新元素"one" 127.0.0.1:6379> sadd myset "one" (integer) 1 127.0.0.1:6379> sadd myset "two" (integer) 1 //列出集合myset中的所有元素 127.0.0.1:6379> smembers myset 1) "one" 2) "two" //判斷元素1是否在集合myset中,返回1表示存在 127.0.0.1:6379> sismember myset "one" (integer) 1 //判斷元素3是否在集合myset中,返回0表示不存在 127.0.0.1:6379> sismember myset "three" (integer) 0 //新建一個新的集合yourset 127.0.0.1:6379> sadd yourset "1" (integer) 1 127.0.0.1:6379> sadd yourset "2" (integer) 1 127.0.0.1:6379> smembers yourset 1) "1" 2) "2" //對兩個集合求并集 127.0.0.1:6379> sunion myset yourset 1) "1" 2) "one" 3) "2" 4) "two"

對于集合的使用,也有一些常見的方式,比如,QQ有一個社交功能叫做“好友標簽”,大家可以給你的好友貼標簽,比如“大美女”、“土豪”、“歐巴”等等,這時就可以使用redis的集合來實現(xiàn),把每一個用戶的標簽都存儲在一個集合之中。

相關(guān)命令

命令原型 時間復(fù)雜度 返回值 SADDkey member [member ...] O(N) 本次操作實際插入的成員數(shù)量。時間復(fù)雜度中的N表示操作的成員數(shù)量。如果在插入的過程用,參數(shù)中有的成員在Set中已經(jīng)存在,該成員將被忽略,而其它成員仍將會被正常插入。如果執(zhí)行該命令之前,該Key并不存在,該命令將會創(chuàng)建一個新的Set,此后再將參數(shù)中的成員陸續(xù)插入。如果該Key的Value不是Set類型,該命令將返回相關(guān)的錯誤信息。 SCARDkey O(1) 返回Set中成員的數(shù)量,如果該Key并不存在,返回0。獲取Set中成員的數(shù)量。 SISMEMBER key member O(1) 1表示已經(jīng)存在,0表示不存在,或該Key本身并不存在。判斷參數(shù)中指定成員是否已經(jīng)存在于與Key相關(guān)聯(lián)的Set集合中。 SMEMBERS key O(N) 返回Set中所有的成員。時間復(fù)雜度中的N表示Set中已經(jīng)存在的成員數(shù)量。獲取與該Key關(guān)聯(lián)的Set中所有的成員。 SPOPkey O(1) 返回移除的成員,如果該Key并不存在,則返回nil。隨機的移除并返回Set中的某一成員。 由于Set中元素的布局不受外部控制,因此無法像List那樣確定哪個元素位于Set的頭部或者尾部。 SREMkey member [member ...] O(N) 從Set中實際移除的成員數(shù)量,如果沒有則返回0。時間復(fù)雜度中的N表示被刪除的成員數(shù)量。從與Key關(guān)聯(lián)的Set中刪除參數(shù)中指定的成員,不存在的參數(shù)成員將被忽略,如果該Key并不存在,將視為空Set處理。 SRANDMEMBER key O(1) 返回隨機位置的成員,如果Key不存在則返回nil。和SPOP一樣,隨機的返回Set中的一個成員,不同的是該命令并不會刪除返回的成員。 SMOVEsource destination member O(1) 1表示正常移動,0表示source中并不包含參數(shù)成員。原子性的將參數(shù)中的成員從source鍵移入到destination鍵所關(guān)聯(lián)的Set中。因此在某一時刻,該成員或者出現(xiàn)在source中,或者出現(xiàn)在destination中。如果該成員在source中并不存在,該命令將不會再執(zhí)行任何操作并返回0,否則,該成員將從source移入到destination。如果此時該成員已經(jīng)在destination中存在,那么該命令僅是將該成員從source中移出。如果和Key關(guān)聯(lián)的Value不是Set,將返回相關(guān)的錯誤信息。 SDIFFkey [key ...] O(N) 差異結(jié)果成員的集合。 時間復(fù)雜度中的N表示所有Sets中成員的總數(shù)量。返回參數(shù)中第一個Key所關(guān)聯(lián)的Set和其后所有Keys所關(guān)聯(lián)的Sets中成員的差異。如果Key不存在,則視為空Set。 SDIFFSTOREdestination key [key ...] O(N) 返回差異成員的數(shù)量。 該命令和SDIFF命令在功能上完全相同。兩者之間唯一的差別是SDIFF返回差異的結(jié)果成員,而該命令將差異成員存儲在destination關(guān)聯(lián)的Set中。如果destination鍵已經(jīng)存在,該操作將覆蓋它的成員。兩者之間唯一的差別是SDIFF返回差異的結(jié)果成員,而該命令將差異成員存儲在destination關(guān)聯(lián)的Set中。如果destination鍵已經(jīng)存在,該操作將覆蓋它的成員。 SINTERkey [key ...] O(N*M) 交集結(jié)果成員的集合。時間復(fù)雜度中的N表示最小Set中元素的數(shù)量,M則表示參數(shù)中Sets的數(shù)量。該命令將返回參數(shù)中所有Keys關(guān)聯(lián)的Sets中成員的交集。因此如果參數(shù)中任何一個Key關(guān)聯(lián)的Set為空,或某一Key不存在,那么該命令的結(jié)果將為空集。 SINTERSTOREdestination key [key ...] O(N*M) 返回交集成員的數(shù)量。 該命令和SINTER命令在功能上完全相同,兩者之間唯一的差別是SINTER返回交集的結(jié)果成員,而該命令將交集成員存儲在destination關(guān)聯(lián)的Set中。如果destination鍵已經(jīng)存在,該操作將覆蓋它的成員。 SUNION key [key ...] O(N) 并集結(jié)果成員的集合。時間復(fù)雜度中的N表示所有Sets中成員的總數(shù)量。該命令將返回參數(shù)中所有Keys關(guān)聯(lián)的Sets中成員的并集。 SUNIONSTOREdestination key [key ...] O(N) 返回并集成員的數(shù)量。該命令和SUNION命令在功能上完全相同,兩者之間唯一的差別是SUNION返回并集的結(jié)果成員,而該命令將并集成員存儲在destination關(guān)聯(lián)的Set中。如果destination鍵已經(jīng)存在,該操作將覆蓋它的成員。

命令示例

1. SADD/SMEMBERS/SCARD/SISMEMBER:#在Shell命令行下啟動Redis的客戶端程序。/> redis-cli#插入測試數(shù)據(jù),由于該鍵myset之前并不存在,因此參數(shù)中的三個成員都被正常插入。redis 127.0.0.1:6379> sadd myset a b c(integer) 3#由于參數(shù)中的a在myset中已經(jīng)存在,因此本次操作僅僅插入了d和e兩個新成員。redis 127.0.0.1:6379> sadd myset a d e(integer) 2#判斷a是否已經(jīng)存在,返回值為1表示存在。redis 127.0.0.1:6379> sismember myset a(integer) 1#判斷f是否已經(jīng)存在,返回值為0表示不存在。redis 127.0.0.1:6379> sismember myset f(integer) 0#通過smembers命令查看插入的結(jié)果,從結(jié)果可以,輸出的順序和插入順序無關(guān)。redis 127.0.0.1:6379> smembers myset1) "c"2) "d"3) "a"4) "b"5) "e"#獲取Set集合中元素的數(shù)量。redis 127.0.0.1:6379> scard myset(integer) 52. SPOP/SREM/SRANDMEMBER/SMOVE:#刪除該鍵,便于后面的測試。redis 127.0.0.1:6379> del myset(integer) 1#為后面的示例準備測試數(shù)據(jù)。redis 127.0.0.1:6379> sadd myset a b c d(integer) 4#查看Set中成員的位置。redis 127.0.0.1:6379> smembers myset1) "c"2) "d"3) "a"4) "b"#從結(jié)果可以看出,該命令確實是隨機的返回了某一成員。redis 127.0.0.1:6379> srandmember myset"c"#Set中尾部的成員b被移出并返回,事實上b并不是之前插入的第一個或最后一個成員。redis 127.0.0.1:6379> spop myset"b"#查看移出后Set的成員信息。redis 127.0.0.1:6379> smembers myset1) "c"2) "d"3) "a"#從Set中移出a、d和f三個成員,其中f并不存在,因此只有a和d兩個成員被移出,返回為2。redis 127.0.0.1:6379> srem myset a d f(integer) 2#查看移出后的輸出結(jié)果。redis 127.0.0.1:6379> smembers myset1) "c"#為后面的smove命令準備數(shù)據(jù)。redis 127.0.0.1:6379> sadd myset a b(integer) 2redis 127.0.0.1:6379> sadd myset2 c d(integer) 2#將a從myset移到myset2,從結(jié)果可以看出移動成功。redis 127.0.0.1:6379> smove myset myset2 a(integer) 1#再次將a從myset移到myset2,由于此時a已經(jīng)不是myset的成員了,因此移動失敗并返回0。redis 127.0.0.1:6379> smove myset myset2 a(integer) 0#分別查看myset和myset2的成員,確認移動是否真的成功。redis 127.0.0.1:6379> smembers myset1) "b"redis 127.0.0.1:6379> smembers myset21) "c"2) "d"3) "a"3. SDIFF/SDIFFSTORE/SINTER/SINTERSTORE:#為后面的命令準備測試數(shù)據(jù)。redis 127.0.0.1:6379> sadd myset a b c d(integer) 4redis 127.0.0.1:6379> sadd myset2 c(integer) 1redis 127.0.0.1:6379> sadd myset3 a c e(integer) 3#myset和myset2相比,a、b和d三個成員是兩者之間的差異成員。再用這個結(jié)果繼續(xù)和myset3進行差異比較,b和d是myset3不存在的成員。redis 127.0.0.1:6379> sdiff myset myset2 myset31) "d"2) "b"#將3個集合的差異成員存在在diffkey關(guān)聯(lián)的Set中,并返回插入的成員數(shù)量。redis 127.0.0.1:6379> sdiffstore diffkey myset myset2 myset3(integer) 2#查看一下sdiffstore的操作結(jié)果。redis 127.0.0.1:6379> smembers diffkey1) "d"2) "b"#從之前準備的數(shù)據(jù)就可以看出,這三個Set的成員交集只有c。redis 127.0.0.1:6379> sinter myset myset2 myset31) "c"#將3個集合中的交集成員存儲到與interkey關(guān)聯(lián)的Set中,并返回交集成員的數(shù)量。redis 127.0.0.1:6379> sinterstore interkey myset myset2 myset3(integer) 1#查看一下sinterstore的操作結(jié)果。redis 127.0.0.1:6379> smembers interkey1) "c"#獲取3個集合中的成員的并集。 redis 127.0.0.1:6379> sunion myset myset2 myset31) "b"2) "c"3) "d"4) "e"5) "a"#將3個集合中成員的并集存儲到unionkey關(guān)聯(lián)的set中,并返回并集成員的數(shù)量。redis 127.0.0.1:6379> sunionstore unionkey myset myset2 myset3(integer) 5#查看一下suiionstore的操作結(jié)果。redis 127.0.0.1:6379> smembers unionkey1) "b"2) "c"3) "d"4) "e"5) "a"

應(yīng)用范圍:

  • 1). 可以使用Redis的Set數(shù)據(jù)類型跟蹤一些唯一性數(shù)據(jù),比如訪問某一博客的唯一IP地址信息。對于此場景,我們僅需在每次訪問該博客時將訪問者的IP存入Redis中,Set數(shù)據(jù)類型會自動保證IP地址的唯一性。
  • 2). 充分利用Set類型的服務(wù)端聚合操作方便、高效的特性,可以用于維護數(shù)據(jù)對象之間的關(guān)聯(lián)關(guān)系。比如所有購買某一電子設(shè)備的客戶ID被存儲在一個指定的Set中,而購買另外一種電子產(chǎn)品的客戶ID被存儲在另外一個Set中,如果此時我們想獲取有哪些客戶同時購買了這兩種商品時,Set的intersections命令就可以充分發(fā)揮它的方便和效率的優(yōu)勢了。

?

redis 數(shù)據(jù)結(jié)構(gòu) – 有序集合

?

Sorted-Sets 和 Sets 類型極為相似,它們都是字符串的集合,都不允許重復(fù)的成員出現(xiàn)在一個Set中。它們之間的主要差別是Sorted-Sets中的每一個成員都會有一個分數(shù)(score)與之關(guān)聯(lián),Redis正是通過分數(shù)來為集合中的成員進行從小到大的排序。然而需要額外指出的是,盡管Sorted-Sets中的成員必須是唯一的,但是分數(shù)(score)卻是可以重復(fù)的。
在Sorted-Set中添加、刪除或更新一個成員都是非常快速的操作,其時間復(fù)雜度為集合中成員數(shù)量的對數(shù)。由于Sorted-Sets中的成員在集合中的位置是有序的,因此,即便是訪問位于集合中部的成員也仍然是非常高效的。事實上,Redis所具有的這一特征在很多其它類型的數(shù)據(jù)庫中是很難實現(xiàn)的,換句話說,在該點上要想達到和Redis同樣的高效,在其它數(shù)據(jù)庫中進行建模是非常困難的。

redis不但提供了無需集合(sets),還很體貼的提供了有序集合(sorted sets)。有序集合中的每個元素都關(guān)聯(lián)一個序號(score),這便是排序的依據(jù)。
很多時候,我們都將redis中的有序集合叫做zsets,這是因為在redis中,有序集合相關(guān)的操作指令都是以z開頭的,比如zrange、zadd、zrevrange、zrangebyscore等等
老規(guī)矩,我們來看幾個生動的例子:
//新增一個有序集合myzset,并加入一個元素baidu.com,給它賦予的序號是1:

127.0.0.1:6379> zadd myzset 1 baidu.com (integer) 1 //向myzset中新增一個元素360.com,賦予它的序號是3 127.0.0.1:6379> zadd myzset 3 360.com (integer) 1 //向myzset中新增一個元素google.com,賦予它的序號是2 127.0.0.1:6379> zadd myzset 2 google.com (integer) 1 //列出myzset的所有元素,同時列出其序號,可以看出myzset已經(jīng)是有序的了。 127.0.0.1:6379> zrange myzset 0 -1 with scores 1) "baidu.com" 2) "1" 3) "google.com" 4) "2" 5) "360.com" 6) "3" //只列出myzset的元素 127.0.0.1:6379> zrange myzset 0 -1 1) "baidu.com" 2) "google.com" 3) "360.com"

相關(guān)命令

命令原型 時間復(fù)雜度 返回值 ZADD key score member [score] [member] O(log(N)) 本次操作實際插入的成員數(shù)量。時間復(fù)雜度中的N表示Sorted-Sets中成員的數(shù)量。添加參數(shù)中指定的所有成員及其分數(shù)到指定key的Sorted-Set中,在該命令中我們可以指定多組score/member作為參數(shù)。如果在添加時參數(shù)中的某一成員已經(jīng)存在,該命令將更新此成員的分數(shù)為新值,同時再將該成員基于新值重新排序。如果鍵不存在,該命令將為該鍵創(chuàng)建一個新的Sorted-Sets Value,并將score/member對插入其中。如果該鍵已經(jīng)存在,但是與其關(guān)聯(lián)的Value不是Sorted-Sets類型,相關(guān)的錯誤信息將被返回。 ZCARD key O(1) 返回Sorted-Sets中的成員數(shù)量,如果該Key不存在,返回0。獲取與該Key相關(guān)聯(lián)的Sorted-Sets中包含的成員數(shù)量。 ZCOUNTkey min max O(log(N)+M) 分數(shù)指定范圍內(nèi)成員的數(shù)量。時間復(fù)雜度中的N表示Sorted-Sets中成員的數(shù)量,M則表示min和max之間元素的數(shù)量。該命令用于獲取分數(shù)(score)在min和max之間的成員數(shù)量。針對min和max參數(shù)需要額外說明的是,-inf和+inf分別表示Sorted-Sets中分數(shù)的最高值和最低值。缺省情況下,min和max表示的范圍是閉區(qū)間范圍,即min <= score <= max內(nèi)的成員將被返回。然而我們可以通過在min和max的前面添加"("字符來表示開區(qū)間,如(min max表示min < score <= max,而(min (max表示min < score < max。 ZINCRBYkey increment member O(log(N)) 以字符串形式表示的新分數(shù)。時間復(fù)雜度中的N表示Sorted-Sets中成員的數(shù)量。該命令將為指定Key中的指定成員增加指定的分數(shù)。如果成員不存在,該命令將添加該成員并假設(shè)其初始分數(shù)為0,此后再將其分數(shù)加上increment。如果Key不存,該命令將創(chuàng)建該Key及其關(guān)聯(lián)的Sorted-Sets,并包含參數(shù)指定的成員,其分數(shù)為increment參數(shù)。如果與該Key關(guān)聯(lián)的不是Sorted-Sets類型,相關(guān)的錯誤信息將被返回。 ZRANGEkey start stop [WITHSCORES] O(log(N)+M) 返回索引在start和stop之間的成員列表。時間復(fù)雜度中的N表示Sorted-Set中成員的數(shù)量,M則表示返回的成員數(shù)量。該命令返回順序在參數(shù)start和stop指定范圍內(nèi)的成員,這里start和stop參數(shù)都是0-based,即0表示第一個成員,-1表示最后一個成員。如果start大于該Sorted-Set中的最大索引值,或start > stop,此時一個空集合將被返回。如果stop大于最大索引值,該命令將返回從start到集合的最后一個成員。如果命令中帶有可選參數(shù)WITHSCORES選項,該命令在返回的結(jié)果中將包含每個成員的分數(shù)值,如value1,score1,value2,score2...。   ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] O(log(N)+M) 返回分數(shù)在指定范圍內(nèi)的成員列表。時間復(fù)雜度中的N表示Sorted-Set中成員的數(shù)量,M則表示返回的成員數(shù)量。該命令將返回分數(shù)在min和max之間的所有成員,即滿足表達式min <= score <= max的成員,其中返回的成員是按照其分數(shù)從低到高的順序返回,如果成員具有相同的分數(shù),則按成員的字典順序返回。可選參數(shù)LIMIT用于限制返回成員的數(shù)量范圍。可選參數(shù)offset表示從符合條件的第offset個成員開始返回,同時返回count個成員。可選參數(shù)WITHSCORES的含義參照ZRANGE中該選項的說明。最后需要說明的是參數(shù)中min和max的規(guī)則可參照命令ZCOUNT。 ZRANK key member O(log(N)) 如果該成員存在,則返回它的位置索引值。否則返回nil。時間復(fù)雜度中的N表示Sorted-Set中成員的數(shù)量。 Sorted-Set中的成員都是按照分數(shù)從低到高的順序存儲,該命令將返回參數(shù)中指定成員的位置值,其中0表示第一個成員,它是Sorted-Set中分數(shù)最低的成員。 ZREM key member [member ...] O(M log(N)) 實際被刪除的成員數(shù)量。時間復(fù)雜度中N表示Sorted-Set中成員的數(shù)量,M則表示被刪除的成員數(shù)量。該命令將移除參數(shù)中指定的成員,其中不存在的成員將被忽略。如果與該Key關(guān)聯(lián)的Value不是Sorted-Set,相應(yīng)的錯誤信息將被返回。 ZREVRANGE key startstop[WITHSCORES] O(log(N)+M) 返回指定的成員列表。時間復(fù)雜度中的N表示Sorted-Set中成員的數(shù)量,M則表示返回的成員數(shù)量。該命令的功能和ZRANGE基本相同,唯一的差別在于該命令是通過反向排序獲取指定位置的成員,即從高到低的順序。如果成員具有相同的分數(shù),則按降序字典順序排序。 ZREVRANKkey member O(log(N)) 如果該成員存在,則返回它的位置索引值。否則返回nil。 時間復(fù)雜度中的N表示Sorted-Set中成員的數(shù)量。該命令的功能和ZRANK基本相同,唯一的差別在于該命令獲取的索引是從高到低排序后的位置,同樣0表示第一個元素,即分數(shù)最高的成員。 ZSCOREkey member O(1) 如果該成員存在,以字符串的形式返回其分數(shù),否則返回nil。獲取指定Key的指定成員的分數(shù)。 ZREVRANGEBYSCOREkey max min [WITHSCORES] [LIMIT offset count] O(log(N)+M) 返回分數(shù)在指定范圍內(nèi)的成員列表。 時間復(fù)雜度中的N表示Sorted-Set中成員的數(shù)量,M則表示返回的成員數(shù)量。該命令除了排序方式是基于從高到低的分數(shù)排序之外,其它功能和參數(shù)含義均與ZRANGEBYSCORE相同。 ZREMRANGEBYRANKkey start stop O(log(N)+M) 被刪除的成員數(shù)量。時間復(fù)雜度中的N表示Sorted-Set中成員的數(shù)量,M則表示被刪除的成員數(shù)量。刪除索引位置位于start和stop之間的成員,start和stop都是0-based,即0表示分數(shù)最低的成員,-1表示最后一個成員,即分數(shù)最高的成員。 ZREMRANGEBYSCOREkey min max O(log(N)+M) 被刪除的成員數(shù)量。時間復(fù)雜度中的N表示Sorted-Set中成員的數(shù)量,M則表示被刪除的成員數(shù)量。刪除分數(shù)在min和max之間的所有成員,即滿足表達式min <= score <= max的所有成員。對于min和max參數(shù),可以采用開區(qū)間的方式表示,具體規(guī)則參照ZCOUNT。

命令示例

1. ZADD/ZCARD/ZCOUNT/ZREM/ZINCRBY/ZSCORE/ZRANGE/ZRANK:#在Shell的命令行下啟動Redis客戶端工具。/> redis-cli#添加一個分數(shù)為1的成員。redis 127.0.0.1:6379> zadd myzset 1 "one"(integer) 1#添加兩個分數(shù)分別是2和3的兩個成員。redis 127.0.0.1:6379> zadd myzset 2 "two" 3 "three"(integer) 2#0表示第一個成員,-1表示最后一個成員。WITHSCORES選項表示返回的結(jié)果中包含每個成員及其分數(shù),否則只返回成員。redis 127.0.0.1:6379> zrange myzset 0 -1 WITHSCORES1) "one"2) "1"3) "two"4) "2"5) "three"6) "3"#獲取成員one在Sorted-Set中的位置索引值。0表示第一個位置。redis 127.0.0.1:6379> zrank myzset one(integer) 0#成員four并不存在,因此返回nil。redis 127.0.0.1:6379> zrank myzset four(nil)#獲取myzset鍵中成員的數(shù)量。 redis 127.0.0.1:6379> zcard myzset(integer) 3#返回與myzset關(guān)聯(lián)的Sorted-Set中,分數(shù)滿足表達式1 <= score <= 2的成員的數(shù)量。redis 127.0.0.1:6379> zcount myzset 1 2(integer) 2#刪除成員one和two,返回實際刪除成員的數(shù)量。redis 127.0.0.1:6379> zrem myzset one two(integer) 2#查看是否刪除成功。redis 127.0.0.1:6379> zcard myzset(integer) 1#獲取成員three的分數(shù)。返回值是字符串形式。redis 127.0.0.1:6379> zscore myzset three"3"#由于成員two已經(jīng)被刪除,所以該命令返回nil。redis 127.0.0.1:6379> zscore myzset two(nil)#將成員one的分數(shù)增加2,并返回該成員更新后的分數(shù)。redis 127.0.0.1:6379> zincrby myzset 2 one"3"#將成員one的分數(shù)增加-1,并返回該成員更新后的分數(shù)。redis 127.0.0.1:6379> zincrby myzset -1 one"2"#查看在更新了成員的分數(shù)后是否正確。redis 127.0.0.1:6379> zrange myzset 0 -1 WITHSCORES1) "one"2) "2"3) "two"4) "2"5) "three"6) "3"2. ZRANGEBYSCORE/ZREMRANGEBYRANK/ZREMRANGEBYSCOREredis 127.0.0.1:6379> del myzset(integer) 1redis 127.0.0.1:6379> zadd myzset 1 one 2 two 3 three 4 four(integer) 4#獲取分數(shù)滿足表達式1 <= score <= 2的成員。redis 127.0.0.1:6379> zrangebyscore myzset 1 21) "one"2) "two"#獲取分數(shù)滿足表達式1 < score <= 2的成員。redis 127.0.0.1:6379> zrangebyscore myzset (1 21) "two"#-inf表示第一個成員,+inf表示最后一個成員,limit后面的參數(shù)用于限制返回成員的自己,#2表示從位置索引(0-based)等于2的成員開始,去后面3個成員。redis 127.0.0.1:6379> zrangebyscore myzset -inf +inf limit 2 31) "three"2) "four"#刪除分數(shù)滿足表達式1 <= score <= 2的成員,并返回實際刪除的數(shù)量。redis 127.0.0.1:6379> zremrangebyscore myzset 1 2(integer) 2#看出一下上面的刪除是否成功。redis 127.0.0.1:6379> zrange myzset 0 -11) "three"2) "four"#刪除位置索引滿足表達式0 <= rank <= 1的成員。redis 127.0.0.1:6379> zremrangebyrank myzset 0 1(integer) 2#查看上一條命令是否刪除成功。redis 127.0.0.1:6379> zcard myzset(integer) 03. ZREVRANGE/ZREVRANGEBYSCORE/ZREVRANK:#為后面的示例準備測試數(shù)據(jù)。redis 127.0.0.1:6379> del myzset(integer) 0redis 127.0.0.1:6379> zadd myzset 1 one 2 two 3 three 4 four(integer) 4#以位置索引從高到低的方式獲取并返回此區(qū)間內(nèi)的成員。redis 127.0.0.1:6379> zrevrange myzset 0 -1 WITHSCORES1) "four"2) "4"3) "three"4) "3"5) "two"6) "2"7) "one"8) "1"#由于是從高到低的排序,所以位置等于0的是four,1是three,并以此類推。redis 127.0.0.1:6379> zrevrange myzset 1 31) "three"2) "two"3) "one"#由于是從高到低的排序,所以one的位置是3。redis 127.0.0.1:6379> zrevrank myzset one(integer) 3#由于是從高到低的排序,所以four的位置是0。redis 127.0.0.1:6379> zrevrank myzset four(integer) 0#獲取分數(shù)滿足表達式3 >= score >= 0的成員,并以相反的順序輸出,即從高到底的順序。redis 127.0.0.1:6379> zrevrangebyscore myzset 3 01) "three"2) "two"3) "one"#該命令支持limit選項,其含義等同于zrangebyscore中的該選項,只是在計算位置時按照相反的順序計算和獲取。redis 127.0.0.1:6379> zrevrangebyscore myzset 4 0 limit 1 21) "three"2) "two"

應(yīng)用范圍:

  • 1). 可以用于一個大型在線游戲的積分排行榜。每當玩家的分數(shù)發(fā)生變化時,可以執(zhí)行ZADD命令更新玩家的分數(shù),此后再通過ZRANGE命令獲取積分TOP TEN的用戶信息。當然我們也可以利用ZRANK命令通過username來獲取玩家的排行信息。最后我們將組合使用ZRANGE和ZRANK命令快速的獲取和某個玩家積分相近的其他用戶的信息。
  • 2). Sorted-Sets 類型還可用于構(gòu)建索引數(shù)據(jù)。

?

redis 數(shù)據(jù)結(jié)構(gòu) ---?哈希

?

最后要給大家介紹的是hashes,即哈希。哈希是從redis-2.0.0版本之后才有的數(shù)據(jù)結(jié)構(gòu)。
hashes存的是字符串和字符串值之間的映射,比如一個用戶要存儲其全名、姓氏、年齡等等,就很適合使用哈希。

可以將 Redis中的 Hashes 類型看成具有 String Key 和 String Value 的 map 容器。所以該類型非常適合于存儲值對象的信息。如Username、Password和Age等。如果Hash中包含很少的字段,那么該類型的數(shù)據(jù)也將僅占用很少的磁盤空間。每一個Hash可以存儲4294967295個鍵值對。

看一個例子:

//建立哈希,并賦值 127.0.0.1:6379> HMSET user:001 username antirez password P1pp0 age 34 OK //列出哈希的內(nèi)容 127.0.0.1:6379> HGETALL user:001 1) "username" 2) "antirez" 3) "password" 4) "P1pp0" 5) "age" 6) "34" //更改哈希中的某一個值 127.0.0.1:6379> HSET user:001 password 12345 (integer) 0 //再次列出哈希的內(nèi)容 127.0.0.1:6379> HGETALL user:001 1) "username" 2) "antirez" 3) "password" 4) "12345" 5) "age" 6) "34"

有關(guān) hashes 的操作,同樣很豐富,需要時,大家可以從這里查詢:https://redis.io/commands

相關(guān)命令

命令原型 時間復(fù)雜度 返回值 HSET key field value O(1) 1表示新的Field被設(shè)置了新值,0表示Field已經(jīng)存在,用新值覆蓋原有值。為指定的Key設(shè)定Field/Value對,如果Key不存在,該命令將創(chuàng)建新Key以參數(shù)中的Field/Value對,如果參數(shù)中的Field在該Key中已經(jīng)存在,則用新值覆蓋其原有值。 HGET key field O(1) 返回參數(shù)中Field的關(guān)聯(lián)值,如果參數(shù)中的Key或Field不存,返回nil。返回指定Key中指定Field的關(guān)聯(lián)值。 HEXISTSkey field O(1) 1表示存在,0表示參數(shù)中的Field或Key不存在。判斷指定Key中的指定Field是否存在。 HLEN key O(1) 返回Key包含的Field數(shù)量,如果Key不存在,返回0。獲取該Key所包含的Field的數(shù)量。 HDEL key field [field ...] O(N) 時間復(fù)雜度中的N表示參數(shù)中待刪除的字段數(shù)量。從指定Key的Hashes Value中刪除參數(shù)中指定的多個字段,如果不存在的字段將被忽略。如果Key不存在,則將其視為空Hashes,并返回0. 實際刪除的Field數(shù)量。 HSETNXkey field value O(1) 1表示新的Field被設(shè)置了新值,0表示Key或Field已經(jīng)存在,該命令沒有進行任何操作。只有當參數(shù)中的Key或Field不存在的情況下,為指定的Key設(shè)定Field/Value對,否則該命令不會進行任何操作。 HINCRBYkey field increment O(1) 返回運算后的值。增加指定Key中指定Field關(guān)聯(lián)的Value的值。如果Key或Field不存在,該命令將會創(chuàng)建一個新Key或新Field,并將其關(guān)聯(lián)的Value初始化為0,之后再指定數(shù)字增加的操作。該命令支持的數(shù)字是64位有符號整型,即increment可以負數(shù)。 HGETALLkey O(N) Field/Value的列表。時間復(fù)雜度中的N表示Key包含的Field數(shù)量。獲取該鍵包含的所有Field/Value。其返回格式為一個Field、一個Value,并以此類推。 HKEYSkey O(N) 返回指定Key的所有Fields名。 Field的列表。時間復(fù)雜度中的N表示Key包含的Field數(shù)量。 HVALSkey O(N) Value的列表。 時間復(fù)雜度中的N表示Key包含的Field數(shù)量。返回指定Key的所有Values名。 HMGETkey field [field ...] O(N) 時間復(fù)雜度中的N表示請求的Field數(shù)量。 返回和請求Fields關(guān)聯(lián)的一組Values,其返回順序等同于Fields的請求順序。獲取和參數(shù)中指定Fields關(guān)聯(lián)的一組Values。如果請求的Field不存在,其值返回nil。如果Key不存在,該命令將其視為空Hash,因此返回一組nil。 HMSET key field value [field value ...] O(N) 時間復(fù)雜度中的N表示被設(shè)置的Field數(shù)量。逐對依次設(shè)置參數(shù)中給出的Field/Value對。如果其中某個Field已經(jīng)存在,則用新值覆蓋原有值。如果Key不存在,則創(chuàng)建新Key,同時設(shè)定參數(shù)中的Field/Value。

命令示例

1. HSET/HGET/HDEL/HEXISTS/HLEN/HSETNX:#在Shell命令行啟動Redis客戶端程序/> redis-cli#給鍵值為myhash的鍵設(shè)置字段為field1,值為stephen。redis 127.0.0.1:6379> hset myhash field1 "stephen"(integer) 1#獲取鍵值為myhash,字段為field1的值。redis 127.0.0.1:6379> hget myhash field1"stephen"#myhash鍵中不存在field2字段,因此返回nil。redis 127.0.0.1:6379> hget myhash field2(nil)#給myhash關(guān)聯(lián)的Hashes值添加一個新的字段field2,其值為liu。redis 127.0.0.1:6379> hset myhash field2 "liu"(integer) 1#獲取myhash鍵的字段數(shù)量。redis 127.0.0.1:6379> hlen myhash(integer) 2#判斷myhash鍵中是否存在字段名為field1的字段,由于存在,返回值為1。redis 127.0.0.1:6379> hexists myhash field1(integer) 1#刪除myhash鍵中字段名為field1的字段,刪除成功返回1。redis 127.0.0.1:6379> hdel myhash field1(integer) 1#再次刪除myhash鍵中字段名為field1的字段,由于上一條命令已經(jīng)將其刪除,因為沒有刪除,返回0。redis 127.0.0.1:6379> hdel myhash field1(integer) 0#判斷myhash鍵中是否存在field1字段,由于上一條命令已經(jīng)將其刪除,因為返回0。redis 127.0.0.1:6379> hexists myhash field1(integer) 0#通過hsetnx命令給myhash添加新字段field1,其值為stephen,因為該字段已經(jīng)被刪除,所以該命令添加成功并返回1。redis 127.0.0.1:6379> hsetnx myhash field1 stephen(integer) 1#由于myhash的field1字段已經(jīng)通過上一條命令添加成功,因為本條命令不做任何操作后返回0。redis 127.0.0.1:6379> hsetnx myhash field1 stephen(integer) 02. HINCRBY:#刪除該鍵,便于后面示例的測試。redis 127.0.0.1:6379> del myhash(integer) 1#準備測試數(shù)據(jù),該myhash的field字段設(shè)定值1。redis 127.0.0.1:6379> hset myhash field 5(integer) 1#給myhash的field字段的值加1,返回加后的結(jié)果。redis 127.0.0.1:6379> hincrby myhash field 1(integer) 6#給myhash的field字段的值加-1,返回加后的結(jié)果。redis 127.0.0.1:6379> hincrby myhash field -1(integer) 5#給myhash的field字段的值加-10,返回加后的結(jié)果。redis 127.0.0.1:6379> hincrby myhash field -10(integer) -5 3. HGETALL/HKEYS/HVALS/HMGET/HMSET:#刪除該鍵,便于后面示例測試。redis 127.0.0.1:6379> del myhash(integer) 1#為該鍵myhash,一次性設(shè)置多個字段,分別是field1 = "hello", field2 = "world"。redis 127.0.0.1:6379> hmset myhash field1 "hello" field2 "world"OK#獲取myhash鍵的多個字段,其中field3并不存在,因為在返回結(jié)果中與該字段對應(yīng)的值為nil。redis 127.0.0.1:6379> hmget myhash field1 field2 field31) "hello"2) "world"3) (nil)#返回myhash鍵的所有字段及其值,從結(jié)果中可以看出,他們是逐對列出的。redis 127.0.0.1:6379> hgetall myhash1) "field1"2) "hello"3) "field2"4) "world"#僅獲取myhash鍵中所有字段的名字。redis 127.0.0.1:6379> hkeys myhash1) "field1"2) "field2"#僅獲取myhash鍵中所有字段的值。redis 127.0.0.1:6379> hvals myhash1) "hello"2) "world"

?

?

Redis 持久化 詳解

?

Redis 提供了兩種持久化的方式:分別是 RDB(Redis DataBase)和 AOF(Append Only File)。
RDB,簡而言之,就是在不同的時間點,將redis存儲的數(shù)據(jù)生成快照并存儲到磁盤等介質(zhì)上;
AOF,則是換了一個角度來實現(xiàn)持久化,那就是將redis執(zhí)行過的所有寫指令記錄下來,在下次redis重新啟動時,只要把這些寫指令從前到后再重復(fù)執(zhí)行一遍,就可以實現(xiàn)數(shù)據(jù)恢復(fù)了。
其實RDB和AOF兩種方式也可以同時使用,在這種情況下,如果redis重啟的話,則會優(yōu)先采用AOF方式來進行數(shù)據(jù)恢復(fù),這是因為AOF方式的數(shù)據(jù)恢復(fù)完整度更高。
如果你沒有數(shù)據(jù)持久化的需求,也完全可以關(guān)閉RDB和AOF方式,這樣的話,redis將變成一個純內(nèi)存數(shù)據(jù)庫,就像memcache一樣。

Redis 提供的持久化機制:

  • RDB 持久化:
    該機制是指在指定的時間間隔內(nèi)將內(nèi)存中的數(shù)據(jù)集快照寫入磁盤。
  • AOF 持久化:
    該機制將以日志的形式記錄服務(wù)器所處理的每一個寫操作,在Redis服務(wù)器啟動之初會讀取該文件來重新構(gòu)建數(shù)據(jù)庫,以保證啟動后數(shù)據(jù)庫中的數(shù)據(jù)是完整的。
  • 無持久化:
    我們可以通過配置的方式禁用Redis服務(wù)器的持久化功能,這樣我們就可以將Redis視為一個功能加強版的memcached了。
  • 同時應(yīng)用 AOF 和 RDB。
  • ?

    RDB 機制的 優(yōu)勢 和 劣勢

    ? ? ? ? RDB方式,是將redis某一時刻的數(shù)據(jù)持久化到磁盤中,是一種快照式的持久化方法。redis在進行數(shù)據(jù)持久化的過程中,會先將數(shù)據(jù)寫入到一個臨時文件中,待持久化過程都結(jié)束了,才會用這個臨時文件替換上次持久化好的文件。正是這種特性,讓我們可以隨時來進行備份,因為快照文件總是完整可用的。
    ? ? ? ? 對于RDB方式,redis會單獨創(chuàng)建(fork)一個子進程來進行持久化,而主進程是不會進行任何IO操作的,這樣就確保了redis極高的性能。如果需要進行大規(guī)模數(shù)據(jù)的恢復(fù),且對于數(shù)據(jù)恢復(fù)的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。
    ? ? ? ? 雖然RDB有不少優(yōu)點,但它的缺點也是不容忽視的。如果你對數(shù)據(jù)的完整性非常敏感,那么RDB方式就不太適合你,因為即使你每5分鐘都持久化一次,當redis故障時,仍然會有近5分鐘的數(shù)據(jù)丟失。所以,redis還提供了另一種持久化方式,那就是AOF。

    ? ? RDB 優(yōu)勢

  • 一旦采用該方式,那么你的整個Redis數(shù)據(jù)庫將只包含一個文件,這對于文件備份而言是非常完美的。比如,你可能打算每個小時歸檔一次最近24小時的數(shù)據(jù),同時還要每天歸檔一次最近30天的數(shù)據(jù)。通過這樣的備份策略,一旦系統(tǒng)出現(xiàn)災(zāi)難性故障,我們可以非常容易的進行恢復(fù)。
  • 對于災(zāi)難恢復(fù)而言,RDB是非常不錯的選擇。因為我們可以非常輕松的將一個單獨的文件壓縮后再轉(zhuǎn)移到其它存儲介質(zhì)上。
  • 性能最大化。對于Redis的服務(wù)進程而言,在開始持久化時,它唯一需要做的只是fork出子進程,之后再由子進程完成這些持久化的工作,這樣就可以極大的避免服務(wù)進程執(zhí)行IO操作了。
  • 相比于AOF機制,如果數(shù)據(jù)集很大,RDB的啟動效率會更高。
  • ? ? RDB 劣勢

  • 如果你想保證數(shù)據(jù)的高可用性,即最大限度的避免數(shù)據(jù)丟失,那么RDB將不是一個很好的選擇。因為系統(tǒng)一旦在定時持久化之前出現(xiàn)宕機現(xiàn)象,此前沒有來得及寫入磁盤的數(shù)據(jù)都將丟失。
  • 由于RDB是通過fork子進程來協(xié)助完成數(shù)據(jù)持久化工作的,因此,如果當數(shù)據(jù)集較大時,可能會導(dǎo)致整個服務(wù)器停止服務(wù)幾百毫秒,甚至是1秒鐘。
  • ?

    AOF 機制的 優(yōu)勢 和 劣勢

    AOF,英文是Append Only File,即只允許追加不允許改寫的文件。
    如前面介紹的,AOF方式是將執(zhí)行過的寫指令記錄下來,在數(shù)據(jù)恢復(fù)時按照從前到后的順序再將指令都執(zhí)行一遍,就這么簡單。
    我們通過配置redis.conf中的appendonly yes就可以打開AOF功能。如果有寫操作(如SET等),redis就會被追加到AOF文件的末尾。
    默認的AOF持久化策略是每秒鐘fsync一次(fsync是指把緩存中的寫指令記錄到磁盤中),因為在這種情況下,redis仍然可以保持很好的處理性能,即使redis故障,也只會丟失最近1秒鐘的數(shù)據(jù)。
    如果在追加日志時,恰好遇到磁盤空間滿、inode滿或斷電等情況導(dǎo)致日志寫入不完整,也沒有關(guān)系,redis提供了redis-check-aof工具,可以用來進行日志修復(fù)。
    因為采用了追加方式,如果不做任何處理的話,AOF文件會變得越來越大,為此,redis提供了AOF文件重寫(rewrite)機制,即當AOF文件的大小超過所設(shè)定的閾值時,redis就會啟動AOF文件的內(nèi)容壓縮,只保留可以恢復(fù)數(shù)據(jù)的最小指令集。舉個例子或許更形象,假如我們調(diào)用了100次INCR指令,在AOF文件中就要存儲100條指令,但這明顯是很低效的,完全可以把這100條指令合并成一條SET指令,這就是重寫機制的原理。
    在進行AOF重寫時,仍然是采用先寫臨時文件,全部完成后再替換的流程,所以斷電、磁盤滿等問題都不會影響AOF文件的可用性,這點大家可以放心。
    AOF方式的另一個好處,我們通過一個“場景再現(xiàn)”來說明。某同學(xué)在操作redis時,不小心執(zhí)行了FLUSHALL,導(dǎo)致redis內(nèi)存中的數(shù)據(jù)全部被清空了,這是很悲劇的事情。不過這也不是世界末日,只要redis配置了AOF持久化方式,且AOF文件還沒有被重寫(rewrite),我們就可以用最快的速度暫停redis并編輯AOF文件,將最后一行的FLUSHALL命令刪除,然后重啟redis,就可以恢復(fù)redis的所有數(shù)據(jù)到FLUSHALL之前的狀態(tài)了。是不是很神奇,這就是AOF持久化方式的好處之一。但是如果AOF文件已經(jīng)被重寫了,那就無法通過這種方法來恢復(fù)數(shù)據(jù)了。
    雖然優(yōu)點多多,但AOF方式也同樣存在缺陷,比如在同樣數(shù)據(jù)規(guī)模的情況下,AOF文件要比RDB文件的體積大。而且,AOF方式的恢復(fù)速度也要慢于RDB方式。
    如果你直接執(zhí)行BGREWRITEAOF命令,那么redis會生成一個全新的AOF文件,其中便包括了可以恢復(fù)現(xiàn)有數(shù)據(jù)的最少的命令集。
    如果運氣比較差,AOF文件出現(xiàn)了被寫壞的情況,也不必過分擔憂,redis并不會貿(mào)然加載這個有問題的AOF文件,而是報錯退出。這時可以通過以下步驟來修復(fù)出錯的文件:
    1.備份被寫壞的AOF文件
    2.運行redis-check-aof –fix進行修復(fù)
    3.用diff -u來看下兩個文件的差異,確認問題點
    4.重啟redis,加載修復(fù)后的AOF文件

    ? ??AOF優(yōu)勢

  • 該機制可以帶來更高的數(shù)據(jù)安全性,即數(shù)據(jù)持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事實上,每秒同步也是異步完成的,其效率也是非常高的,所差的是一旦系統(tǒng)出現(xiàn)宕機現(xiàn)象,那么這一秒鐘之內(nèi)修改的數(shù)據(jù)將會丟失。而每修改同步,我們可以將其視為同步持久化,即每次發(fā)生的數(shù)據(jù)變化都會被立即記錄到磁盤中。可以預(yù)見,這種方式在效率上是最低的。至于無同步,無需多言,我想大家都能正確的理解它。
  • 由于該機制對日志文件的寫入操作采用的是append模式,因此在寫入過程中即使出現(xiàn)宕機現(xiàn)象,也不會破壞日志文件中已經(jīng)存在的內(nèi)容。然而如果我們本次操作只是寫入了一半數(shù)據(jù)就出現(xiàn)了系統(tǒng)崩潰問題,不用擔心,在Redis下一次啟動之前,我們可以通過redis-check-aof工具來幫助我們解決數(shù)據(jù)一致性的問題。
  • 如果日志過大,Redis可以自動啟用rewrite機制。即Redis以append模式不斷的將修改數(shù)據(jù)寫入到老的磁盤文件中,同時Redis還會創(chuàng)建一個新的文件用于記錄此期間有哪些修改命令被執(zhí)行。因此在進行rewrite切換時可以更好的保證數(shù)據(jù)安全性。
  • AOF包含一個格式清晰、易于理解的日志文件用于記錄所有的修改操作。事實上,我們也可以通過該文件完成數(shù)據(jù)的重建。
  • ? ? AOF劣勢

  • 對于相同數(shù)量的數(shù)據(jù)集而言,AOF文件通常要大于RDB文件。
  • 根據(jù)同步策略的不同,AOF在運行效率上往往會慢于RDB。總之,每秒同步策略的效率是比較高的,同步禁用策略的效率和RDB一樣高效。
  • ?

    AOF 重寫

    AOF重寫的內(nèi)部運行原理,我們有必要了解一下。
    在重寫即將開始之際,redis會創(chuàng)建(fork)一個“重寫子進程”,這個子進程會首先讀取現(xiàn)有的AOF文件,并將其包含的指令進行分析壓縮并寫入到一個臨時文件中。
    與此同時,主工作進程會將新接收到的寫指令一邊累積到內(nèi)存緩沖區(qū)中,一邊繼續(xù)寫入到原有的AOF文件中,這樣做是保證原有的AOF文件的可用性,避免在重寫過程中出現(xiàn)意外。
    當“重寫子進程”完成重寫工作后,它會給父進程發(fā)一個信號,父進程收到信號后就會將內(nèi)存中緩存的寫指令追加到新AOF文件中。
    當追加結(jié)束后,redis就會用新AOF文件來代替舊AOF文件,之后再有新的寫指令,就都會追加到新的AOF文件中了。

    ?

    如何選擇 RDB 和 AOF

    對于我們應(yīng)該選擇 RDB 還是 AOF,官方的建議是兩個同時使用。這樣可以提供更可靠的持久化方案。

    ?

    其它

    1. Snapshotting:

    缺省情況下,Redis會將數(shù)據(jù)集的快照dump到dump.rdb文件中。此外,我們也可以通過配置文件來修改Redis服務(wù)器dump快照的頻率,在打開6379.conf文件之后,我們搜索save,可以看到下面的配置信息:
    save 900 1 #在900秒(15分鐘)之后,如果至少有1個key發(fā)生變化,則dump內(nèi)存快照。
    save 300 10 #在300秒(5分鐘)之后,如果至少有10個key發(fā)生變化,則dump內(nèi)存快照。
    save 60 10000 #在60秒(1分鐘)之后,如果至少有10000個key發(fā)生變化,則dump內(nèi)存快照。

    2. Dump快照的機制:

    1). Redis先fork子進程。
    2). 子進程將快照數(shù)據(jù)寫入到臨時RDB文件中。
    3). 當子進程完成數(shù)據(jù)寫入操作后,再用臨時文件替換老的文件。

    3. AOF文件:

    上面已經(jīng)多次講過,RDB的快照定時dump機制無法保證很好的數(shù)據(jù)持久性。如果我們的應(yīng)用確實非常關(guān)注此點,我們可以考慮使用Redis中的AOF機制。對于Redis服務(wù)器而言,其缺省的機制是RDB,如果需要使用AOF,則需要修改配置文件中的以下條目:
    將appendonly no改為appendonly yes
    從現(xiàn)在起,Redis在每一次接收到數(shù)據(jù)修改的命令之后,都會將其追加到AOF文件中。在Redis下一次重新啟動時,需要加載AOF文件中的信息來構(gòu)建最新的數(shù)據(jù)到內(nèi)存中。

    4. AOF的配置:

    在Redis的配置文件中存在三種同步方式,它們分別是:
    appendfsync always #每次有數(shù)據(jù)修改發(fā)生時都會寫入AOF文件。
    appendfsync everysec #每秒鐘同步一次,該策略為AOF的缺省策略。
    appendfsync no #從不同步。高效但是數(shù)據(jù)不會被持久化。

    5. 如何修復(fù)壞損的AOF文件:

    1). 將現(xiàn)有已經(jīng)壞損的AOF文件額外拷貝出來一份。
    2). 執(zhí)行"redis-check-aof --fix <filename>"命令來修復(fù)壞損的AOF文件。
    3). 用修復(fù)后的AOF文件重新啟動Redis服務(wù)器。

    6. Redis的數(shù)據(jù)備份:

    在Redis中我們可以通過copy的方式在線備份正在運行的Redis數(shù)據(jù)文件。這是因為RDB文件一旦被生成之后就不會再被修改。Redis每次都是將最新的數(shù)據(jù)dump到一個臨時文件中,之后在利用rename函數(shù)原子性的將臨時文件改名為原有的數(shù)據(jù)文件名。因此我們可以說,在任意時刻copy數(shù)據(jù)文件都是安全的和一致的。鑒于此,我們就可以通過創(chuàng)建cron job的方式定時備份Redis的數(shù)據(jù)文件,并將備份文件copy到安全的磁盤介質(zhì)中。?

    ?

    ?

    虛擬內(nèi)存介紹

    ?

    簡介

    和大多NoSQL數(shù)據(jù)庫一樣,Redis同樣遵循了Key/Value數(shù)據(jù)存儲模型。在有些情況下,Redis會將Keys/Values保存在內(nèi)存中以提高數(shù)據(jù)查詢和數(shù)據(jù)修改的效率,然而這樣的做法并非總是很好的選擇。鑒于此,我們可以將之進一步優(yōu)化,即盡量在內(nèi)存中只保留Keys的數(shù)據(jù),這樣可以保證數(shù)據(jù)檢索的效率,而Values數(shù)據(jù)在很少使用的時候則可以被換出到磁盤。
    在實際的應(yīng)用中,大約只有10%的Keys屬于相對比較常用的鍵,這樣Redis就可以通過虛存將其余不常用的Keys和Values換出到磁盤上,而一旦這些被換出的Keys或Values需要被讀取時,Redis則將其再次讀回到主內(nèi)存中。

    ?

    應(yīng)用場景

    對于大多數(shù)數(shù)據(jù)庫而言,最為理想的運行方式就是將所有的數(shù)據(jù)都加載到內(nèi)存中,而之后的查詢操作則可以完全基于內(nèi)存數(shù)據(jù)完成。然而在現(xiàn)實中這樣的場景卻并不普遍,更多的情況則是只有部分數(shù)據(jù)可以被加載到內(nèi)存中。
    在Redis中,有一個非常重要的概念,即keys一般不會被交換,所以如果你的數(shù)據(jù)庫中有大量的keys,其中每個key僅僅關(guān)聯(lián)很小的value,那么這種場景就不是非常適合使用虛擬內(nèi)存。如果恰恰相反,數(shù)據(jù)庫中只是包含少量的keys,而每一個key所關(guān)聯(lián)的value卻非常大,那么這種場景對于使用虛存就再合適不過了。
    在實際的應(yīng)用中,為了能讓虛存更為充分的發(fā)揮作用以幫助我們提高系統(tǒng)的運行效率,我們可以將帶有很多較小值的Keys合并為帶有少量較大值的Keys。其中最主要的方法就是將原有的Key/Value模式改為基于Hash的模式,這樣可以讓很多原來的Keys成為Hash中的屬性。

    ?

    配置

    1). 在配置文件中添加以下配置項,以使當前Redis服務(wù)器在啟動時打開虛存功能。

    vm-enabled yes

    2). 在配置文件中設(shè)定Redis最大可用的虛存字節(jié)數(shù)。如果內(nèi)存中的數(shù)據(jù)大于該值,則有部分對象被換出到磁盤中,其中被換出對象所占用內(nèi)存將被釋放,直到已用內(nèi)存小于該值時才停止換出。

    vm-max-memory (bytes)

    Redis的交換規(guī)則是盡量考慮"最老"的數(shù)據(jù),即最長時間沒有使用的數(shù)據(jù)將被換出。如果兩個對象的age相同,那么Value較大的數(shù)據(jù)將先被換出。需要注意的是,Redis不會將Keys交換到磁盤,因此如果僅僅keys的數(shù)據(jù)就已經(jīng)填滿了整個虛存,那么這種數(shù)據(jù)模型將不適合使用虛存機制,或者是將該值設(shè)置的更大,以容納整個Keys的數(shù)據(jù)。在實際的應(yīng)用,如果考慮使用Redis虛擬內(nèi)存,我們應(yīng)盡可能的分配更多的內(nèi)存交給Redis使用,以避免頻繁的換入換出。

    3). 在配置文件中設(shè)定頁的數(shù)量及每一頁所占用的字節(jié)數(shù)。為了將內(nèi)存中的數(shù)據(jù)傳送到磁盤上,我們需要使用交換文件。這些文件與數(shù)據(jù)持久性無關(guān),Redis會在退出前會將它們?nèi)縿h除。由于對交換文件的訪問方式大多為隨機訪問,因此建議將交換文件存儲在固態(tài)磁盤上,這樣可以大大提高系統(tǒng)的運行效率。

    vm-pages 134217728 vm-page-size 32

    在上面的配置中,Redis將交換文件劃分為vm-pages個頁,其中每個頁所占用的字節(jié)為vm-page-size,那么Redis最終可用的交換文件大小為:vm-pages * vm-page-size。由于一個value可以存放在一個或多個頁上,但是一個頁不能持有多個value,鑒于此,我們在設(shè)置vm-page-size時需要充分考慮Redis的該特征。

    4). 在Redis的配置文件中有一個非常重要的配置參數(shù),即:

    vm-max-threads 4

    該參數(shù)表示Redis在對交換文件執(zhí)行IO操作時所應(yīng)用的最大線程數(shù)量。通常而言,我們推薦該值等于主機的CPU cores。如果將該值設(shè)置為0,那么Redis在與交換文件進行IO交互時,將以同步的方式執(zhí)行此操作。
    對于Redis而言,如果操作交換文件是以同步的方式進行,那么當某一客戶端正在訪問交換文件中的數(shù)據(jù)時,其它客戶端如果再試圖訪問交換文件中的數(shù)據(jù),該客戶端的請求就將被掛起,直到之前的操作結(jié)束為止。特別是在相對較慢或較忙的磁盤上讀取較大的數(shù)據(jù)值時,這種阻塞所帶來的影響就更為突兀了。然而同步操作也并非一無是處,事實上,從全局執(zhí)行效率視角來看,同步方式要好于異步方式,畢竟同步方式節(jié)省了線程切換、線程間同步,以及線程拉起等操作產(chǎn)生的額外開銷。特別是當大部分頻繁使用的數(shù)據(jù)都可以直接從主內(nèi)存中讀取時,同步方式的表現(xiàn)將更為優(yōu)異。
    如果你的現(xiàn)實應(yīng)用恰恰相反,即有大量的換入換出操作,同時你的系統(tǒng)又有很多的cores,有鑒于此,你又不希望客戶端在訪問交換文件之前不得不阻塞一小段時間,如果確實是這樣,我想異步方式可能更適合于你的系統(tǒng)。
    至于最終選用哪種配置方式,最好的答案將來自于不斷的實驗和調(diào)優(yōu)。

    ?

    ?

    聊聊主從 ( Master-Slave模式 ) ---?用法

    ?

    Redis 的主從復(fù)制與集群配置實踐:http://lib.csdn.net/article/redis/22518

    主從同步 與 集群管理:http://blog.csdn.net/u012152619/article/details/52854465

    ?

    ? ? ? ? 從數(shù)據(jù)庫是主數(shù)據(jù)庫的備份,當主數(shù)據(jù)庫變化時,從數(shù)據(jù)庫就要同步更新,這些數(shù)據(jù)庫軟件可以設(shè)計更新周期。這是提高信息安全的手段。主從數(shù)據(jù)庫服務(wù)器不在一個地理位置上,當發(fā)生意外時數(shù)據(jù)庫可以保存。

    ? ? ? ? 像MySQL一樣,redis是支持主從同步的,而且也支持一主多從以及多級從結(jié)構(gòu)。

    ? ? ? ? 主從結(jié)構(gòu),一是為了純粹的冗余備份,二是為了提升讀性能,比如很消耗性能的SORT就可以由從服務(wù)器來承擔。

    ? ? ? ? redis的主從同步是異步進行的,這意味著主從同步不會影響主邏輯,也不會降低redis的處理性能。

    ? ? ? ? 主從架構(gòu)中,可以考慮關(guān)閉主服務(wù)器的數(shù)據(jù)持久化功能,只讓從服務(wù)器進行持久化,這樣可以提高主服務(wù)器的處理性能。

    ? ? ? ? 在主從架構(gòu)中,從服務(wù)器通常被設(shè)置為只讀模式,這樣可以避免從服務(wù)器的數(shù)據(jù)被誤修改。但是從服務(wù)器仍然可以接受CONFIG等指令,所以還是不應(yīng)該將從服務(wù)器直接暴露到不安全的網(wǎng)絡(luò)環(huán)境中。如果必須如此,那可以考慮給重要指令進行重命名,來避免命令被外人誤執(zhí)行。

    ?

    ?

    聊聊主從( Master-Slave模式?) ---?同步原理

    ?

    從服務(wù)器會向主服務(wù)器發(fā)出SYNC指令,當主服務(wù)器接到此命令后,就會調(diào)用BGSAVE指令來創(chuàng)建一個子進程專門進行數(shù)據(jù)持久化工作,也就是將主服務(wù)器的數(shù)據(jù)寫入RDB文件中。在數(shù)據(jù)持久化期間,主服務(wù)器將執(zhí)行的寫指令都緩存在內(nèi)存中。
    在BGSAVE指令執(zhí)行完成后,主服務(wù)器會將持久化好的RDB文件發(fā)送給從服務(wù)器,從服務(wù)器接到此文件后會將其存儲到磁盤上,然后再將其讀取到內(nèi)存中。這個動作完成后,主服務(wù)器會將這段時間緩存的寫指令再以redis協(xié)議的格式發(fā)送給從服務(wù)器。
    另外,要說的一點是,即使有多個從服務(wù)器同時發(fā)來SYNC指令,主服務(wù)器也只會執(zhí)行一次BGSAVE,然后把持久化好的RDB文件發(fā)給多個下游。在redis2.8版本之前,如果從服務(wù)器與主服務(wù)器因某些原因斷開連接的話,都會進行一次主從之間的全量的數(shù)據(jù)同步;而在2.8版本之后,redis支持了效率更高的增量同步策略,這大大降低了連接斷開的恢復(fù)成本。
    主服務(wù)器會在內(nèi)存中維護一個緩沖區(qū),緩沖區(qū)中存儲著將要發(fā)給從服務(wù)器的內(nèi)容。從服務(wù)器在與主服務(wù)器出現(xiàn)網(wǎng)絡(luò)瞬斷之后,從服務(wù)器會嘗試再次與主服務(wù)器連接,一旦連接成功,從服務(wù)器就會把“希望同步的主服務(wù)器ID”和“希望請求的數(shù)據(jù)的偏移位置(replication offset)”發(fā)送出去。主服務(wù)器接收到這樣的同步請求后,首先會驗證主服務(wù)器ID是否和自己的ID匹配,其次會檢查“請求的偏移位置”是否存在于自己的緩沖區(qū)中,如果兩者都滿足的話,主服務(wù)器就會向從服務(wù)器發(fā)送增量內(nèi)容。
    增量同步功能,需要服務(wù)器端支持全新的PSYNC指令。這個指令,只有在redis-2.8之后才具有。

    ?

    ?

    主從( Master-Slave模式 )復(fù)制配置實例

    ?

    Redis 的 Replication

    在Redis中配置Master-Slave模式很簡單。讀完下面的內(nèi)容后你也可以輕松做到。先列出一些理論性的知識,后面給出實際操作的案例。

    下面的列表解釋了 Redis Replication的特點和優(yōu)勢。

  • ?同一個Master可以同步多個Slaves。
  • ?Slave同樣可以接受其它Slaves的連接和同步請求,這樣可以有效的分載Master的同步壓力。因此我們可以將Redis的Replication架構(gòu)視為圖結(jié)構(gòu)。
  • ?Master Server是以非阻塞的方式為Slaves提供服務(wù)。所以在Master-Slave同步期間,客戶端仍然可以提交查詢或修改請求。
  • ?Slave Server同樣是以非阻塞的方式完成數(shù)據(jù)同步。在同步期間,如果有客戶端提交查詢請求,Redis則返回同步之前的數(shù)據(jù)。
  • ?為了分載Master的讀操作壓力,Slave服務(wù)器可以為客戶端提供只讀操作的服務(wù),寫服務(wù)仍然必須由Master來完成。即便如此,系統(tǒng)的伸縮性還是得到了很大的提高。
  • ?Master可以將數(shù)據(jù)保存操作交給Slaves完成,從而避免了在Master中要有獨立的進程來完成此操作。
  • ?

    Replication的工作原理

    ? ? ? ? 在Slave啟動并連接到Master之后,它將主動發(fā)送一個SYNC命令。此后Master將啟動后臺存盤進程,同時收集所有接收到的用于修改數(shù)據(jù)集的命令,在后臺進程執(zhí)行完畢后,Master將傳送整個數(shù)據(jù)庫文件到Slave,以完成一次完全同步。而Slave服務(wù)器在接收到數(shù)據(jù)庫文件數(shù)據(jù)之后將其存盤并加載到內(nèi)存中。此后,Master繼續(xù)將所有已經(jīng)收集到的修改命令,和新的修改命令依次傳送給Slaves,Slave將在本次執(zhí)行這些數(shù)據(jù)修改命令,從而達到最終的數(shù)據(jù)同步。
    ? ? ? ? 如果Master和Slave之間的鏈接出現(xiàn)斷連現(xiàn)象,Slave可以自動重連Master,但是在連接成功之后,一次完全同步將被自動執(zhí)行。

    ?

    如何配置Replication

    見如下步驟:

    1). 同時啟動兩個Redis服務(wù)器,可以考慮在同一臺機器上啟動兩個Redis服務(wù)器,分別監(jiān)聽不同的端口,如6379和6380。2). 在Slave服務(wù)器上執(zhí)行一下命令: /> redis-cli -p 6380 #這里我們假設(shè)Slave的端口號是6380redis 127.0.0.1:6380> slaveof 127.0.0.1 6379 #我們假設(shè)Master和Slave在同一臺主機,Master的端口為6379OK

    上面的方式只是保證了在執(zhí)行slaveof命令之后,redis_6380成為了redis_6379的slave,一旦服務(wù)(redis_6380)重新啟動之后,他們之間的復(fù)制關(guān)系將終止。
    如果希望長期保證這兩個服務(wù)器之間的Replication關(guān)系,可以在redis_6380的配置文件中做如下修改:

    /> cd /etc/redis #切換Redis服務(wù)器配置文件所在的目錄。 /> ls 6379.conf 6380.conf /> vi 6380.conf 將 # slaveof <masterip> <masterport> 改為 slaveof 127.0.0.1 6379

    保存退出。這樣就可以保證Redis_6380服務(wù)程序在每次啟動后都會主動建立與Redis_6379的Replication連接了。

    ?

    應(yīng)用示例:

    這里我們假設(shè) Master-Slave 已經(jīng)建立。

    #啟動master服務(wù)器。 [root@Stephen-PC redis]# redis-cli -p 6379 redis 127.0.0.1:6379> #情況Master當前數(shù)據(jù)庫中的所有Keys。 redis 127.0.0.1:6379> flushdb OK #在Master中創(chuàng)建新的Keys作為測試數(shù)據(jù)。 redis 127.0.0.1:6379> set mykey hello OK redis 127.0.0.1:6379> set mykey2 world OK #查看Master中存在哪些Keys。 redis 127.0.0.1:6379> keys * 1) "mykey" 2) "mykey2"#啟動slave服務(wù)器。 [root@Stephen-PC redis]# redis-cli -p 6380 #查看Slave中的Keys是否和Master中一致,從結(jié)果看,他們是相等的。 redis 127.0.0.1:6380> keys * 1) "mykey" 2) "mykey2"#在Master中刪除其中一個測試Key,并查看刪除后的結(jié)果。 redis 127.0.0.1:6379> del mykey2 (integer) 1 redis 127.0.0.1:6379> keys * 1) "mykey"#在Slave中查看是否mykey2也已經(jīng)在Slave中被刪除。 redis 127.0.0.1:6380> keys * 1) "mykey"

    ?

    ?

    聊聊 redis 的 事務(wù)處理

    ?

    眾所周知,事務(wù)是指“一個完整的動作,要么全部執(zhí)行,要么什么也沒有做”。在聊 redis 事務(wù)處理之前,要先和大家介紹四個redis指令,即 MULTI、EXEC、DISCARD、WATCH。這四個指令構(gòu)成了redis 事務(wù)處理的基礎(chǔ)。

    • 1. MULTI 用來組裝一個事務(wù);
    • 2. EXEC 用來執(zhí)行一個事務(wù);
    • 3. DISCARD 用來取消一個事務(wù);
    • 4. WATCH 用來監(jiān)視一些 key,一旦這些 key 在事務(wù)執(zhí)行之前被改變,則取消事務(wù)的執(zhí)行。

    和眾多其它數(shù)據(jù)庫一樣,Redis 作為NoSQL數(shù)據(jù)庫也同樣提供了事務(wù)機制。在Redis中,MULTI / EXEC / DISCARD / WATCH 這四個命令是我們實現(xiàn)事務(wù)的基石。相信對有關(guān)系型數(shù)據(jù)庫開發(fā)經(jīng)驗的開發(fā)者而言這一概念并不陌生,即便如此,我們還是會簡要的列出Redis中事務(wù)的實現(xiàn)特征:
    1). 在事務(wù)中的所有命令都將會被串行化的順序執(zhí)行,事務(wù)執(zhí)行期間,Redis不會再為其它客戶端的請求提供任何服務(wù),從而保證了事物中的所有命令被原子的執(zhí)行。
    2). 和關(guān)系型數(shù)據(jù)庫中的事務(wù)相比,在Redis事務(wù)中如果有某一條命令執(zhí)行失敗,其后的命令仍然會被繼續(xù)執(zhí)行。
    3). 我們可以通過MULTI命令開啟一個事務(wù),有關(guān)系型數(shù)據(jù)庫開發(fā)經(jīng)驗的人可以將其理解為"BEGIN TRANSACTION"語句。在該語句之后執(zhí)行的命令都將被視為事務(wù)之內(nèi)的操作,最后我們可以通過執(zhí)行EXEC/DISCARD命令來提交/回滾該事務(wù)內(nèi)的所有操作。這兩個Redis命令可被視為等同于關(guān)系型數(shù)據(jù)庫中的COMMIT/ROLLBACK語句。
    4). 在事務(wù)開啟之前,如果客戶端與服務(wù)器之間出現(xiàn)通訊故障并導(dǎo)致網(wǎng)絡(luò)斷開,其后所有待執(zhí)行的語句都將不會被服務(wù)器執(zhí)行。然而如果網(wǎng)絡(luò)中斷事件是發(fā)生在客戶端執(zhí)行EXEC命令之后,那么該事務(wù)中的所有命令都會被服務(wù)器執(zhí)行。
    5). 當使用Append-Only模式時,Redis會通過調(diào)用系統(tǒng)函數(shù)write將該事務(wù)內(nèi)的所有寫操作在本次調(diào)用中全部寫入磁盤。然而如果在寫入的過程中出現(xiàn)系統(tǒng)崩潰,如電源故障導(dǎo)致的宕機,那么此時也許只有部分數(shù)據(jù)被寫入到磁盤,而另外一部分數(shù)據(jù)卻已經(jīng)丟失。Redis服務(wù)器會在重新啟動時執(zhí)行一系列必要的一致性檢測,一旦發(fā)現(xiàn)類似問題,就會立即退出并給出相應(yīng)的錯誤提示。此時,我們就要充分利用Redis工具包中提供的redis-check-aof工具,該工具可以幫助我們定位到數(shù)據(jù)不一致的錯誤,并將已經(jīng)寫入的部分數(shù)據(jù)進行回滾。修復(fù)之后我們就可以再次重新啟動Redis服務(wù)器了。

    相關(guān)命令

    MULTI用于標記事務(wù)的開始,其后執(zhí)行的命令都將被存入命令隊列,直到執(zhí)行EXEC時,這些命令才會被原子的執(zhí)行。始終返回OK EXEC執(zhí)行在一個事務(wù)內(nèi)命令隊列中的所有命令,同時將當前連接的狀態(tài)恢復(fù)為正常狀態(tài),即非事務(wù)狀態(tài)。如果在事務(wù)中執(zhí)行了WATCH命令,那么只有當WATCH所監(jiān)控的Keys沒有被修改的前提下,EXEC命令才能執(zhí)行事務(wù)隊列中的所有命令,否則EXEC將放棄當前事務(wù)中的所有命令。 原子性的返回事務(wù)中各條命令的返回結(jié)果。如果在事務(wù)中使用了WATCH,一旦事務(wù)被放棄,EXEC將返回NULL-multi-bulk回復(fù)。 DISCARD回滾事務(wù)隊列中的所有命令,同時再將當前連接的狀態(tài)恢復(fù)為正常狀態(tài),即非事務(wù)狀態(tài)。如果WATCH命令被使用,該命令將UNWATCH所有的Keys。 始終返回OK。 WATCHkey [key ...] 在MULTI命令執(zhí)行之前,可以指定待監(jiān)控的Keys,然而在執(zhí)行EXEC之前,如果被監(jiān)控的Keys發(fā)生修改,EXEC將放棄執(zhí)行該事務(wù)隊列中的所有命令。 始終返回OK。 UNWATCH 取消當前事務(wù)中指定監(jiān)控的Keys,如果執(zhí)行了EXEC或DISCARD命令,則無需再手工執(zhí)行該命令了,因為在此之后,事務(wù)中所有被監(jiān)控的Keys都將自動取消。 始終返回OK。

    命令示例

    1. 事務(wù)被正常執(zhí)行:#在Shell命令行下執(zhí)行Redis的客戶端工具。/> redis-cli#在當前連接上啟動一個新的事務(wù)。redis 127.0.0.1:6379> multiOK#執(zhí)行事務(wù)中的第一條命令,從該命令的返回結(jié)果可以看出,該命令并沒有立即執(zhí)行,而是存于事務(wù)的命令隊列。redis 127.0.0.1:6379> incr t1QUEUED#又執(zhí)行一個新的命令,從結(jié)果可以看出,該命令也被存于事務(wù)的命令隊列。redis 127.0.0.1:6379> incr t2QUEUED#執(zhí)行事務(wù)命令隊列中的所有命令,從結(jié)果可以看出,隊列中命令的結(jié)果得到返回。redis 127.0.0.1:6379> exec1) (integer) 12) (integer) 1 2. 事務(wù)中存在失敗的命令:#開啟一個新的事務(wù)。redis 127.0.0.1:6379> multiOK#設(shè)置鍵a的值為string類型的3。redis 127.0.0.1:6379> set a 3QUEUED#從鍵a所關(guān)聯(lián)的值的頭部彈出元素,由于該值是字符串類型,而lpop命令僅能用于List類型,因此在執(zhí)行exec命令時,該命令將會失敗。redis 127.0.0.1:6379> lpop aQUEUED#再次設(shè)置鍵a的值為字符串4。redis 127.0.0.1:6379> set a 4QUEUED#獲取鍵a的值,以便確認該值是否被事務(wù)中的第二個set命令設(shè)置成功。redis 127.0.0.1:6379> get aQUEUED#從結(jié)果中可以看出,事務(wù)中的第二條命令lpop執(zhí)行失敗,而其后的set和get命令均執(zhí)行成功,這一點是Redis的事務(wù)與關(guān)系型數(shù)據(jù)庫中的事務(wù)之間最為重要的差別。redis 127.0.0.1:6379> exec1) OK2) (error) ERR Operation against a key holding the wrong kind of value3) OK4) "4"3. 回滾事務(wù):#為鍵t2設(shè)置一個事務(wù)執(zhí)行前的值。redis 127.0.0.1:6379> set t2 ttOK#開啟一個事務(wù)。redis 127.0.0.1:6379> multiOK#在事務(wù)內(nèi)為該鍵設(shè)置一個新值。redis 127.0.0.1:6379> set t2 ttnewQUEUED#放棄事務(wù)。redis 127.0.0.1:6379> discardOK#查看鍵t2的值,從結(jié)果中可以看出該鍵的值仍為事務(wù)開始之前的值。redis 127.0.0.1:6379> get t2"tt"4、WATCH命令和基于CAS的樂觀鎖:在Redis的事務(wù)中,WATCH命令可用于提供CAS(check-and-set)功能。假設(shè)我們通過WATCH命令在事務(wù)執(zhí)行之前監(jiān)控了多個Keys,倘若在WATCH之后有任何Key的值發(fā)生了變化,EXEC命令執(zhí)行的事務(wù)都將被放棄,同時返回Null multi-bulk應(yīng)答以通知調(diào)用者事務(wù)執(zhí)行失敗。例如,我們再次假設(shè)Redis中并未提供incr命令來完成鍵值的原子性遞增,如果要實現(xiàn)該功能,我們只能自行編寫相應(yīng)的代碼。其偽碼如下:val = GET mykeyval = val + 1SET mykey $val以上代碼只有在單連接的情況下才可以保證執(zhí)行結(jié)果是正確的,因為如果在同一時刻有多個客戶端在同時執(zhí)行該段代碼,那么就會出現(xiàn)多線程程序中經(jīng)常出現(xiàn)的一種錯誤場景--競態(tài)爭用(race condition)。比如,客戶端A和B都在同一時刻讀取了mykey的原有值,假設(shè)該值為10,此后兩個客戶端又均將該值加一后set回Redis服務(wù)器,這樣就會導(dǎo)致mykey的結(jié)果為11,而不是我們認為的12。為了解決類似的問題,我們需要借助WATCH命令的幫助,見如下代碼:WATCH mykeyval = GET mykeyval = val + 1MULTISET mykey $valEXEC和此前代碼不同的是,新代碼在獲取mykey的值之前先通過WATCH命令監(jiān)控了該鍵,此后又將set命令包圍在事務(wù)中,這樣就可以有效的保證每個連接在執(zhí)行EXEC之前,如果當前連接獲取的mykey的值被其它連接的客戶端修改,那么當前連接的EXEC命令將執(zhí)行失敗。這樣調(diào)用者在判斷返回值后就可以獲悉val是否被重新設(shè)置成功。

    紙上得來終覺淺,我們來看一個 MULTI 和 EXEC 的例子:

    redis> MULTI //標記事務(wù)開始 OK redis> INCR user_id //多條命令按順序入隊 QUEUED redis> INCR user_id QUEUED redis> INCR user_id QUEUED redis> PING QUEUED redis> EXEC //執(zhí)行 1) (integer) 1 2) (integer) 2 3) (integer) 3 4) PONG

    在上面的例子中,我們看到了QUEUED的字樣,這表示我們在用MULTI組裝事務(wù)時,每一個命令都會進入到內(nèi)存隊列中緩存起來,如果出現(xiàn)QUEUED則表示我們這個命令成功插入了緩存隊列,在將來執(zhí)行EXEC時,這些被QUEUED的命令都會被組裝成一個事務(wù)來執(zhí)行。
    對于事務(wù)的執(zhí)行來說,如果redis開啟了AOF持久化的話,那么一旦事務(wù)被成功執(zhí)行,事務(wù)中的命令就會通過write命令一次性寫到磁盤中去,如果在向磁盤中寫的過程中恰好出現(xiàn)斷電、硬件故障等問題,那么就可能出現(xiàn)只有部分命令進行了AOF持久化,這時AOF文件就會出現(xiàn)不完整的情況,這時,我們可以使用redis-check-aof工具來修復(fù)這一問題,這個工具會將AOF文件中不完整的信息移除,確保AOF文件完整可用。
    有關(guān)事務(wù),大家經(jīng)常會遇到的是兩類錯誤:
    1.調(diào)用EXEC之前的錯誤
    2.調(diào)用EXEC之后的錯誤
    “調(diào)用EXEC之前的錯誤”,有可能是由于語法有誤導(dǎo)致的,也可能時由于內(nèi)存不足導(dǎo)致的。只要出現(xiàn)某個命令無法成功寫入緩沖隊列的情況,redis都會進行記錄,在客戶端調(diào)用EXEC時,redis會拒絕執(zhí)行這一事務(wù)。(這時2.6.5版本之后的策略。在2.6.5之前的版本中,redis會忽略那些入隊失敗的命令,只執(zhí)行那些入隊成功的命令)。我們來看一個這樣的例子:

    127.0.0.1:6379> multi OK 127.0.0.1:6379> haha //一個明顯錯誤的指令 (error) ERR unknown command 'haha' 127.0.0.1:6379> ping QUEUED 127.0.0.1:6379> exec //redis無情的拒絕了事務(wù)的執(zhí)行,原因是“之前出現(xiàn)了錯誤” (error) EXECABORT Transaction discarded because of previous errors.

    而對于“調(diào)用EXEC之后的錯誤”,redis則采取了完全不同的策略,即redis不會理睬這些錯誤,而是繼續(xù)向下執(zhí)行事務(wù)中的其他命令。這是因為,對于應(yīng)用層面的錯誤,并不是redis自身需要考慮和處理的問題,所以一個事務(wù)中如果某一條命令執(zhí)行失敗,并不會影響接下來的其他命令的執(zhí)行。我們也來看一個例子:

    127.0.0.1:6379> multi OK 127.0.0.1:6379> set age 23 QUEUED //age不是集合,所以如下是一條明顯錯誤的指令 127.0.0.1:6379> sadd age 15 QUEUED 127.0.0.1:6379> set age 29 QUEUED 127.0.0.1:6379> exec //執(zhí)行事務(wù)時,redis不會理睬第2條指令執(zhí)行錯誤 1) OK 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value 3) OK 127.0.0.1:6379> get age "29" //可以看出第3條指令被成功執(zhí)行了

    好了,我們來說說最后一個指令“WATCH”,這是一個很好用的指令,它可以幫我們實現(xiàn)類似于“樂觀鎖”的效果,即CAS(check and set)。
    WATCH本身的作用是“監(jiān)視key是否被改動過”,而且支持同時監(jiān)視多個key,只要還沒真正觸發(fā)事務(wù),WATCH都會盡職盡責的監(jiān)視,一旦發(fā)現(xiàn)某個key被修改了,在執(zhí)行EXEC時就會返回nil,表示事務(wù)無法觸發(fā)。

    127.0.0.1:6379> set age 23 OK 127.0.0.1:6379> watch age //開始監(jiān)視age OK 127.0.0.1:6379> set age 24 //在EXEC之前,age的值被修改了 OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> set age 25 QUEUED 127.0.0.1:6379> get age QUEUED 127.0.0.1:6379> exec //觸發(fā)EXEC (nil) //事務(wù)無法被執(zhí)行

    ?

    ?

    教你看懂 redis 配置 ---?簡介

    ?

    我們可以在啟動redis-server時指定應(yīng)該加載的配置文件,方法如下:

    $ ./redis-server /path/to/redis.conf

    接下來,我們就來講解下redis配置文件的各個配置項的含義,注意,本文是基于redis-2.8.4版本進行講解的。redis官方提供的redis.conf文件,足有700+行,其中100多行為有效配置行,另外的600多行為注釋說明。在配置文件的開頭部分,首先明確了一些度量單位:

    # 1k => 1000 bytes # 1kb => 1024 bytes # 1m => 1000000 bytes # 1mb => 1024*1024 bytes # 1g => 1000000000 bytes # 1gb => 1024*1024*1024 bytes

    可以看出,redis配置中對單位的大小寫不敏感,1GB、1Gb和1gB都是相同的。由此也說明,redis只支持bytes,不支持bit單位。redis支持“主配置文件中引入外部配置文件”,很像C/C++中的include指令,比如:

    include /path/to/other.conf

    如果你看過redis的配置文件,會發(fā)現(xiàn)還是很有條理的。redis配置文件被分成了幾大塊區(qū)域,它們分別是:

    1.通用(general) 2.快照(snapshotting) 3.復(fù)制(replication) 4.安全(security) 5.限制(limits) 6.追加模式(append only mode) 7.LUA腳本(lua scripting) 8.慢日志(slow log) 9.事件通知(event notification)

    下面我們就來逐一講解。

    ?

    教你看懂 redis 配置 --- 通用

    默認情況下,redis并不是以daemon形式來運行的。通過daemonize配置項可以控制redis的運行形式,如果改為yes,那么redis就會以daemon形式運行:

    daemonize no

    當以daemon形式運行時,redis會生成一個pid文件,默認會生成在/var/run/redis.pid。當然,你可以通過pidfile來指定pid文件生成的位置,比如:

    pidfile /path/to/redis.pid

    默認情況下,redis會響應(yīng)本機所有可用網(wǎng)卡的連接請求。當然,redis允許你通過bind配置項來指定要綁定的IP,比如:

    bind 192.168.1.2 10.8.4.2

    redis的默認服務(wù)端口是6379,你可以通過port配置項來修改。如果端口設(shè)置為0的話,redis便不會監(jiān)聽端口了。

    port 6379

    有些同學(xué)會問“如果redis不監(jiān)聽端口,還怎么與外界通信呢”,其實redis還支持通過unix socket方式來接收請求。可以通過unixsocket配置項來指定unix socket文件的路徑,并通過unixsocketperm來指定文件的權(quán)限。

    unixsocket /tmp/redis.sock unixsocketperm 755

    當一個redis-client一直沒有請求發(fā)向server端,那么server端有權(quán)主動關(guān)閉這個連接,可以通過timeout來設(shè)置“空閑超時時限”,0表示永不關(guān)閉。

    timeout 0

    TCP連接保活策略,可以通過tcp-keepalive配置項來進行設(shè)置,單位為秒,假如設(shè)置為60秒,則server端會每60秒向連接空閑的客戶端發(fā)起一次ACK請求,以檢查客戶端是否已經(jīng)掛掉,對于無響應(yīng)的客戶端則會關(guān)閉其連接。所以關(guān)閉一個連接最長需要120秒的時間。如果設(shè)置為0,則不會進行保活檢測。

    tcp-keepalive 0

    redis支持通過loglevel配置項設(shè)置日志等級,共分四級,即debug、verbose、notice、warning。

    loglevel notice

    redis也支持通過logfile配置項來設(shè)置日志文件的生成位置。如果設(shè)置為空字符串,則redis會將日志輸出到標準輸出。假如你在daemon情況下將日志設(shè)置為輸出到標準輸出,則日志會被寫到/dev/null中。

    logfile ""

    如果希望日志打印到syslog中,也很容易,通過syslog-enabled來控制。另外,syslog-ident還可以讓你指定syslog里的日志標志,比如:

    syslog-ident redis

    而且還支持指定syslog設(shè)備,值可以是USER或LOCAL0-LOCAL7。具體可以參考syslog服務(wù)本身的用法。

    syslog-facility local0

    對于redis來說,可以設(shè)置其數(shù)據(jù)庫的總數(shù)量,假如你希望一個redis包含16個數(shù)據(jù)庫,那么設(shè)置如下:

    databases 16

    這16個數(shù)據(jù)庫的編號將是0到15。默認的數(shù)據(jù)庫是編號為0的數(shù)據(jù)庫。用戶可以使用select <DBid>來選擇相應(yīng)的數(shù)據(jù)庫。

    ?

    教你看懂 redis 配置 ---?快照

    快照,主要涉及的是redis的RDB持久化相關(guān)的配置,我們來一起看一看。我們可以用如下的指令來讓數(shù)據(jù)保存到磁盤上,即控制RDB快照功能:

    save <seconds> <changes>

    舉例來說:

    save 900 1 //表示每15分鐘且至少有1個key改變,就觸發(fā)一次持久化 save 300 10 //表示每5分鐘且至少有10個key改變,就觸發(fā)一次持久化 save 60 10000 //表示每60秒至少有10000個key改變,就觸發(fā)一次持久化

    如果你想禁用RDB持久化的策略,只要不設(shè)置任何save指令就可以,或者給save傳入一個空字符串參數(shù)也可以達到相同效果,就像這樣:

    save ""

    如果用戶開啟了RDB快照功能,那么在redis持久化數(shù)據(jù)到磁盤時如果出現(xiàn)失敗,默認情況下,redis會停止接受所有的寫請求。這樣做的好處在于可以讓用戶很明確的知道內(nèi)存中的數(shù)據(jù)和磁盤上的數(shù)據(jù)已經(jīng)存在不一致了。如果redis不顧這種不一致,一意孤行的繼續(xù)接收寫請求,就可能會引起一些災(zāi)難性的后果。
    如果下一次RDB持久化成功,redis會自動恢復(fù)接受寫請求。
    當然,如果你不在乎這種數(shù)據(jù)不一致或者有其他的手段發(fā)現(xiàn)和控制這種不一致的話,你完全可以關(guān)閉這個功能,以便在快照寫入失敗時,也能確保redis繼續(xù)接受新的寫請求。配置項如下:

    stop-writes-on-bgsave-error yes

    對于存儲到磁盤中的快照,可以設(shè)置是否進行壓縮存儲。如果是的話,redis會采用LZF算法進行壓縮。如果你不想消耗CPU來進行壓縮的話,可以設(shè)置為關(guān)閉此功能,但是存儲在磁盤上的快照會比較大。

    rdbcompression yes

    在存儲快照后,我們還可以讓redis使用CRC64算法來進行數(shù)據(jù)校驗,但是這樣做會增加大約10%的性能消耗,如果你希望獲取到最大的性能提升,可以關(guān)閉此功能。

    rdbchecksum yes

    我們還可以設(shè)置快照文件的名稱,默認是這樣配置的:

    dbfilename dump.rdb

    最后,你還可以設(shè)置這個快照文件存放的路徑。比如默認設(shè)置就是當前文件夾:

    dir ./

    ?

    教你看懂 redis 配置 ---?復(fù)制

    redis提供了主從同步功能。
    通過slaveof配置項可以控制某一個redis作為另一個redis的從服務(wù)器,通過指定IP和端口來定位到主redis的位置。一般情況下,我們會建議用戶為從redis設(shè)置一個不同頻率的快照持久化的周期,或者為從redis配置一個不同的服務(wù)端口等等。

    slaveof <masterip> <masterport>

    如果主redis設(shè)置了驗證密碼的話(使用requirepass來設(shè)置),則在從redis的配置中要使用masterauth來設(shè)置校驗密碼,否則的話,主redis會拒絕從redis的訪問請求。

    masterauth <master-password>

    當從redis失去了與主redis的連接,或者主從同步正在進行中時,redis該如何處理外部發(fā)來的訪問請求呢?這里,從redis可以有兩種選擇:
    第一種選擇:如果slave-serve-stale-data設(shè)置為yes(默認),則從redis仍會繼續(xù)響應(yīng)客戶端的讀寫請求。
    第二種選擇:如果slave-serve-stale-data設(shè)置為no,則從redis會對客戶端的請求返回“SYNC with master in progress”,當然也有例外,當客戶端發(fā)來INFO請求和SLAVEOF請求,從redis還是會進行處理。
    你可以控制一個從redis是否可以接受寫請求。將數(shù)據(jù)直接寫入從redis,一般只適用于那些生命周期非常短的數(shù)據(jù),因為在主從同步時,這些臨時數(shù)據(jù)就會被清理掉。自從redis2.6版本之后,默認從redis為只讀。

    slave-read-only yes

    只讀的從redis并不適合直接暴露給不可信的客戶端。為了盡量降低風(fēng)險,可以使用rename-command指令來將一些可能有破壞力的命令重命名,避免外部直接調(diào)用。比如:

    rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52

    從redis會周期性的向主redis發(fā)出PING包。你可以通過repl_ping_slave_period指令來控制其周期。默認是10秒。

    repl-ping-slave-period 10

    在主從同步時,可能在這些情況下會有超時發(fā)生:

    1.以從redis的角度來看,當有大規(guī)模IO傳輸時。 2.以從redis的角度來看,當數(shù)據(jù)傳輸或PING時,主redis超時 3.以主redis的角度來看,在回復(fù)從redis的PING時,從redis超時

    用戶可以設(shè)置上述超時的時限,不過要確保這個時限比repl-ping-slave-period的值要大,否則每次主redis都會認為從redis超時。

    repl-timeout 60

    我們可以控制在主從同步時是否禁用TCP_NODELAY。如果開啟TCP_NODELAY,那么主redis會使用更少的TCP包和更少的帶寬來向從redis傳輸數(shù)據(jù)。但是這可能會增加一些同步的延遲,大概會達到40毫秒左右。如果你關(guān)閉了TCP_NODELAY,那么數(shù)據(jù)同步的延遲時間會降低,但是會消耗更多的帶寬。(如果你不了解TCP_NODELAY,可以到這里來科普一下)。

    repl-disable-tcp-nodelay no

    我們還可以設(shè)置同步隊列長度。隊列長度(backlog)是主redis中的一個緩沖區(qū),在與從redis斷開連接期間,主redis會用這個緩沖區(qū)來緩存應(yīng)該發(fā)給從redis的數(shù)據(jù)。這樣的話,當從redis重新連接上之后,就不必重新全量同步數(shù)據(jù),只需要同步這部分增量數(shù)據(jù)即可。

    repl-backlog-size 1mb

    如果主redis等了一段時間之后,還是無法連接到從redis,那么緩沖隊列中的數(shù)據(jù)將被清理掉。我們可以設(shè)置主redis要等待的時間長度。如果設(shè)置為0,則表示永遠不清理。默認是1個小時。

    repl-backlog-ttl 3600

    我們可以給眾多的從redis設(shè)置優(yōu)先級,在主redis持續(xù)工作不正常的情況,優(yōu)先級高的從redis將會升級為主redis。而編號越小,優(yōu)先級越高。比如一個主redis有三個從redis,優(yōu)先級編號分別為10、100、25,那么編號為10的從redis將會被首先選中升級為主redis。當優(yōu)先級被設(shè)置為0時,這個從redis將永遠也不會被選中。默認的優(yōu)先級為100。

    slave-priority 100

    假如主redis發(fā)現(xiàn)有超過M個從redis的連接延時大于N秒,那么主redis就停止接受外來的寫請求。這是因為從redis一般會每秒鐘都向主redis發(fā)出PING,而主redis會記錄每一個從redis最近一次發(fā)來PING的時間點,所以主redis能夠了解每一個從redis的運行情況。

    min-slaves-to-write 3 min-slaves-max-lag 10

    上面這個例子表示,假如有大于等于3個從redis的連接延遲大于10秒,那么主redis就不再接受外部的寫請求。上述兩個配置中有一個被置為0,則這個特性將被關(guān)閉。默認情況下min-slaves-to-write為0,而min-slaves-max-lag為10。

    ?

    教你看懂redis配置 ---?安全

    我們可以要求redis客戶端在向redis-server發(fā)送請求之前,先進行密碼驗證。當你的redis-server處于一個不太可信的網(wǎng)絡(luò)環(huán)境中時,相信你會用上這個功能。由于redis性能非常高,所以每秒鐘可以完成多達15萬次的密碼嘗試,所以你最好設(shè)置一個足夠復(fù)雜的密碼,否則很容易被黑客破解。

    requirepass zhimakaimen

    這里我們通過requirepass將密碼設(shè)置成“芝麻開門”。
    redis允許我們對redis指令進行更名,比如將一些比較危險的命令改個名字,避免被誤執(zhí)行。比如可以把CONFIG命令改成一個很復(fù)雜的名字,這樣可以避免外部的調(diào)用,同時還可以滿足內(nèi)部調(diào)用的需要:

    rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c89

    我們甚至可以禁用掉CONFIG命令,那就是把CONFIG的名字改成一個空字符串:

    rename-command CONFIG ""

    但需要注意的是,如果你使用AOF方式進行數(shù)據(jù)持久化,或者需要與從redis進行通信,那么更改指令的名字可能會引起一些問題。

    ?

    教你看懂redis配置 --- 限制

    我們可以設(shè)置redis同時可以與多少個客戶端進行連接。默認情況下為10000個客戶端。當你無法設(shè)置進程文件句柄限制時,redis會設(shè)置為當前的文件句柄限制值減去32,因為redis會為自身內(nèi)部處理邏輯留一些句柄出來。
    如果達到了此限制,redis則會拒絕新的連接請求,并且向這些連接請求方發(fā)出“max number of clients reached”以作回應(yīng)。

    maxclients 10000

    我們甚至可以設(shè)置redis可以使用的內(nèi)存量。一旦到達內(nèi)存使用上限,redis將會試圖移除內(nèi)部數(shù)據(jù),移除規(guī)則可以通過maxmemory-policy來指定。
    如果redis無法根據(jù)移除規(guī)則來移除內(nèi)存中的數(shù)據(jù),或者我們設(shè)置了“不允許移除”,那么redis則會針對那些需要申請內(nèi)存的指令返回錯誤信息,比如SET、LPUSH等。但是對于無內(nèi)存申請的指令,仍然會正常響應(yīng),比如GET等。

    maxmemory <bytes>

    需要注意的一點是,如果你的redis是主redis(說明你的redis有從redis),那么在設(shè)置內(nèi)存使用上限時,需要在系統(tǒng)中留出一些內(nèi)存空間給同步隊列緩存,只有在你設(shè)置的是“不移除”的情況下,才不用考慮這個因素。
    對于內(nèi)存移除規(guī)則來說,redis提供了多達6種的移除規(guī)則。他們是:

    1.volatile-lru:使用LRU算法移除過期集合中的key 2.allkeys-lru:使用LRU算法移除key 3.volatile-random:在過期集合中移除隨機的key 4.allkeys-random:移除隨機的key 5.volatile-ttl:移除那些TTL值最小的key,即那些最近才過期的key。 6.noeviction:不進行移除。針對寫操作,只是返回錯誤信息。

    無論使用上述哪一種移除規(guī)則,如果沒有合適的key可以移除的話,redis都會針對寫請求返回錯誤信息。

    maxmemory-policy volatile-lru

    LRU算法和最小TTL算法都并非是精確的算法,而是估算值。所以你可以設(shè)置樣本的大小。假如redis默認會檢查三個key并選擇其中LRU的那個,那么你可以改變這個key樣本的數(shù)量。

    maxmemory-samples 3

    最后,我們補充一個信息,那就是到目前版本(2.8.4)為止,redis支持的寫指令包括了如下這些:

    set setnx setex append incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby getset mset msetnx exec sort

    ?

    教你看懂redis配置 ---?追加模式

    默認情況下,redis會異步的將數(shù)據(jù)持久化到磁盤。這種模式在大部分應(yīng)用程序中已被驗證是很有效的,但是在一些問題發(fā)生時,比如斷電,則這種機制可能會導(dǎo)致數(shù)分鐘的寫請求丟失。
    如博文上半部分中介紹的,追加文件(Append Only File)是一種更好的保持數(shù)據(jù)一致性的方式。即使當服務(wù)器斷電時,也僅會有1秒鐘的寫請求丟失,當redis進程出現(xiàn)問題且操作系統(tǒng)運行正常時,甚至只會丟失一條寫請求。

    我們建議大家,AOF機制和RDB機制可以同時使用,不會有任何沖突。對于如何保持數(shù)據(jù)一致性的討論,請參見:https://redis.io/topics/persistence

    appendonly no

    我們還可以設(shè)置aof文件的名稱:

    appendfilename "appendonly.aof"

    fsync()調(diào)用,用來告訴操作系統(tǒng)立即將緩存的指令寫入磁盤。一些操作系統(tǒng)會“立即”進行,而另外一些操作系統(tǒng)則會“盡快”進行。
    redis支持三種不同的模式:

    1.no:不調(diào)用fsync()。而是讓操作系統(tǒng)自行決定sync的時間。這種模式下,redis的性能會最快。 2.always:在每次寫請求后都調(diào)用fsync()。這種模式下,redis會相對較慢,但數(shù)據(jù)最安全。 3.everysec:每秒鐘調(diào)用一次fsync()。這是性能和安全的折衷。

    默認情況下為everysec。有關(guān)數(shù)據(jù)一致性的揭秘,可以參考:http://oldblog.antirez.com/post/redis-persistence-demystified.html

    ?

    appendfsync everysec

    當fsync方式設(shè)置為always或everysec時,如果后臺持久化進程需要執(zhí)行一個很大的磁盤IO操作,那么redis可能會在fsync()調(diào)用時卡住。目前尚未修復(fù)這個問題,這是因為即使我們在另一個新的線程中去執(zhí)行fsync(),也會阻塞住同步寫調(diào)用。
    為了緩解這個問題,我們可以使用下面的配置項,這樣的話,當BGSAVE或BGWRITEAOF運行時,fsync()在主進程中的調(diào)用會被阻止。這意味著當另一路進程正在對AOF文件進行重構(gòu)時,redis的持久化功能就失效了,就好像我們設(shè)置了“appendsync none”一樣。如果你的redis有時延問題,那么請將下面的選項設(shè)置為yes。否則請保持no,因為這是保證數(shù)據(jù)完整性的最安全的選擇。

    no-appendfsync-on-rewrite no

    我們允許redis自動重寫aof。當aof增長到一定規(guī)模時,redis會隱式調(diào)用BGREWRITEAOF來重寫log文件,以縮減文件體積。
    redis是這樣工作的:redis會記錄上次重寫時的aof大小。假如redis自啟動至今還沒有進行過重寫,那么啟動時aof文件的大小會被作為基準值。這個基準值會和當前的aof大小進行比較。如果當前aof大小超出所設(shè)置的增長比例,則會觸發(fā)重寫。另外,你還需要設(shè)置一個最小大小,是為了防止在aof很小時就觸發(fā)重寫。

    auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb

    如果設(shè)置auto-aof-rewrite-percentage為0,則會關(guān)閉此重寫功能。

    ?

    教你看懂 redis 配置? ---?LUA腳本

    lua腳本的最大運行時間是需要被嚴格限制的,要注意單位是毫秒:

    lua-time-limit 5000

    如果此值設(shè)置為0或負數(shù),則既不會有報錯也不會有時間限制。

    ?

    教你看懂redis配置 ---?慢日志

    redis慢日志是指一個系統(tǒng)進行日志查詢超過了指定的時長。這個時長不包括IO操作,比如與客戶端的交互、發(fā)送響應(yīng)內(nèi)容等,而僅包括實際執(zhí)行查詢命令的時間。
    針對慢日志,你可以設(shè)置兩個參數(shù),一個是執(zhí)行時長,單位是微秒,另一個是慢日志的長度。當一個新的命令被寫入日志時,最老的一條會從命令日志隊列中被移除。
    單位是微秒,即1000000表示一秒。負數(shù)則會禁用慢日志功能,而0則表示強制記錄每一個命令。

    slowlog-log-slower-than 10000

    慢日志最大長度,可以隨便填寫數(shù)值,沒有上限,但要注意它會消耗內(nèi)存。你可以使用SLOWLOG RESET來重設(shè)這個值。

    slowlog-max-len 128

    ?

    教你看懂 redis 配置 ---?事件通知

    redis可以向客戶端通知某些事件的發(fā)生。這個特性的具體解釋可以參見:https://redis.io/topics/keyspace-events

    ?

    教你看懂redis配置 ---?高級配置

    有關(guān)哈希數(shù)據(jù)結(jié)構(gòu)的一些配置項:

    hash-max-ziplist-entries 512 hash-max-ziplist-value 64

    有關(guān)列表數(shù)據(jù)結(jié)構(gòu)的一些配置項:

    list-max-ziplist-entries 512 list-max-ziplist-value 64

    有關(guān)集合數(shù)據(jù)結(jié)構(gòu)的配置項:

    set-max-intset-entries 512

    有關(guān)有序集合數(shù)據(jù)結(jié)構(gòu)的配置項:

    zset-max-ziplist-entries 128 zset-max-ziplist-value 64

    關(guān)于是否需要再哈希的配置項:

    activerehashing yes

    關(guān)于客戶端輸出緩沖的控制項:

    client-output-buffer-limit normal 0 0 0 client-output-buffer-limit slave 256mb 64mb 60 client-output-buffer-limit pubsub 32mb 8mb 60

    有關(guān)頻率的配置項:

    hz 10

    有關(guān)重寫aof的配置項

    aof-rewrite-incremental-fsync yes

    至此,redis的入門內(nèi)容就結(jié)束了,內(nèi)容實在不少,但相對來說都很基礎(chǔ),本文沒有涉及redis集群、redis工作原理、redis源碼、redis相關(guān)LIB庫等內(nèi)容,后續(xù)會陸續(xù)奉獻,大家敬請期待:)

    ?

    ?

    ?

    ?

    ?

    總結(jié)

    以上是生活随笔為你收集整理的Redis 数据库入门教程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。