日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

Redis 数据库入门教程

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

?

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

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

Redis 設(shè)計(jì)與實(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ā)送多個(gè)命令,節(jié)省往返時(shí)間。
  • Redis 發(fā)布/訂閱(Pub/Sub):redis是一個(gè)快速、穩(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允許為每一個(gè)key設(shè)置不同的過期時(shí)間,當(dāng)它們到期時(shí)將自動(dòng)從服務(wù)器上刪除。
  • 將Redis當(dāng)做使用LRU算法的緩存來使用:如何配置并且將Redis當(dāng)做緩存來使用,通過限制內(nèi)存及自動(dòng)回收鍵。
  • Redis 事務(wù):將一組命令放在同一個(gè)事務(wù)中進(jìn)行處理。
  • 大量插入數(shù)據(jù):如何在短時(shí)間里向Redis寫入大量數(shù)據(jù)。
  • 從文件中批量插入數(shù)據(jù):將文件中的指令批量執(zhí)行。
  • 分區(qū)(Partitioning):如何將你的數(shù)據(jù)分布在多個(gè)Redis里面。
  • 分布式鎖(Distributed locks):用Redis實(shí)現(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官方的高可用性解決方案。目前工作進(jìn)展情況(beta階段,積極發(fā)展),已經(jīng)可用。
  • 延遲監(jiān)控(Latency monitoring):redis集成的延遲監(jiān)控和報(bào)告功能對于為低延遲應(yīng)用場景優(yōu)化redis很有幫助。
  • 基準(zhǔn)(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ī)范:進(jìn)階版的Redis集群使用規(guī)范。

教程 & FAQ

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

?

?

前言

?

? ? ? ? NoSQL數(shù)據(jù)庫?(?非關(guān)系型數(shù)據(jù)庫?) 一度成為高并發(fā)、海量數(shù)據(jù)存儲解決方案的代名詞,與之相應(yīng)的產(chǎn)品也呈現(xiàn)出雨后春筍般的生機(jī)。然而在眾多產(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í)時(shí)數(shù)據(jù)方面具有極高的可用價(jià)值。然而需要指出的是,該產(chǎn)品的 Licence 為 GPL,這就意味著它并不是在所有情況下都是免費(fèi)使用的。
  • 2). 對 MongoDB 的定義為 Oriented-Document 數(shù)據(jù)庫服務(wù)器,和 BerkeleyDB 不同的是該數(shù)據(jù)庫可以像其他關(guān)系型數(shù)據(jù)庫服務(wù)器那樣獨(dú)立的運(yùn)行并提供相關(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ù)程序獨(dú)立運(yùn)行于自己的服務(wù)器主機(jī)。在很多時(shí)候,人們只是將 Redi s視為 Key/Value 數(shù)據(jù)庫服務(wù)器,然而事實(shí)并非如此,在目前的版本中,Redis 除了Key/Value之外還支持 List、Hash、Set 和 Ordered Set 等數(shù)據(jù)結(jié)構(gòu),因此它的用途也更為寬泛。對于此種誤解,Redis官網(wǎng)也進(jìn)行了相應(yīng)的澄清。和以上兩種產(chǎn)品不同的是,Redis 的 License 是 Apache License,就目前而言,它是完全免費(fèi)。
  • 4). memcached,數(shù)據(jù)緩存服務(wù)器。為什么在這里要給出該產(chǎn)品的解釋呢?很簡單,因?yàn)楣P者認(rèn)為它在使用方式上和 Redis 最為相似。畢竟這是一篇關(guān)于 Redis 的技術(shù)系列博客,有鑒于此,我們將簡要的對比一下這兩個(gè)產(chǎn)品。首先說一下它們之間的最大區(qū)別,memcached 只是提供了數(shù)據(jù)緩存服務(wù),一旦服務(wù)器宕機(jī),之前在內(nèi)存中緩存的數(shù)據(jù)也將全部消失,因此可以看出 memcached 沒有提供任何形式的數(shù)據(jù)持久化功能,而 Redis 則提供了這樣的功能。再有就是 Redis 提供了更為豐富的數(shù)據(jù)存儲結(jié)構(gòu),如 Hash 和 Set。至于它們的相同點(diǎn),主要有兩個(gè),一是完全免費(fèi),再有就是它們的提供的命令形式極為接近。

?

?

redis 概述

?

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

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

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

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

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

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

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

?

誰在使用 redis

Blizzard、digg、stackoverflow、github、flickr …

?

?

Redis 管理工具

?

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

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

?

redis-server

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

?

Redis 提示說:

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

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

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

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

?

redis-cli

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

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

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

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

?

redis-benchmark

?

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

?

redis-check-aof

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

?

redis-check-dump

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

?

redis-sentinel

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

?

Redis GUI 工具

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

?

?

學(xué)會(huì)安裝 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 成功后會(huì)在 src 文件夾下產(chǎn)生一些二進(jìn)制可執(zhí)行文件,包括 redis-server、redis-cli 等等:

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

??

啟動(dòng) redis

下面啟動(dòng) redis 服務(wù)

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

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

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

redis.conf 是一個(gè)默認(rèn)的配置文件。我們可以根據(jù)需要使用自己的配置文件。啟動(dòng) redis 服務(wù)進(jìn)程后,就可以使用測試客戶端程序 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??

啟動(dòng) Redis:redis-server

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

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

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

?

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

# 檢查Redis服務(wù)器系統(tǒng)進(jìn)程 ~ 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# 通過啟動(dòng)命令檢查Redis服務(wù)器狀態(tài) ~ netstat -nlt|grep 6379 tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN# 通過啟動(dòng)命令檢查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ù)器運(yùn)行命令:Redis 安裝目錄 /redis-server.exe
  • Redis 客戶端運(yùn)行命令:Redis 安裝目錄 /redis-cli.exe

?

?

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

?

概述

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

?

命令行 訪問 Redis

安裝 Redis 服務(wù)器,會(huì)自動(dòng)地一起安裝 Redis 命令行客戶端程序。在本機(jī)輸入redis-cli 命令就可以啟動(dòng),客戶端程序訪問 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 的訪問賬號

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

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

?

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

默認(rèn)情況下,Redis服務(wù)器不允許遠(yuǎn)程訪問,只允許本機(jī)訪問,所以我們需要設(shè)置打開遠(yuǎn)程訪問的功能。用 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)允許遠(yuǎn)程登陸訪問。

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

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

?

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

?

管理 命令 總結(jié)

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

?

Redis 客戶端

直接看一個(gè)例子:

$ ./redis-cli // 啟動(dòng) 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 查詢滿足表達(dá)式的所有key。redis 允許模糊查詢key,有3個(gè)通配符 *、?、[] (2)randomkey: 返回隨機(jī)key   (3)type key: 返回key存儲的類型 (4)exists key: 判斷某個(gè)key是否存在 (5)del key: 刪除 key (6)rename key newkey: 改名 (7)renamenx key newkey:如果 newkey 不存在則修改成功 (8)move key 1: 將 key 移動(dòng)到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同時(shí)寫,則以后面的有效期為準(zhǔn)nx:如果key不存在則建立xx:如果key存在則修改其值 (2)get key:取值 (3)mset key1 value1 key2 value2 一次設(shè)置多個(gè)值 (4)mget key1 key2 : 一次獲取多個(gè)值 (5)setrange key offset value:把字符串的offset偏移字節(jié)改成value如果偏移量 > 字符串長度,該字符自動(dòng)補(bǔ)0x00 (6)append key value :把value追加到key 的原值上 (7)getrange key start stop:獲取字符串中[start, stop]范圍的值對于字符串的下標(biāo),左數(shù)從0開始,右數(shù)從-1開始注意:當(dāng)start>length,則返回空字符串當(dāng)stop>=length,則截取至字符串尾如果start所處位置在stop右邊,則返回空字符串 (8)getset key nrevalue: 獲取并返回舊值,在設(shè)置新值 (9)incr key: 自增,返回新值,如果incr一個(gè)不是int的value則返回錯(cuò)誤,incr一個(gè)不存在的key,則設(shè)置key為1 (10)incrby key 2: 跳2自增 (11)incrbyfloat by 0.7: 自增浮點(diǎn)數(shù)  (12)setbit key offset value:設(shè)置offset對應(yīng)二進(jìn)制上的值,返回該位上的舊值注意:如果offset過大,則會(huì)在中間填充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 類型其實(shí)就是一個(gè)每個(gè)子元素都是 string 類型的雙向鏈表,鏈表的最大長度是2^32。list 既可以用做棧,也可以用做隊(duì)列。

  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的絕對值個(gè)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:計(jì)算鏈表的元素個(gè)數(shù)(10)linsert key after|before search value:在key 鏈表中尋找search,并在search值之前|之后插入value(11)rpoplpush source dest:把source 的末尾拿出,放到dest頭部,并返回單元值應(yīng)用場景: task + bak 雙鏈表完成安全隊(duì)列業(yè)務(wù)邏輯: rpoplpush task bak接收返回值并做業(yè)務(wù)處理如果成功則rpop bak清除任務(wù),如果不成功,下次從bak表取任務(wù)(12)brpop,blpop key timeout:等待彈出key的尾/頭元素timeout為等待超時(shí)時(shí)間,如果timeout為0則一直等待下去應(yīng)用場景:長輪詢ajax,在線聊天時(shí)能用到

?

四、hashes 類型及操作

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

? ? ? ? hash 特別適用于存儲對象,將一個(gè)對象存儲在 hash 類型中會(huì)占用更少的內(nèi)存,并且可以方便的存取整個(gè)對象。
? ? ? ? 配置: hash_max_zipmap_entries 64? ?# 配置字段最多64個(gè)
? ? ? ? ? ? ? ? ? ? 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í)設(shè)置多個(gè)field (4)hget myhash field: 獲取指定的hash field (5)hmget myhash field1 field2:一次獲取多個(gè)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: 獲取某個(gè)hash中全部的field及value 

?

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

特點(diǎn):無序性、確定性、唯一性

(1)sadd key value1 value2: 往集合里面添加元素 (2)smembers key: 獲取集合所有的元素 (3)srem key value: 刪除集合某個(gè)元素 (4)spop key: 返回并刪除集合中1個(gè)隨機(jī)元素(可以坐抽獎(jiǎng),不會(huì)重復(fù)抽到某人)    (5)srandmember key: 隨機(jī)取一個(gè)元素 (6)sismember key value: 判斷集合是否有某個(gè)值 (7)scard key: 返回集合元素的個(gè)數(shù) (8)smove source dest value: 把 source的value移動(dòng)到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ǔ)上增加了一個(gè)順序?qū)傩?#xff0c;這一屬性在添加修改元素的時(shí)候可以指定,每次指定后,zset 會(huì)自動(dòng)按新的值調(diào)整順序。可以理解為有兩列的 mysql 表,一列存儲value,一列存儲順序,操作中 key 理解為 zset 的名字。
  和 set 一樣 sorted sets 也是 string 類型元素的集合,不同的是每個(gè)元素都會(huì)關(guān)聯(lián)一個(gè) double 型的 score。sorted set 的實(shí)現(xiàn)是 skip list 和 hash table 的混合體。
  當(dāng)元素被添加到集合中時(shí),一個(gè)元素到score的映射被添加到hash table中,所以給定一個(gè)元素獲取score的開銷是O(1)。另一個(gè)score到元素的映射被添加的skip list,并按照score排序,所以就可以有序地獲取集合中的元素。添加、刪除操作開銷都是O(logN)和skip list的開銷一致,redis的skip list 實(shí)現(xiàn)是雙向鏈表,這樣就可以逆序從尾部去元素。sorted set最經(jīng)常使用方式應(yīng)該就是作為索引來使用,我們可以把要排序的字段作為score存儲,對象的ID當(dāng)元素存儲。

(1)zadd key score1 value1: 添加元素 (2)zrange key start stop [withscore]:把集合排序后,返回名次[start,stop]的元素 默認(rèn)是升續(xù)排列 withscores 是把score也打印出來 (3)zrank key member: 查詢 member 的排名(升序0名開始) (4)zrangebyscore key min max [withscores] limit offset N:集合(升序)排序后取score在[min, max]內(nèi)的元素,并跳過offset個(gè),取出N個(gè) (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: 返回集合元素的個(gè)數(shù) (10)zcount key min max: 返回[min, max]區(qū)間內(nèi)元素?cá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)重,交集時(shí) 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ā)送給客戶端。在此過程中,客戶端都會(huì)以阻塞的方式等待服務(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條請求。要想解決這一性能問題,我們該如何進(jìn)行優(yōu)化呢?

?

管線 (pipelining):

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

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ù)器端在接受到請求并處理之后,會(huì)將每條命令的應(yīng)答數(shù)據(jù)存入隊(duì)列,之后再發(fā)送到客戶端。

?

Benchmark:

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

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

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

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

?

Redis 的 Key 操作命令詳解

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

命令示例

1. KEYS/RENAME/DEL/EXISTS/MOVE/RENAMENX:#在Shell命令行下啟動(dòng)Redis客戶端工具。/> redis-cli#清空當(dāng)前選擇的數(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ù)中的模式,獲取當(dāng)前數(shù)據(jù)庫中符合該模式的所有key,從輸出可以看出,該命令在執(zhí)行時(shí)并不區(qū)分與Key關(guān)聯(lián)的Value類型。redis 127.0.0.1:6379> keys my*1) "mysetkey"2) "mykey"3) "mykey2"#刪除了兩個(gè)Keys。redis 127.0.0.1:6379> del mykey mykey2(integer) 2#查看一下剛剛刪除的Key是否還存在,從返回結(jié)果看,mykey確實(shí)已經(jīng)刪除了。redis 127.0.0.1:6379> exists mykey(integer) 0#查看一下沒有刪除的Key,以和上面的命令結(jié)果進(jìn)行比較。redis 127.0.0.1:6379> exists mysetkey(integer) 1#將當(dāng)前數(shù)據(jù)庫中的mysetkey鍵移入到ID為1的數(shù)據(jù)庫中,從結(jié)果可以看出已經(jīng)移動(dòng)成功。redis 127.0.0.1:6379> move mysetkey 1(integer) 1#打開ID為1的數(shù)據(jù)庫。redis 127.0.0.1:6379> select 1OK#查看一下剛剛移動(dòng)過來的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#準(zhǔn)備新的測試數(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)不存在了,所以返回錯(cuò)誤信息。redis 127.0.0.1:6379> rename mykey mykey1(error) ERR no such key#為renamenx準(zhǔn)備測試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: #為后面的示例準(zhǔn)備的測試數(shù)據(jù)。redis 127.0.0.1:6379> set mykey "hello"OK#將該鍵的超時(shí)設(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命令,該存在超時(shí)的鍵變成持久化的鍵,即將該Key的超時(shí)去掉。redis 127.0.0.1:6379> persist mykey(integer) 1#ttl的返回值告訴我們,該鍵已經(jīng)沒有超時(shí)了。redis 127.0.0.1:6379> ttl mykey(integer) -1#為后面的expire命令準(zhǔn)備數(shù)據(jù)。redis 127.0.0.1:6379> del mykey(integer) 1redis 127.0.0.1:6379> set mykey "hello"OK#設(shè)置該鍵的超時(shí)被100秒。redis 127.0.0.1:6379> expire mykey 100(integer) 1#用ttl命令看一下當(dāng)前還剩下多少秒,從結(jié)果中可以看出還剩下96秒。redis 127.0.0.1:6379> ttl mykey(integer) 96#重新更新該鍵的超時(shí)時(shí)間為20秒,從返回值可以看出該命令執(zhí)行成功。redis 127.0.0.1:6379> expire mykey 20(integer) 1#再用ttl確認(rèn)一下,從結(jié)果中可以看出果然被更新了。redis 127.0.0.1:6379> ttl mykey(integer) 17#立刻更新該鍵的值,以使其超時(shí)無效。redis 127.0.0.1:6379> set mykey "world"OK#從ttl的結(jié)果可以看出,在上一條修改該鍵的命令執(zhí)行后,該鍵的超時(shí)也無效了。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#準(zhǔn)備一個(gè)值是set類型的鍵。redis 127.0.0.1:6379> sadd mysetkey 1 2(integer) 2#mysetkey的鍵是set,因此返回字符串set。redis 127.0.0.1:6379> type mysetkeyset#返回?cái)?shù)據(jù)庫中的任意鍵。redis 127.0.0.1:6379> randomkey"oldkey"#清空當(dāng)前打開的數(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 類型是一個(gè)很基礎(chǔ)的數(shù)據(jù)類型,也是任何存儲系統(tǒng)都必備的數(shù)據(jù)類型。 字符串類型是 Redis 中最為基礎(chǔ)的數(shù)據(jù)存儲類型,它在 Redis 中是二進(jìn)制安全的,這便意味著該類型可以接受任何格式的數(shù)據(jù),如JPEG圖像數(shù)據(jù)或Json對象描述信息等。在 Redis 中字符串類型的 Value 最多可以容納的數(shù)據(jù)長度是512M。
看一個(gè)最簡單的例子:

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

字符串類型的用法就是這么簡單,因?yàn)槭嵌M(jìn)制安全的,所以你完全可以把一個(gè)圖片文件的內(nèi)容作為字符串來存儲。
另外,我們還可以通過字符串類型進(jìn)行數(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ù)值操作時(shí),redis會(huì)將字符串類型轉(zhuǎn)換成數(shù)值。由于INCR等指令本身就具有原子操作的特性,所以我們完全可以利用redis的INCR、INCRBY、DECR、DECRBY等指令來實(shí)現(xiàn)原子計(jì)數(shù)的效果,假如,在某種場景下有3個(gè)客戶端同時(shí)讀取了mynum的值(值為2),然后對其同時(shí)進(jìn)行了加1的操作,那么,最后mynum的值一定是5。不少網(wǎng)站都利用redis的這個(gè)特性來實(shí)現(xiàn)業(yè)務(wù)上的統(tǒng)計(jì)計(jì)數(shù)需求。

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

?

命令示例:

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命令返回當(dāng)前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í)行遞增操作時(shí),Redis將報(bào)告錯(cuò)誤信息。 (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 #將計(jì)數(shù)器的值原子性的遞增1 (integer) 1 #在獲取計(jì)數(shù)器原有值的同時(shí),并將其設(shè)置為新值,這兩個(gè)操作原子性的同時(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的過期時(shí)間為10秒。 OK #通過ttl命令查看一下指定Key的剩余存活時(shí)間(秒數(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 #刪除該鍵,以便于下面的測試驗(yàn)證。 (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 #從第六個(gè)字節(jié)開始替換2個(gè)字節(jié)(dd只有2個(gè)字節(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原有值的長度了,該命令將會(huì)在末尾補(bǔ)0。 (integer) 22 redis 127.0.0.1:6379> get mykey #查看補(bǔ)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,從第一個(gè)字節(jié)開始,到第二個(gè)字節(jié)結(jié)束。 "12" redis 127.0.0.1:6379> getrange mykey 1 20 #20已經(jīng)超過Value的總長度,因此將截取第一個(gè)字節(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開始計(jì)算的第七位BIT值為1,返回原有BIT值0 (integer) 0 redis 127.0.0.1:6379> get mykey #獲取設(shè)置的結(jié)果,二進(jìn)制的0000 0001的十六進(jìn)制值為0x01 "\x01" redis 127.0.0.1:6379> setbit mykey 6 1 #設(shè)置從0開始計(jì)算的第六位BIT值為1,返回原有BIT值0 (integer) 0 redis 127.0.0.1:6379> get mykey #獲取設(shè)置的結(jié)果,二進(jìn)制的0000 0011的十六進(jìn)制值為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兩個(gè)鍵。 OK redis 127.0.0.1:6379> mget key1 key2 #批量獲取了key1和key2兩個(gè)鍵的值。 1) "hello" 2) "world" #批量設(shè)置了key3和key4兩個(gè)鍵,因?yàn)橹八麄儾⒉淮嬖?#xff0c;所以該命令執(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兩個(gè)鍵,但是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 的另一個(gè)重要的數(shù)據(jù)結(jié)構(gòu)叫做 lists,翻譯成中文叫做“列表”。
首先要明確一點(diǎn),redis中的lists在底層實(shí)現(xiàn)上并不是數(shù)組,而是鏈表,也就是說對于一個(gè)具有上百萬個(gè)元素的lists來說,在頭部和尾部插入一個(gè)新元素,其時(shí)間復(fù)雜度是常數(shù)級別的,比如用LPUSH在10個(gè)元素的lists頭部插入新元素,和在上千萬元素的lists頭部插入新元素的速度應(yīng)該是相同的。雖然lists有這樣的優(yōu)勢,但同樣有其弊端,那就是,鏈表型lists的元素定位會(huì)比較慢,而數(shù)組型lists的元素定位就會(huì)快得多。lists的常用操作包括LPUSH、RPUSH、LRANGE等。我們可以用LPUSH在lists的左側(cè)插入一個(gè)新元素,用RPUSH在lists的右側(cè)插入一個(gè)新元素,用LRANGE命令從lists中指定一個(gè)范圍來提取元素。

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

我們來看幾個(gè)例子:

//新建一個(gè)list叫做mylist,并在列表頭部插入元素"1" 127.0.0.1:6379> lpush mylist "1" //返回當(dāng)前mylist中的元素個(gè)數(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ù)第一個(gè)元素 127.0.0.1:6379> lrange mylist 0 -1 1) "0" 2) "1" 3) "2"

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

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

相關(guān)命令

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

命令示例

1. LPUSH/LPUSHX/LRANGE:/> redis-cli #在Shell提示符下啟動(dòng)redis客戶端工具。redis 127.0.0.1:6379> del mykey(integer) 1#mykey鍵并不存在,該命令會(huì)創(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個(gè)元素。redis 127.0.0.1:6379> lrange mykey 0 21) "d"2) "c"3) "b"#取鏈表中的全部元素,其中0表示第一個(gè)元素,-1表示最后一個(gè)元素。redis 127.0.0.1:6379> lrange mykey 0 -11) "d"2) "c"3) "b"4) "a"#mykey2鍵此時(shí)并不存在,因此該命令將不會(huì)進(jìn)行任何操作,其返回值為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鍵此時(shí)已經(jīng)存在,所以該命令插入成功,并返回鏈表中當(dā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命令兩次后,鏈表頭部的兩個(gè)元素已經(jīng)被彈出,此時(shí)鏈表中元素的數(shù)量是2redis 127.0.0.1:6379> llen mykey(integer) 23. LREM/LSET/LINDEX/LTRIM:#為后面的示例準(zhǔn)備測試數(shù)據(jù)。redis 127.0.0.1:6379> lpush mykey a b c d a c(integer) 6#從頭部(left)向尾部(right)變量鏈表,刪除2個(gè)值等于a的元素,返回值為實(shí)際刪除的數(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(頭部的第二個(gè)元素)的元素值。redis 127.0.0.1:6379> lindex mykey 1"d"#將索引值為1(頭部的第二個(gè)元素)的元素值設(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è)置失敗,該命令返回錯(cuò)誤信息。redis 127.0.0.1:6379> lset mykey 6 hh(error) ERR index out of range#僅保留索引值0到2之間的3個(gè)元素,注意第0個(gè)和第2個(gè)元素均被保留。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#為后面的示例準(zhǔn)備測試數(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在插入多值時(shí)的插入順序。redis 127.0.0.1:6379> lrange mykey 0 -11) "a"2) "b"3) "c"4) "d"#該鍵已經(jīng)存在并且包含4個(gè)元素,rpushx命令將執(zhí)行成功,并將元素e插入到鏈表的尾部。redis 127.0.0.1:6379> rpushx mykey e(integer) 5#通過lindex命令可以看出之前的rpushx命令確實(shí)執(zhí)行成功,因?yàn)樗饕禐?的元素已經(jīng)是新元素了。redis 127.0.0.1:6379> lindex mykey 4"e"#由于mykey2鍵并不存在,因此該命令不會(huì)插入數(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彈出,同時(shí)再插入到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"#查看移動(dòng)結(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在其官方文檔中給出了一些實(shí)用技巧,如RPOPLPUSH命令,下面給出具體的解釋。
Redis鏈表經(jīng)常會(huì)被用于消息隊(duì)列的服務(wù),以完成多程序之間的消息交換。假設(shè)一個(gè)應(yīng)用程序正在執(zhí)行LPUSH操作向鏈表中添加新的元素,我們通常將這樣的程序稱之為"生產(chǎn)者(Producer)",而另外一個(gè)應(yīng)用程序正在執(zhí)行RPOP操作從鏈表中取出元素,我們稱這樣的程序?yàn)?#34;消費(fèi)者(Consumer)"。如果此時(shí),消費(fèi)者程序在取出消息元素后立刻崩潰,由于該消息已經(jīng)被取出且沒有被正常處理,那么我們就可以認(rèn)為該消息已經(jīng)丟失,由此可能會(huì)導(dǎo)致業(yè)務(wù)數(shù)據(jù)丟失,或業(yè)務(wù)狀態(tài)的不一致等現(xiàn)象的發(fā)生。然而通過使RPOPLPUSH命令,消費(fèi)者程序在從主消息隊(duì)列中取出消息之后再將其插入到備份隊(duì)列中,直到消費(fèi)者程序完成正常的處理邏輯后再將該消息從備份隊(duì)列中刪除。同時(shí)我們還可以提供一個(gè)守護(hù)進(jìn)程,當(dāng)發(fā)現(xiàn)備份隊(duì)列中的消息過期時(shí),可以重新將其再放回到主消息隊(duì)列中,以便其它的消費(fèi)者程序繼續(xù)處理。

?

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

?

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

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

//向集合myset中加入一個(gè)新元素"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 //新建一個(gè)新的集合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" //對兩個(gè)集合求并集 127.0.0.1:6379> sunion myset yourset 1) "1" 2) "one" 3) "2" 4) "two"

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

相關(guān)命令

命令原型 時(shí)間復(fù)雜度 返回值 SADDkey member [member ...] O(N) 本次操作實(shí)際插入的成員數(shù)量。時(shí)間復(fù)雜度中的N表示操作的成員數(shù)量。如果在插入的過程用,參數(shù)中有的成員在Set中已經(jīng)存在,該成員將被忽略,而其它成員仍將會(huì)被正常插入。如果執(zhí)行該命令之前,該Key并不存在,該命令將會(huì)創(chuàng)建一個(gè)新的Set,此后再將參數(shù)中的成員陸續(xù)插入。如果該Key的Value不是Set類型,該命令將返回相關(guān)的錯(cuò)誤信息。 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中所有的成員。時(shí)間復(fù)雜度中的N表示Set中已經(jīng)存在的成員數(shù)量。獲取與該Key關(guān)聯(lián)的Set中所有的成員。 SPOPkey O(1) 返回移除的成員,如果該Key并不存在,則返回nil。隨機(jī)的移除并返回Set中的某一成員。 由于Set中元素的布局不受外部控制,因此無法像List那樣確定哪個(gè)元素位于Set的頭部或者尾部。 SREMkey member [member ...] O(N) 從Set中實(shí)際移除的成員數(shù)量,如果沒有則返回0。時(shí)間復(fù)雜度中的N表示被刪除的成員數(shù)量。從與Key關(guān)聯(lián)的Set中刪除參數(shù)中指定的成員,不存在的參數(shù)成員將被忽略,如果該Key并不存在,將視為空Set處理。 SRANDMEMBER key O(1) 返回隨機(jī)位置的成員,如果Key不存在則返回nil。和SPOP一樣,隨機(jī)的返回Set中的一個(gè)成員,不同的是該命令并不會(huì)刪除返回的成員。 SMOVEsource destination member O(1) 1表示正常移動(dòng),0表示source中并不包含參數(shù)成員。原子性的將參數(shù)中的成員從source鍵移入到destination鍵所關(guān)聯(lián)的Set中。因此在某一時(shí)刻,該成員或者出現(xiàn)在source中,或者出現(xiàn)在destination中。如果該成員在source中并不存在,該命令將不會(huì)再執(zhí)行任何操作并返回0,否則,該成員將從source移入到destination。如果此時(shí)該成員已經(jīng)在destination中存在,那么該命令僅是將該成員從source中移出。如果和Key關(guān)聯(lián)的Value不是Set,將返回相關(guān)的錯(cuò)誤信息。 SDIFFkey [key ...] O(N) 差異結(jié)果成員的集合。 時(shí)間復(fù)雜度中的N表示所有Sets中成員的總數(shù)量。返回參數(shù)中第一個(gè)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é)果成員的集合。時(shí)間復(fù)雜度中的N表示最小Set中元素的數(shù)量,M則表示參數(shù)中Sets的數(shù)量。該命令將返回參數(shù)中所有Keys關(guān)聯(lián)的Sets中成員的交集。因此如果參數(shù)中任何一個(gè)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é)果成員的集合。時(shí)間復(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命令行下啟動(dòng)Redis的客戶端程序。/> redis-cli#插入測試數(shù)據(jù),由于該鍵myset之前并不存在,因此參數(shù)中的三個(gè)成員都被正常插入。redis 127.0.0.1:6379> sadd myset a b c(integer) 3#由于參數(shù)中的a在myset中已經(jīng)存在,因此本次操作僅僅插入了d和e兩個(gè)新成員。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#為后面的示例準(zhǔn)備測試數(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é)果可以看出,該命令確實(shí)是隨機(jī)的返回了某一成員。redis 127.0.0.1:6379> srandmember myset"c"#Set中尾部的成員b被移出并返回,事實(shí)上b并不是之前插入的第一個(gè)或最后一個(gè)成員。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三個(gè)成員,其中f并不存在,因此只有a和d兩個(gè)成員被移出,返回為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命令準(zhǔn)備數(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é)果可以看出移動(dòng)成功。redis 127.0.0.1:6379> smove myset myset2 a(integer) 1#再次將a從myset移到myset2,由于此時(shí)a已經(jīng)不是myset的成員了,因此移動(dòng)失敗并返回0。redis 127.0.0.1:6379> smove myset myset2 a(integer) 0#分別查看myset和myset2的成員,確認(rèn)移動(dòng)是否真的成功。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:#為后面的命令準(zhǔn)備測試數(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三個(gè)成員是兩者之間的差異成員。再用這個(gè)結(jié)果繼續(xù)和myset3進(jìn)行差異比較,b和d是myset3不存在的成員。redis 127.0.0.1:6379> sdiff myset myset2 myset31) "d"2) "b"#將3個(gè)集合的差異成員存在在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"#從之前準(zhǔn)備的數(shù)據(jù)就可以看出,這三個(gè)Set的成員交集只有c。redis 127.0.0.1:6379> sinter myset myset2 myset31) "c"#將3個(gè)集合中的交集成員存儲到與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個(gè)集合中的成員的并集。 redis 127.0.0.1:6379> sunion myset myset2 myset31) "b"2) "c"3) "d"4) "e"5) "a"#將3個(gè)集合中成員的并集存儲到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地址信息。對于此場景,我們僅需在每次訪問該博客時(shí)將訪問者的IP存入Redis中,Set數(shù)據(jù)類型會(huì)自動(dòng)保證IP地址的唯一性。
  • 2). 充分利用Set類型的服務(wù)端聚合操作方便、高效的特性,可以用于維護(hù)數(shù)據(jù)對象之間的關(guān)聯(lián)關(guān)系。比如所有購買某一電子設(shè)備的客戶ID被存儲在一個(gè)指定的Set中,而購買另外一種電子產(chǎn)品的客戶ID被存儲在另外一個(gè)Set中,如果此時(shí)我們想獲取有哪些客戶同時(shí)購買了這兩種商品時(shí),Set的intersections命令就可以充分發(fā)揮它的方便和效率的優(yōu)勢了。

?

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

?

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

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

127.0.0.1:6379> zadd myzset 1 baidu.com (integer) 1 //向myzset中新增一個(gè)元素360.com,賦予它的序號是3 127.0.0.1:6379> zadd myzset 3 360.com (integer) 1 //向myzset中新增一個(gè)元素google.com,賦予它的序號是2 127.0.0.1:6379> zadd myzset 2 google.com (integer) 1 //列出myzset的所有元素,同時(shí)列出其序號,可以看出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)命令

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

命令示例

1. ZADD/ZCARD/ZCOUNT/ZREM/ZINCRBY/ZSCORE/ZRANGE/ZRANK:#在Shell的命令行下啟動(dòng)Redis客戶端工具。/> redis-cli#添加一個(gè)分?jǐn)?shù)為1的成員。redis 127.0.0.1:6379> zadd myzset 1 "one"(integer) 1#添加兩個(gè)分?jǐn)?shù)分別是2和3的兩個(gè)成員。redis 127.0.0.1:6379> zadd myzset 2 "two" 3 "three"(integer) 2#0表示第一個(gè)成員,-1表示最后一個(gè)成員。WITHSCORES選項(xiàng)表示返回的結(jié)果中包含每個(gè)成員及其分?jǐn)?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表示第一個(gè)位置。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中,分?jǐn)?shù)滿足表達(dá)式1 <= score <= 2的成員的數(shù)量。redis 127.0.0.1:6379> zcount myzset 1 2(integer) 2#刪除成員one和two,返回實(shí)際刪除成員的數(shù)量。redis 127.0.0.1:6379> zrem myzset one two(integer) 2#查看是否刪除成功。redis 127.0.0.1:6379> zcard myzset(integer) 1#獲取成員three的分?jǐn)?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的分?jǐn)?shù)增加2,并返回該成員更新后的分?jǐn)?shù)。redis 127.0.0.1:6379> zincrby myzset 2 one"3"#將成員one的分?jǐn)?shù)增加-1,并返回該成員更新后的分?jǐn)?shù)。redis 127.0.0.1:6379> zincrby myzset -1 one"2"#查看在更新了成員的分?jǐn)?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#獲取分?jǐn)?shù)滿足表達(dá)式1 <= score <= 2的成員。redis 127.0.0.1:6379> zrangebyscore myzset 1 21) "one"2) "two"#獲取分?jǐn)?shù)滿足表達(dá)式1 < score <= 2的成員。redis 127.0.0.1:6379> zrangebyscore myzset (1 21) "two"#-inf表示第一個(gè)成員,+inf表示最后一個(gè)成員,limit后面的參數(shù)用于限制返回成員的自己,#2表示從位置索引(0-based)等于2的成員開始,去后面3個(gè)成員。redis 127.0.0.1:6379> zrangebyscore myzset -inf +inf limit 2 31) "three"2) "four"#刪除分?jǐn)?shù)滿足表達(dá)式1 <= score <= 2的成員,并返回實(shí)際刪除的數(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"#刪除位置索引滿足表達(dá)式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:#為后面的示例準(zhǔn)備測試數(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#獲取分?jǐn)?shù)滿足表達(dá)式3 >= score >= 0的成員,并以相反的順序輸出,即從高到底的順序。redis 127.0.0.1:6379> zrevrangebyscore myzset 3 01) "three"2) "two"3) "one"#該命令支持limit選項(xiàng),其含義等同于zrangebyscore中的該選項(xiàng),只是在計(jì)算位置時(shí)按照相反的順序計(jì)算和獲取。redis 127.0.0.1:6379> zrevrangebyscore myzset 4 0 limit 1 21) "three"2) "two"

應(yīng)用范圍:

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

?

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

?

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

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

看一個(gè)例子:

//建立哈希,并賦值 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" //更改哈希中的某一個(gè)值 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 的操作,同樣很豐富,需要時(shí),大家可以從這里查詢:https://redis.io/commands

相關(guān)命令

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

命令示例

1. HSET/HGET/HDEL/HEXISTS/HLEN/HSETNX:#在Shell命令行啟動(dòng)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值添加一個(gè)新的字段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)將其刪除,因?yàn)闆]有刪除,返回0。redis 127.0.0.1:6379> hdel myhash field1(integer) 0#判斷myhash鍵中是否存在field1字段,由于上一條命令已經(jīng)將其刪除,因?yàn)榉祷?。redis 127.0.0.1:6379> hexists myhash field1(integer) 0#通過hsetnx命令給myhash添加新字段field1,其值為stephen,因?yàn)樵撟侄我呀?jīng)被刪除,所以該命令添加成功并返回1。redis 127.0.0.1:6379> hsetnx myhash field1 stephen(integer) 1#由于myhash的field1字段已經(jīng)通過上一條命令添加成功,因?yàn)楸緱l命令不做任何操作后返回0。redis 127.0.0.1:6379> hsetnx myhash field1 stephen(integer) 02. HINCRBY:#刪除該鍵,便于后面示例的測試。redis 127.0.0.1:6379> del myhash(integer) 1#準(zhǔn)備測試數(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è)置多個(gè)字段,分別是field1 = "hello", field2 = "world"。redis 127.0.0.1:6379> hmset myhash field1 "hello" field2 "world"OK#獲取myhash鍵的多個(gè)字段,其中field3并不存在,因?yàn)樵诜祷亟Y(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,簡而言之,就是在不同的時(shí)間點(diǎn),將redis存儲的數(shù)據(jù)生成快照并存儲到磁盤等介質(zhì)上;
AOF,則是換了一個(gè)角度來實(shí)現(xiàn)持久化,那就是將redis執(zhí)行過的所有寫指令記錄下來,在下次redis重新啟動(dòng)時(shí),只要把這些寫指令從前到后再重復(fù)執(zhí)行一遍,就可以實(shí)現(xiàn)數(shù)據(jù)恢復(fù)了。
其實(shí)RDB和AOF兩種方式也可以同時(shí)使用,在這種情況下,如果redis重啟的話,則會(huì)優(yōu)先采用AOF方式來進(jìn)行數(shù)據(jù)恢復(fù),這是因?yàn)锳OF方式的數(shù)據(jù)恢復(fù)完整度更高。
如果你沒有數(shù)據(jù)持久化的需求,也完全可以關(guān)閉RDB和AOF方式,這樣的話,redis將變成一個(gè)純內(nèi)存數(shù)據(jù)庫,就像memcache一樣。

Redis 提供的持久化機(jī)制:

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

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

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

    ? ? RDB 優(yōu)勢

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

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

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

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

    ? ??AOF優(yōu)勢

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

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

    AOF 重寫

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

    ?

    如何選擇 RDB 和 AOF

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

    ?

    其它

    1. Snapshotting:

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

    2. Dump快照的機(jī)制:

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

    3. AOF文件:

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

    4. AOF的配置:

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

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

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

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

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

    ?

    ?

    虛擬內(nèi)存介紹

    ?

    簡介

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

    ?

    應(yīng)用場景

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

    ?

    配置

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

    vm-enabled yes

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

    vm-max-memory (bytes)

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

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

    vm-pages 134217728 vm-page-size 32

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

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

    vm-max-threads 4

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

    ?

    ?

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

    ?

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

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

    ?

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

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

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

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

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

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

    ?

    ?

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

    ?

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

    ?

    ?

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

    ?

    Redis 的 Replication

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

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

  • ?同一個(gè)Master可以同步多個(gè)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中要有獨(dú)立的進(jìn)程來完成此操作。
  • ?

    Replication的工作原理

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

    ?

    如何配置Replication

    見如下步驟:

    1). 同時(shí)啟動(dòng)兩個(gè)Redis服務(wù)器,可以考慮在同一臺機(jī)器上啟動(dòng)兩個(gè)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在同一臺主機(jī),Master的端口為6379OK

    上面的方式只是保證了在執(zhí)行slaveof命令之后,redis_6380成為了redis_6379的slave,一旦服務(wù)(redis_6380)重新啟動(dòng)之后,他們之間的復(fù)制關(guān)系將終止。
    如果希望長期保證這兩個(gè)服務(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ù)程序在每次啟動(dòng)后都會(huì)主動(dòng)建立與Redis_6379的Replication連接了。

    ?

    應(yīng)用示例:

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

    #啟動(dòng)master服務(wù)器。 [root@Stephen-PC redis]# redis-cli -p 6379 redis 127.0.0.1:6379> #情況Master當(dāng)前數(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"#啟動(dòng)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中刪除其中一個(gè)測試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ù)是指“一個(gè)完整的動(dòng)作,要么全部執(zhí)行,要么什么也沒有做”。在聊 redis 事務(wù)處理之前,要先和大家介紹四個(gè)redis指令,即 MULTI、EXEC、DISCARD、WATCH。這四個(gè)指令構(gòu)成了redis 事務(wù)處理的基礎(chǔ)。

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

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

    相關(guān)命令

    MULTI用于標(biāo)記事務(wù)的開始,其后執(zhí)行的命令都將被存入命令隊(duì)列,直到執(zhí)行EXEC時(shí),這些命令才會(huì)被原子的執(zhí)行。始終返回OK EXEC執(zhí)行在一個(gè)事務(wù)內(nèi)命令隊(duì)列中的所有命令,同時(shí)將當(dāng)前連接的狀態(tài)恢復(fù)為正常狀態(tài),即非事務(wù)狀態(tài)。如果在事務(wù)中執(zhí)行了WATCH命令,那么只有當(dāng)WATCH所監(jiān)控的Keys沒有被修改的前提下,EXEC命令才能執(zhí)行事務(wù)隊(duì)列中的所有命令,否則EXEC將放棄當(dāng)前事務(wù)中的所有命令。 原子性的返回事務(wù)中各條命令的返回結(jié)果。如果在事務(wù)中使用了WATCH,一旦事務(wù)被放棄,EXEC將返回NULL-multi-bulk回復(fù)。 DISCARD回滾事務(wù)隊(duì)列中的所有命令,同時(shí)再將當(dāng)前連接的狀態(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ù)隊(duì)列中的所有命令。 始終返回OK。 UNWATCH 取消當(dāng)前事務(wù)中指定監(jiān)控的Keys,如果執(zhí)行了EXEC或DISCARD命令,則無需再手工執(zhí)行該命令了,因?yàn)樵诖酥?#xff0c;事務(wù)中所有被監(jiān)控的Keys都將自動(dòng)取消。 始終返回OK。

    命令示例

    1. 事務(wù)被正常執(zhí)行:#在Shell命令行下執(zhí)行Redis的客戶端工具。/> redis-cli#在當(dāng)前連接上啟動(dòng)一個(gè)新的事務(wù)。redis 127.0.0.1:6379> multiOK#執(zhí)行事務(wù)中的第一條命令,從該命令的返回結(jié)果可以看出,該命令并沒有立即執(zhí)行,而是存于事務(wù)的命令隊(duì)列。redis 127.0.0.1:6379> incr t1QUEUED#又執(zhí)行一個(gè)新的命令,從結(jié)果可以看出,該命令也被存于事務(wù)的命令隊(duì)列。redis 127.0.0.1:6379> incr t2QUEUED#執(zhí)行事務(wù)命令隊(duì)列中的所有命令,從結(jié)果可以看出,隊(duì)列中命令的結(jié)果得到返回。redis 127.0.0.1:6379> exec1) (integer) 12) (integer) 1 2. 事務(wù)中存在失敗的命令:#開啟一個(gè)新的事務(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命令時(shí),該命令將會(huì)失敗。redis 127.0.0.1:6379> lpop aQUEUED#再次設(shè)置鍵a的值為字符串4。redis 127.0.0.1:6379> set a 4QUEUED#獲取鍵a的值,以便確認(rèn)該值是否被事務(wù)中的第二個(gè)set命令設(shè)置成功。redis 127.0.0.1:6379> get aQUEUED#從結(jié)果中可以看出,事務(wù)中的第二條命令lpop執(zhí)行失敗,而其后的set和get命令均執(zhí)行成功,這一點(diǎn)是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è)置一個(gè)事務(wù)執(zhí)行前的值。redis 127.0.0.1:6379> set t2 ttOK#開啟一個(gè)事務(wù)。redis 127.0.0.1:6379> multiOK#在事務(wù)內(nèi)為該鍵設(shè)置一個(gè)新值。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)控了多個(gè)Keys,倘若在WATCH之后有任何Key的值發(fā)生了變化,EXEC命令執(zhí)行的事務(wù)都將被放棄,同時(shí)返回Null multi-bulk應(yīng)答以通知調(diào)用者事務(wù)執(zhí)行失敗。例如,我們再次假設(shè)Redis中并未提供incr命令來完成鍵值的原子性遞增,如果要實(shí)現(xiàn)該功能,我們只能自行編寫相應(yīng)的代碼。其偽碼如下:val = GET mykeyval = val + 1SET mykey $val以上代碼只有在單連接的情況下才可以保證執(zhí)行結(jié)果是正確的,因?yàn)槿绻谕粫r(shí)刻有多個(gè)客戶端在同時(shí)執(zhí)行該段代碼,那么就會(huì)出現(xiàn)多線程程序中經(jīng)常出現(xiàn)的一種錯(cuò)誤場景--競態(tài)爭用(race condition)。比如,客戶端A和B都在同一時(shí)刻讀取了mykey的原有值,假設(shè)該值為10,此后兩個(gè)客戶端又均將該值加一后set回Redis服務(wù)器,這樣就會(huì)導(dǎo)致mykey的結(jié)果為11,而不是我們認(rèn)為的12。為了解決類似的問題,我們需要借助WATCH命令的幫助,見如下代碼:WATCH mykeyval = GET mykeyval = val + 1MULTISET mykey $valEXEC和此前代碼不同的是,新代碼在獲取mykey的值之前先通過WATCH命令監(jiān)控了該鍵,此后又將set命令包圍在事務(wù)中,這樣就可以有效的保證每個(gè)連接在執(zhí)行EXEC之前,如果當(dāng)前連接獲取的mykey的值被其它連接的客戶端修改,那么當(dāng)前連接的EXEC命令將執(zhí)行失敗。這樣調(diào)用者在判斷返回值后就可以獲悉val是否被重新設(shè)置成功。

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

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

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

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

    127.0.0.1:6379> multi OK 127.0.0.1:6379> set age 23 QUEUED //age不是集合,所以如下是一條明顯錯(cuò)誤的指令 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ù)時(shí),redis不會(huì)理睬第2條指令執(zhí)行錯(cuò)誤 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í)行了

    好了,我們來說說最后一個(gè)指令“WATCH”,這是一個(gè)很好用的指令,它可以幫我們實(shí)現(xiàn)類似于“樂觀鎖”的效果,即CAS(check and set)。
    WATCH本身的作用是“監(jiān)視key是否被改動(dòng)過”,而且支持同時(shí)監(jiān)視多個(gè)key,只要還沒真正觸發(fā)事務(wù),WATCH都會(huì)盡職盡責(zé)的監(jiān)視,一旦發(fā)現(xiàn)某個(gè)key被修改了,在執(zhí)行EXEC時(shí)就會(huì)返回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 配置 ---?簡介

    ?

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

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

    接下來,我們就來講解下redis配置文件的各個(gè)配置項(xiàng)的含義,注意,本文是基于redis-2.8.4版本進(jìn)行講解的。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的配置文件,會(huì)發(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 配置 --- 通用

    默認(rèn)情況下,redis并不是以daemon形式來運(yùn)行的。通過daemonize配置項(xiàng)可以控制redis的運(yùn)行形式,如果改為yes,那么redis就會(huì)以daemon形式運(yùn)行:

    daemonize no

    當(dāng)以daemon形式運(yùn)行時(shí),redis會(huì)生成一個(gè)pid文件,默認(rèn)會(huì)生成在/var/run/redis.pid。當(dāng)然,你可以通過pidfile來指定pid文件生成的位置,比如:

    pidfile /path/to/redis.pid

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

    bind 192.168.1.2 10.8.4.2

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

    port 6379

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

    unixsocket /tmp/redis.sock unixsocketperm 755

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

    timeout 0

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

    tcp-keepalive 0

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

    loglevel notice

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

    logfile ""

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

    syslog-ident redis

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

    syslog-facility local0

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

    databases 16

    這16個(gè)數(shù)據(jù)庫的編號將是0到15。默認(rèn)的數(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個(gè)key改變,就觸發(fā)一次持久化 save 300 10 //表示每5分鐘且至少有10個(gè)key改變,就觸發(fā)一次持久化 save 60 10000 //表示每60秒至少有10000個(gè)key改變,就觸發(fā)一次持久化

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

    save ""

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

    stop-writes-on-bgsave-error yes

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

    rdbcompression yes

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

    rdbchecksum yes

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

    dbfilename dump.rdb

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

    dir ./

    ?

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

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

    slaveof <masterip> <masterport>

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

    masterauth <master-password>

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

    slave-read-only yes

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

    rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52

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

    repl-ping-slave-period 10

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

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

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

    repl-timeout 60

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

    repl-disable-tcp-nodelay no

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

    repl-backlog-size 1mb

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

    repl-backlog-ttl 3600

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

    slave-priority 100

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

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

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

    ?

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

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

    requirepass zhimakaimen

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

    rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c89

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

    rename-command CONFIG ""

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

    ?

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

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

    maxclients 10000

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

    maxmemory <bytes>

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

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

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

    maxmemory-policy volatile-lru

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

    maxmemory-samples 3

    最后,我們補(bǔ)充一個(gè)信息,那就是到目前版本(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配置 ---?追加模式

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

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

    appendonly no

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

    appendfilename "appendonly.aof"

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

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

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

    ?

    appendfsync everysec

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

    no-appendfsync-on-rewrite no

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

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

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

    ?

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

    lua腳本的最大運(yùn)行時(shí)間是需要被嚴(yán)格限制的,要注意單位是毫秒:

    lua-time-limit 5000

    如果此值設(shè)置為0或負(fù)數(shù),則既不會(huì)有報(bào)錯(cuò)也不會(huì)有時(shí)間限制。

    ?

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

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

    slowlog-log-slower-than 10000

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

    slowlog-max-len 128

    ?

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

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

    ?

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

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

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

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

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

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

    set-max-intset-entries 512

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

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

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

    activerehashing yes

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

    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)頻率的配置項(xiàng):

    hz 10

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

    aof-rewrite-incremental-fsync yes

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

    ?

    ?

    ?

    ?

    ?

    總結(jié)

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

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

    国产精品精品久久久久久 | 夜夜视频资源 | 在线99| 国产精品一区在线 | 99中文字幕视频 | 最近日本韩国中文字幕 | 久草在线一免费新视频 | 国产专区在线播放 | av中文在线观看 | 奇米7777狠狠狠琪琪视频 | 综合天天网 | 日韩免费区| 日韩一级精品 | 91精品国产综合久久婷婷香蕉 | 国产精品久久久久久久久免费 | 人人cao| 美女网站在线免费观看 | 国产精品一区二区麻豆 | 国产精品视频永久免费播放 | 欧美性黑人 | 国产成人三级在线观看 | 91久色蝌蚪| 正在播放国产一区二区 | 一区二区电影网 | 日韩精品一区不卡 | 男女拍拍免费视频 | 精品久久久久久久久亚洲 | 久久成人欧美 | 色视频网站免费观看 | 免费成人短视频 | 波多野结衣在线视频免费观看 | 亚洲国产成人精品在线 | av片一区 | 国产美女精品视频 | 国产高清不卡av | av在线免费观看黄 | 国产涩涩网站 | 一区二区电影在线观看 | 久久国精品 | 亚洲理论电影网 | 狠狠色狠狠色综合日日92 | 中文字幕在线看视频 | 欧美激情综合色 | 九九涩涩av台湾日本热热 | 久久久www成人免费毛片麻豆 | 成年人在线免费看片 | 免费看亚洲毛片 | www视频免费在线观看 | 色天天综合久久久久综合片 | 99久在线精品99re8热视频 | 在线一二区 | 日本公妇在线观看高清 | 免费在线观看一级片 | 欧美在线观看视频一区二区三区 | 青青草国产在线 | 丁香激情综合久久伊人久久 | 美女久久视频 | 日日日视频 | 久久 一区 | 久久婷亚洲五月一区天天躁 | 国产精品免费av | 中文字幕有码在线 | 国产成人在线免费观看 | 中文字幕在线视频网站 | 国产精品电影一区 | 五月天久久精品 | 91自拍成人| 欧美在线视频不卡 | 最新高清无码专区 | 探花系列在线 | 视频精品一区二区三区 | 欧美日韩另类在线观看 | 亚洲国产高清在线 | 久久久免费国产 | 日韩欧美国产精品 | 亚洲伊人婷婷 | 免费av网站在线看 | 99精品国产兔费观看久久99 | 午夜成人免费电影 | 国产中年夫妇高潮精品视频 | 免费高清在线观看成人 | 精品国产免费一区二区三区五区 | 亚洲成人国产精品 | 伊人五月在线 | 国产人在线成免费视频 | 在线播放 日韩专区 | 亚洲一级黄色大片 | 久久精品播放 | 日韩免费一区二区在线观看 | av丝袜在线 | 黄色免费网站下载 | 国产一级黄色片免费看 | japanese黑人亚洲人4k | 操久在线 | 亚洲精品高清一区二区三区四区 | 国产中文伊人 | 欧美日韩成人一区 | 亚洲电影久久 | 狠狠撸电影 | 中文在线免费一区三区 | 亚洲精品视频免费在线观看 | 久久免费国产电影 | 夜夜躁日日躁狠狠躁 | 四虎影视成人永久免费观看视频 | 久久99精品波多结衣一区 | 国产精品一区二区 91 | 久久久精品99 | 亚洲热久久 | 综合网五月天 | 中文字幕999 | 国产在线国偷精品产拍免费yy | 丁香久久激情 | 久久 一区| 成人av电影在线播放 | 亚洲精品美女视频 | 91视频成人免费 | 国产乱码精品一区二区三区介绍 | 免费看的黄色小视频 | 99国产情侣在线播放 | 成人黄色在线电影 | 久久精品高清 | 人人干干人人 | 久久久久欠精品国产毛片国产毛生 | 视频二区在线 | 永久免费视频国产 | 91亚色在线观看 | 久久情侣偷拍 | 91中文在线观看 | 国产精品久久久久久久久久白浆 | 免费一级片在线 | 黄色特一级片 | 午夜精品999 | 国产91欧美 | 中文av字幕在线观看 | 国产这里只有精品 | 久草精品视频 | 免费人成在线观看 | 五月天视频网 | 午夜精品一区二区三区四区 | 久草视频中文 | 国产视频精品久久 | 久草电影在线观看 | 久久久久国 | 国产精品久久久久久久午夜片 | 高清不卡一区二区在线 | 精品综合久久久 | 欧美日韩免费一区二区三区 | 精品久久久久_ | 色干综合 | 极品久久久 | 五月婷婷在线观看视频 | 日韩中文久久 | 亚洲va男人天堂 | 欧美日本高清视频 | 欧美日韩一区二区三区在线免费观看 | 黄色大片入口 | 亚洲伦理电影在线 | 青青河边草免费直播 | 九九综合九九 | 欧美日韩综合在线观看 | 在线视频电影 | 欧美视频99 | 国产亚洲视频中文字幕视频 | 97视频在线观看视频免费视频 | www.色婷婷.com | 久久精品久久国产 | 亚洲成av片人久久久 | 午夜色大片在线观看 | 久久激情视频 | 色综合天天色综合 | 日韩欧美一区二区三区视频 | 97在线视频免费看 | 一区二区三区四区精品视频 | 99久久日韩精品视频免费在线观看 | 草久中文字幕 | 日本电影久久 | 九九精品视频在线观看 | 粉嫩aⅴ一区二区三区 | 九九日九九操 | 91av蜜桃| 在线精品视频在线观看高清 | 国产一级片视频 | 欧美一级片在线免费观看 | 久久精品视频播放 | 久操视频在线免费看 | 91成人精品一区在线播放69 | 久久精品久久久久久久 | 天天曰天天曰 | 久久免费视频在线观看6 | 97超碰人人澡 | 日韩精品中文字幕在线 | 叶爱av在线 | 久久精品成人热国产成 | 中文字幕日韩高清 | 在线视频 91 | 天天做天天爱天天综合网 | 深夜免费福利网站 | av 在线观看 | 国产高清精| 精品国产资源 | 久久精品国产免费看久久精品 | 精品理论片 | 国产第一页福利影院 | 国产 一区二区三区 在线 | 日韩精品资源 | 一级免费av | 最新中文字幕 | 高清不卡免费视频 | 五月婷婷丁香在线观看 | 人人干天天射 | 日本精品视频免费 | 夜夜夜 | 91精品国自产拍天天拍 | 亚洲欧美激情精品一区二区 | 91亚洲国产成人 | 欧美色图亚洲图片 | 日本九九视频 | 日韩国产欧美在线播放 | 伊人中文在线 | 久久成人精品电影 | 国产在线不卡 | 国产精选在线 | 在线看成人 | 日韩欧美国产视频 | 久久视频在线观看中文字幕 | 欧洲精品码一区二区三区免费看 | 成人黄视频 | 999在线视频 | 激情网第四色 | 欧美日韩精品在线观看视频 | 亚洲天堂网在线视频观看 | 日韩视频一 | 在线视频婷婷 | 日韩免费网址 | 午夜丁香视频在线观看 | 久久热首页 | 午夜天天操| 免费aa大片 | 久久欧洲视频 | 中文字幕 婷婷 | 国产黄色精品 | 久久久久久久亚洲精品 | 精品久久精品 | 婷婷在线视频 | 五月天久久婷 | 亚洲精品免费看 | 亚洲做受高潮欧美裸体 | 伊人五月天婷婷 | 久久亚洲私人国产精品 | 国产一区二三区好的 | 午夜精品视频免费在线观看 | 美女黄网站视频免费 | 国产精品麻豆视频 | 97电影网手机版 | 狠狠躁日日躁夜夜躁av | 亚洲高清久久久 | 五月婷婷狠狠 | 欧美日韩午夜爽爽 | 黄色毛片大全 | 国产亚洲婷婷 | 中文字幕在线视频第一页 | 欧美十八 | 深爱婷婷 | 久久视频国产 | 天天插天天射 | 69视频国产 | 亚洲免费av在线播放 | 在线97 | 中文字幕久久网 | 狠狠操导航 | 国产99视频在线观看 | 黄色国产区 | 日韩av高清在线观看 | 国产成人久久精品一区二区三区 | 超碰人人超碰 | 欧美一级特黄aaaaaa大片在线观看 | 精品久久久久久亚洲 | 国产在线传媒 | 免费在线成人 | 在线播放一区 | 婷婷丁香社区 | 2019精品手机国产品在线 | 中文字幕在线观看一区二区 | 在线观看亚洲国产精品 | 国产综合在线观看视频 | 日韩欧美在线视频一区二区三区 | 成人国产精品一区二区 | 亚洲精品国 | 久久av影院 | 日本精品久久久一区二区三区 | 中文字幕色在线 | 亚洲成人黄 | 日韩一区精品 | 999在线视频| 久久精品视频中文字幕 | 中文字幕在线专区 | 中文字幕在线观看免费高清电影 | 色综合久久五月 | 中文字幕在线观看视频网站 | 又黄又爽又湿又无遮挡的在线视频 | 成年人电影免费看 | 欧美日韩高清在线观看 | 色com| 国产精品久久久久久久久久99 | 国产亚洲精品久久久久久 | 国产91精品在线观看 | 国产精品午夜在线 | 中文字幕观看av | 久久免费视频精品 | 国产中文字幕在线看 | 成年在线观看 | 亚洲成成品网站 | 日韩在线高清视频 | 久久艹国产 | 久久精品看片 | 免费久久片 | 免费色黄 | 日韩高清精品免费观看 | 超碰精品在线 | 精品国产区 | 亚洲aaa级 | 69av免费视频 | 在线电影 一区 | 国产精品永久久久久久久久久 | 毛片区 | 怡红院成人在线 | 久久国语露脸国产精品电影 | 久久午夜免费观看 | 人人超在线公开视频 | 一级性av | 欧美亚洲精品在线观看 | 在线 视频 一区二区 | 最近中文字幕高清字幕免费mv | 久草视频在线观 | 久草视频一区 | 91精品国产福利在线观看 | 日韩素人在线观看 | 最新日韩视频在线观看 | 99精品国产99久久久久久福利 | 国产69久久久欧美一级 | 天天射天天操天天色 | 中文字幕丝袜一区二区 | 精品国产一二三四区 | 国产韩国精品一区二区三区 | 亚洲精品美女免费 | 国产麻豆精品免费视频 | www.91国产| 国产黄视频在线观看 | 免费能看的黄色片 | 91久久国产综合精品女同国语 | 成年人视频免费在线播放 | 一区二区三区免费在线 | 国产盗摄精品一区二区 | 亚洲狠狠操 | 久久黄网站| 88av视频 | 欧美日韩免费视频 | 日本高清dvd| 久久亚洲综合色 | 91成人精品观看 | 日韩免费av网址 | 久久久亚洲国产精品麻豆综合天堂 | 成人啪啪18免费游戏链接 | 手机在线视频福利 | 人人澡超碰碰97碰碰碰软件 | 中文字幕在线专区 | 在线看国产精品 | 国产成人一区二区三区影院在线 | 国产高清免费在线观看 | 高清久久久 | 97视频免费观看 | 五月天亚洲激情 | 7799av| 日本高清久久久 | 国产精品高清在线 | 综合在线色 | 91精品久久久久久综合乱菊 | 欧美九九九 | 日韩亚洲在线观看 | 俺要去色综合狠狠 | 成年美女黄网站色大片免费看 | 欧美日韩99 | 国产色资源 | 成人在线视频网 | 久久久午夜剧场 | 九九热在线观看 | 亚洲精品一区二区三区四区高清 | 麻豆影视在线免费观看 | 久久精品91视频 | 中文字幕乱偷在线 | 欧美日韩国产精品一区二区亚洲 | 久久精品国产一区二区 | 91丨九色丨国产丨porny精品 | 亚洲综合激情 | 欧美日韩a视频 | 久要激情网 | 天天操天天舔天天爽 | 很黄很污的视频网站 | 91日本在线播放 | 天天爽网站| 国产一区二区免费看 | 人人爽人人爽人人爽 | 久久综合久久综合久久 | 亚洲成人黄色网址 | 一区二区不卡 | 国产黄色片一级三级 | 999精品| 在线免费视频 你懂得 | 日本三级人妇 | 人人爽久久涩噜噜噜网站 | 国产在线2020 | 午夜国产在线 | 97视频免费看 | 免费在线激情电影 | 国产h在线观看 | 伊人天天综合 | 在线观看免费av网站 | 成人免费电影 | 在线看av的网址 | 91精品天码美女少妇 | 麻豆影视在线播放 | 五月婷网 | 国产精品毛片一区 | 亚洲精品视频免费在线 | 国产精品99精品久久免费 | 欧美综合色在线图区 | 欧美一区日韩精品 | 98涩涩国产露脸精品国产网 | 国产成免费视频 | 免费男女网站 | 中文在线字幕观看电影 | 懂色av懂色av粉嫩av分享吧 | 色吊丝在线永久观看最新版本 | 久久精品一区二区国产 | 最近高清中文字幕 | 国产午夜三级一区二区三桃花影视 | 国产99久久久国产精品成人免费 | 狠狠操操| 久99热| 中文字幕在线播放第一页 | 黄色资源在线观看 | 色亚洲激情| 日韩成人精品 | 91完整版观看 | 日日干夜夜爱 | 在线视频 91 | 日韩伦理片hd | 日本激情视频中文字幕 | 免费看的国产视频网站 | 91色视频 | 久热香蕉视频 | 婷婷国产在线观看 | 二区在线播放 | 国产亚洲成人网 | 亚洲在线视频观看 | 亚洲综合干 | 日韩丝袜视频 | 日批视频在线播放 | 欧美精品999| 干干干操操操 | 狠狠躁夜夜躁人人爽超碰91 | 91精品国 | 欧美成人91| 国产一级视屏 | 国产精品99久久久久久武松影视 | 97视频免费观看 | .国产精品成人自产拍在线观看6 | aav在线 | 日本一区二区高清不卡 | 激情久久小说 | av中文字幕av | 亚洲2019精品 | 亚洲免费精品视频 | 青青草在久久免费久久免费 | 国产艹b视频 | 99久久99久久精品国产片 | 国产精品99精品 | 91麻豆产精品久久久久久 | 中文字幕乱码亚洲精品一区 | 丁香五月缴情综合网 | 中文字幕 二区 | 久久情网 | 精品美女国产在线 | 成人黄色片免费 | 91香蕉视频色版 | 日韩av影视在线 | 亚洲,播放 | 国产在线观看污片 | 波多野结衣视频一区 | 国产精品理论片在线播放 | 综合铜03| 国产精品久久久久永久免费观看 | 欧美大片在线看免费观看 | 婷婷五月在线视频 | 久久成人在线视频 | 丁香六月欧美 | 中国一级片在线观看 | 九九热在线精品 | 中文资源在线观看 | 日韩精品在线免费播放 | 99视频在线观看一区三区 | 五月婷婷中文字幕 | 丝袜护士aⅴ在线白丝护士 天天综合精品 | 在线观看日韩免费视频 | www.com在线观看 | 国产91九色视频 | 国产黑丝一区二区三区 | 一区二区精品视频 | 亚洲欧洲精品一区二区 | 91香蕉视频在线下载 | 精品在线不卡 | 久人人| 91在线看黄 | 综合久久一本 | 爱爱av在线 | 欧美成人精品三级在线观看播放 | 精品在线看 | 精品一区二区三区在线播放 | 亚洲人久久| av丁香| 国产精品99久久久久久宅男 | 久久亚洲欧美日韩精品专区 | 在线观看视频一区二区三区 | 欧美久久久久久久 | 色射色| 日韩免费观看视频 | 日韩亚洲国产中文字幕 | 精品久久久网 | 欧美在线观看禁18 | 久久久久免费精品视频 | 国产精品久久久久免费 | 超碰在线公开 | 久久经典国产视频 | 成年人在线免费看片 | 九七人人干 | 日韩大片免费观看 | 五月婷亚洲 | www.婷婷com| 日本三级在线观看中文字 | 狠狠干天天 | 高清av免费观看 | 免费久久网 | 欧美日韩免费视频 | 久久综合久久伊人 | 色婷婷视频 | 西西人体www444| 伊人va| 国产91精品在线播放 | 久久视频在线看 | 欧美精品中文字幕亚洲专区 | 色婷婷综合成人av | 日韩欧三级 | 天天干,天天操 | 中文字幕韩在线第一页 | 国产亚洲精品久久久久久久久久 | 四虎成人精品永久免费av九九 | 国产高清视频在线观看 | 日韩中文字幕在线观看 | 久草精品在线 | 激情网在线视频 | 8090yy亚洲精品久久 | 日韩精品一区二区久久 | 精品美女国产在线 | 91香蕉久久 | 国产成人333kkk| 九九精品视频在线观看 | 日韩电影在线视频 | a视频免费在线观看 | 国产乱视频 | 日韩在线电影观看 | 曰韩在线| 日本公妇在线观看 | 欧美日韩高清国产 | 91麻豆精品久久久久久 | 国产精品嫩草69影院 | 91精彩在线视频 | 精品国产激情 | 91视频免费播放 | 国产精品欧美久久久久三级 | 日韩精品久久久免费观看夜色 | 97国产大学生情侣白嫩酒店 | 91在线porny国产在线看 | 国产伦理久久精品久久久久_ | 五月精品 | 久久久黄色av | 久久综合狠狠综合久久狠狠色综合 | 精品亚洲视频在线观看 | 国产一区二区高清 | 日韩免费观看一区二区 | 人人插人人插 | 国产精品黄色在线观看 | 久久久www成人免费毛片麻豆 | 超碰在线97观看 | 91亚洲在线 | 国产精品久久久久一区二区 | 在线观看视频国产 | 99国产成+人+综合+亚洲 欧美 | 国产精品乱码久久久久 | 波多野结衣精品 | 国产专区在线播放 | 天天干天天看 | 美女精品 | 在线观看一区二区视频 | 久久久久免费 | 久久精品国产成人精品 | 久久久久免费精品视频 | 亚洲精品人人 | 国产精品久久久久久久久久久免费 | 欧美做受xxx | 91欧美国产 | 国产美女久久久 | av短片在线观看 | 国产亚洲视频在线免费观看 | 亚洲春色综合另类校园电影 | 日本视频精品 | 欧美精品久久久久 | 91av久久| 视频一区二区在线 | 国产精品一区二区中文字幕 | 一级黄视频 | 91黄色在线看 | 欧美激情精品久久久久 | 精品在线播放视频 | 日日操操操| 日韩高清激情 | 日韩高清成人在线 | 99精品视频在线免费观看 | 午夜在线看 | 超碰在线国产 | 91爱爱中文字幕 | 天天插狠狠插 | 91色网址 | 深夜视频久久 | 日本黄色a级大片 | 在线a人片免费观看视频 | 色综合天天综合在线视频 | 天天色草 | 久久久久在线观看 | 黄色国产在线 | 亚洲欧洲国产日韩精品 | 成年人免费观看在线视频 | 亚洲国产精品va在线看 | av在线免费播放 | 日韩午夜电影网 | 中文字幕一区二区三区乱码在线 | 日韩视 | 国产免费美女 | 久久视影 | 久久精品一区二区 | 精品国产一区二区三区四 | 精品欧美一区二区精品久久 | 中文视频一区二区 | 久久综合综合久久综合 | 久久国产免 | 久久国产精品视频观看 | 国内毛片毛片 | 久久图 | 91九色国产在线 | 麻花传媒mv免费观看 | 国产探花视频在线播放 | 久久精品视频99 | 91桃色免费视频 | 亚洲一区日韩 | 中文字幕精品一区二区三区电影 | 中文免费在线观看 | 婷婷综合影院 | www.午夜 | 久久精彩 | 国内外成人免费在线视频 | 中文区中文字幕免费看 | 日韩精品91偷拍在线观看 | 午夜精品久久久久99热app | 97精品伊人 | 黄网站色成年免费观看 | av 一区 二区 久久 | 久草视频中文 | 中文字幕你懂的 | 国产高清专区 | 日本公乱妇视频 | 亚洲精品视频网站在线观看 | 国产一区二区久久 | 射射射综合网 | 国产精品久久久久久久久久久久午夜 | 中文字幕在线影视资源 | 99热精品久久 | 久久免费视频这里只有精品 | 激情综合网色播五月 | 免费日韩视频 | 亚洲精品美女 | 黄色成人91 | 久久欧美综合 | 国产亚洲va综合人人澡精品 | 四虎在线永久免费观看 | 久久久蜜桃 | 精品一区二区三区在线播放 | 手机看片1042 | 久久精彩 | 精品国产伦一区二区三区免费 | 亚洲精品在线免费看 | 久草在线一免费新视频 | 白丝av免费观看 | av在线等| 国产免费激情久久 | 丁香六月久久综合狠狠色 | 亚洲国产精品va在线看黑人 | 日韩中文三级 | 午夜三级在线 | 人人草在线视频 | 成人免费 在线播放 | 伊人中文在线 | 欧美日韩网站 | 六月色婷婷 | 成人一级视频在线观看 | 在线观看中文字幕网站 | 日韩在线免费小视频 | 激情偷乱人伦小说视频在线观看 | 久久久久久久综合色一本 | 91av色 | 亚洲五月| 天天做日日爱夜夜爽 | 国产精品一区欧美 | 在线观看日韩av | 日日干天天插 | 久免费视频| 中文字幕免费播放 | 一区二区亚洲精品 | 成年美女黄网站色大片免费看 | 久久免费观看视频 | 91tv国产成人福利 | 粉嫩高清一区二区三区 | 人人干在线观看 | 免费午夜视频在线观看 | 天堂av在线网址 | 日韩视频一区二区三区在线播放免费观看 | 国产黑丝一区二区三区 | 日韩免费视频在线观看 | 91精品看片 | 激情欧美网 | 精品国产一区二区三区久久久久久 | 最新av免费在线 | 久久精品国产一区二区三 | 日韩精品一二三 | 五月婷婷激情六月 | 国产无遮挡猛进猛出免费软件 | 久久99精品久久久久久久久久久久 | 欧美日韩在线精品一区二区 | 97成人在线免费视频 | 丝袜精品视频 | 六月天色婷婷 | 五月激情丁香图片 | 亚洲精品1区2区3区 超碰成人网 | 亚洲免费观看在线视频 | 久久www免费人成看片高清 | 国产精品久久在线观看 | 色综合久久88色综合天天免费 | 免费a视频在线观看 | 久久99国产精品自在自在app | 成年人在线观看 | 国内久久久 | 午夜av在线 | 免费在线观看毛片网站 | 欧洲视频一区 | 91看片看淫黄大片 | 亚洲精品久久久久久久蜜桃 | 欧美另类xxxx | 欧美日韩精品在线观看视频 | 在线 视频 一区二区 | 少妇性xxx | 在线91色 | 狠狠躁18三区二区一区ai明星 | 在线中文字幕观看 | 看黄色.com | 精品福利视频在线 | 国产香蕉97碰碰久久人人 | 成人动漫一区二区三区 | 亚洲国产精彩中文乱码av | 黄色小视频在线观看免费 | 久久久免费少妇 | 久操操| 激情五月网站 | 中文字幕第 | 国内精品久久久久影院日本资源 | 国产原创在线视频 | 国产在线一区二区 | 国产精品videossex国产高清 | 麻豆成人精品视频 | 99re在线视频观看 | 午夜av在线电影 | 免费试看一区 | 99精品在线免费视频 | 丁香激情五月 | 国产综合香蕉五月婷在线 | 日韩av成人在线观看 | www.91成人 | 人人干网 | 国产精品免费看 | 国产视频在线观看一区 | 久久三级毛片 | 午夜视频亚洲 | 国产在线最新 | 最近2019年日本中文免费字幕 | 在线观av| 亚洲电影一区二区 | 欧美日韩伦理一区 | 国产一区在线观看视频 | 91夜夜夜 | 在线黄av| 午夜三级毛片 | 中文字幕av免费观看 | 欧美黑人性爽 | 日日干网址 | 日韩视频免费在线 | 自拍超碰在线 | avwww在线| 国产在线免费 | 中文字幕丝袜一区二区 | 九九热精品在线 | 国产日韩精品一区二区在线观看播放 | 亚洲最大在线视频 | 在线看片一区 | 婷婷激情在线 | 亚洲影视资源 | 国产经典av | 国产婷婷| 一区二区免费不卡在线 | 色九色 | 五月天综合激情网 | 国产美女网站在线观看 | 在线性视频日韩欧美 | 亚洲午夜久久久久久久久电影网 | 亚洲va欧美va | 午夜18视频在线观看 | 日本精品久久久久影院 | 综合色播| 91在线视频播放 | 黄色影院在线观看 | 免费日韩 精品中文字幕视频在线 | 亚洲精品三级 | 午夜av免费 | 操操综合 | www.国产在线| 亚洲国产精品99久久久久久久久 | 黄色一级免费电影 | 麻豆传媒在线视频 | 亚洲中字幕| 日韩精品中文字幕在线不卡尤物 | 久久精品国产精品亚洲 | av在线电影免费观看 | 国产黄色精品在线 | 麻豆精品91| 国产区精品视频 | 亚洲成人av在线电影 | 怡红院av久久久久久久 | 色夜影院 | 激情综合狠狠 | 人人爽人人看 | 免费av在线网 | 午夜精品一区二区国产 | 免费在线观看a v | 久久久久久久久毛片精品 | 五月天久久综合网 | 久久99电影| 黄色av在| 精品国产美女在线 | 91精品少妇偷拍99 | 一区二区视频免费在线观看 | 美女网站黄在线观看 | 日韩成人在线免费观看 | 国产精品系列在线观看 | 久久久精品欧美 | 这里只有精品视频在线观看 | 欧美va日韩va| 天堂中文在线视频 | 亚洲国产日韩一区 | 久久精品欧美日韩精品 | 欧美激情视频在线免费观看 | 亚洲精品国精品久久99热一 | 精品久久1| 精品国产一区二区三区在线 | 中文字幕一区三区 | 欧美日韩不卡在线 | 天天操导航 | 国产精品免费麻豆入口 | 就要干b| 香蕉视频免费看 | 国产人成一区二区三区影院 | 2024国产精品视频 | 999ZYZ玖玖资源站永久 | 免费观看全黄做爰大片国产 | 黄色tv视频 | 日韩网站在线播放 | 久久精品一区二区三区国产主播 | 成人资源网 | 久久激情日本aⅴ | 丰满少妇在线观看 | 少妇超碰在线 | 在线免费观看涩涩 | 男女啪啪免费网站 | 98超碰在线 | 免费观看成人av | 91福利影院在线观看 | 韩日精品中文字幕 | 日韩高清毛片 | 中文字幕免 | 亚洲精品啊啊啊 | 日韩精品在线一区 | 亚洲国产欧美在线人成大黄瓜 | 中文国产成人精品久久一 | 香蕉久久久久久av成人 | 久久久久久久久免费视频 | 中文字幕在线免费看线人 | 日韩电影精品 | 激情av资源 | 国产一级免费片 | 最近中文字幕免费观看 | 国产精品久久久久久久久大全 | 国产99久久九九精品免费 | 国产亚洲精品久久久久秋 | 国产在线观看,日本 | 91亚洲精品久久久蜜桃借种 | 国产精品青草综合久久久久99 | 免费看一及片 | 黄在线| 久草精品免费 | 97在线视频观看 | 久久伦理视频 | 亚洲三级网 | 99久热在线精品视频 | 亚洲精品视频免费在线观看 | 国产三级精品三级在线观看 | 久久社区视频 | 有码一区二区三区 | 国产一级在线看 | 美国av片在线观看 | 婷婷国产一区二区三区 | 免费看污的网站 | 国产一区二区日本 | 天堂中文在线视频 | 亚洲区色 | 国产精品久久久久久一区二区 | 日韩av在线小说 | 麻豆播放 | 91爱在线 | 成人黄色大片在线免费观看 | 国产精品免费一区二区 | 国产一区二区三区免费在线观看 | 久久不卡日韩美女 | 激情婷婷亚洲 | 99精品免费久久久久久久久日本 | 国产精品11 | 天天爽天天摸 | 免费视频色 | 亚洲爱爱视频 | 天天射狠狠干 | 色婷婷激情五月 | 国产福利一区二区在线 | 在线国产高清 | 亚洲91中文字幕无线码三区 | 在线免费观看国产黄色 | 日本久久久亚洲精品 | 久久精品7 | 久久久久久免费视频 | 久久影院精品 | 久草视频在线免费 | 日韩网站在线播放 | 国内精品视频免费 | 玖玖视频 | 操处女逼| 精品在线观看一区二区 | 亚洲欧美日本A∨在线观看 青青河边草观看完整版高清 | 一区二区三区四区五区六区 | 亚洲综合在线五月天 | 亚洲国产一二三 | 国产精品2区 | 国产一级a毛片视频爆浆 | 日韩电影在线一区二区 | 国产精品久久中文字幕 | 免费视频xnxx com | 一色屋精品视频在线观看 | 亚洲国产婷婷 | 在线视频你懂得 | 亚洲午夜久久久久久久久电影网 | 久久久国产精品久久久 | 99精品视频在线观看免费 | 日韩午夜三级 | 国产精品21区 | 香蕉视频导航 | 亚洲精品视频在线观看视频 | 色综合色综合色综合 | 国产成人一区二区啪在线观看 | 精品国产aⅴ一区二区三区 在线直播av | 国产精品av免费在线观看 | 天天色天天操天天爽 | 在线视频久久 | 免费在线观看中文字幕 | 中文字幕二区在线观看 | 精品视频区 | 91在线亚洲 |