redis 原理+命令

今天遇到了一個前同事挖的坑,刷新緩存中商品信息時先讓key過時,而後從數據庫裏取最新數據而後再放到緩存中,他是這樣寫的html

redisTemplate.expire(CacheConst.GOOGS_PREFIX,1,TimeUnit.MILLISECONDS);java

設置key過時爲一毫秒,致使緩存中有時沒有商品信息,由於在這一毫秒內有可能已經從數據庫中取到了最新數據,而且又放到了緩存中,一毫秒事後key過時了,緩存中就沒了商品信息。python

正確的應該這樣寫redisTemplate.expire(CacheConst.GOOGS_PREFIX,-1,TimeUnit.MILLISECONDS)立馬讓key過時。mysql

redis中對於有設置過時的key有三種處理方式linux

  1. 被動刪除:這個key下一次被訪問到的時候纔會刪除。
  2. 主動刪除:Redis會按期主動淘汰一批已過時的key
  3. 當前已用內存超過maxmemory限定時,觸發主動清理策略

直接刪除大key是有風險的,key過大,直接刪除時會致使Redis阻塞,不一樣類型的大key有不一樣的刪除方式,git

Large Hash Key 可以使用hscan命令,每次獲取500個字段,再用hdel命令,每次刪除1個字段。github

Large Set Key 可以使用sscan命令,每次掃描集合中500個元素,再用srem命令每次刪除一個鍵。redis

Large List Key可經過ltrim命令每次刪除少許元素算法

Large Sorted Set Key使用sortedset自帶的zremrangebyrank命令,每次刪除top 100個元素sql

 

目錄

redis主從遇到的兩個坑 過時key的處理... 1

Redis從節點和主節點中key的數量不一樣是爲何... 2

主從複製原理圖... 3

Redis複製機制的缺陷... 3

注意事項... 4

Python標準庫系列之Redis模塊... 5

Redis經常使用命令... 5

1)鏈接操做命令... 5

2)持久化... 5

3)遠程服務控制... 5

4)對value操做的命令... 5

5)String. 6

6)List. 6

7)Set. 7

8)Hash. 7

Redis高級應用... 8

一、安全性... 8

二、主從複製... 8

三、事務處理... 8

四、持久化機制... 9

redis主從遇到的兩個坑 過時key的處理

最近在使用redis主從的時候作了下面兩件事情:

1 但願redis主從從操做上分析,全部寫操做都在master上寫,全部讀操做都在從上讀。

2 因爲redis的從是放在本地的,因此有的key的讀寫操做就直接放在從上操做了。

可是出現了下面的幾個問題:

1 在主上setex的key即便過時後在從上也始終get的到。

重現:

主: setex abc 20 test

從:

get abc >> test

ttl abc >> 18

...

ttl abc >> -1

get abc >> test (這裏居然還有~!)

主:get  abc >> nil

從:get abc >> nil

因此若是隻在從上獲取一個key須要根據get+ttl來判斷一個key是否已通過期

查了下,也有人吐槽這個問題:http://code.google.com/p/redis/issues/detail?id=519

2 在從上進行讀寫操做,過時時間不生效

重現:

redis 127.0.0.1:6379> get abctest 
(nil) 
redis 127.0.0.1:6379> setex abctest 20 test 
OK 
redis 127.0.0.1:6379> get abctest 
"test" 
redis 127.0.0.1:6379> ttl abctest 
(integer) 10 
redis 127.0.0.1:6379> ttl abctest 
(integer) -1 
redis 127.0.0.1:6379> get abctest 
"test"  (這裏居然還取得出來。。)

分析

這個現象就是像說從歷來不負責刪除key,刪除key只是主負責的。而因爲redis自身刪除key的機制是

1 隨機選取必定比例的過時key

2 get觸發過時刪除。

        這個函數的意圖已經有說明:刪一點點過時key,若是過時key較少,那也只用一點點cpu。25行隨機取一個key,38行刪key成功的機率較低就退出。這個函數被放在一個cron裏,每毫秒被調用一次。這個算法保證每次會刪除必定比例的key,可是若是key總量很大,而這個比例控制的太大,就須要更屢次的循環,浪費cpu,控制的過小,過時的key就會變多,浪費內存——這就是時空權衡了。

       以上3種刪除過時key的途徑,第二種按期刪除必定比例的key是主要的刪除途徑,第一種「讀時刪除」保證過時key不會被訪問到,第三種是一個當內存超出設定時的暴力手段。由此也能看出redis設計的巧妙之處。
       這下知道爲啥從從redis中能訪問到過時的key了,雖然從redis知道key過時,但沒有權限刪除

因此致使在master上設置了過時的key若是不在master上觸發上面兩個條件,在從中就永遠會被取到。。。

這真是個很容易踩到的坑啊。。。

 

Redis從節點和主節點中key的數量不一樣是爲何

原文地址:http://Redis.io/topics/faq

My slave claims to have a different number of keys compared to its master, why?

If you use keys with limited time to live (redis expires) this is normal behavior. This is what happens:

The master generates an RDB file on the first synchronization with the slave.

The RDB file will not include keys already expired in the master, but that are still in memory.

However these keys are still in the memory of the Redis master, even if logically expired. They'll not be considered as existing, but the memory will be reclaimed later, both incrementally and explicitly on access. However while these keys are not logical part of the dataset, they are advertised in INFO output and by the DBSIZE command.

When the slave reads the RDB file generated by the master, this set of keys will not be loaded.

As a result of this, it is common for users with many keys with an expire set to see less keys in the slaves, because of this artifact, but there is no actual logical difference in the instances content.

  主從複製原理圖

 

Redis複製機制的缺陷

從上面的流程能夠看出,Slave從庫在鏈接Master主庫時,Master會進行內存快照,而後把整個快照文件發給Slave,也就是沒有象MySQL那樣有複製位置的概念,即無增量複製,這會給整個集羣搭建帶來很是多的問題。

好比一臺線上正在運行的Master主庫配置了一臺從庫進行簡單讀寫分離,這時Slave因爲網絡或者其它緣由與Master斷開了鏈接,那麼當Slave進行從新鏈接時,須要從新獲取整個Master的內存快照,Slave全部數據跟着所有清除,而後從新創建整個內存表,一方面Slave恢復的時間會很是慢,另外一方面也會給主庫帶來壓力。

因此基於上述緣由,若是你的Redis集羣須要主從複製,那麼最好事先配置好全部的從庫,避免中途再去增長從庫。

Redis複製機制的缺陷

從上面的流程能夠看出,Slave從庫在鏈接Master主庫時,Master會進行內存快照,而後把整個快照文件發給Slave,也就是沒有象MySQL那樣有複製位置的概念,即無增量複製,這會給整個集羣搭建帶來很是多的問題。

好比一臺線上正在運行的Master主庫配置了一臺從庫進行簡單讀寫分離,這時Slave因爲網絡或者其它緣由與Master斷開了鏈接,那麼當Slave進行從新鏈接時,須要從新獲取整個Master的內存快照,Slave全部數據跟着所有清除,而後從新創建整個內存表,一方面Slave恢復的時間會很是慢,另外一方面也會給主庫帶來壓力。

因此基於上述緣由,若是你的Redis集羣須要主從複製,那麼最好事先配置好全部的從庫,避免中途再去增長從庫。

Cache仍是Storage

在咱們分析過了Redis的複製與持久化功能後,咱們不可貴出一個結論,實際上Redis目前發佈的版本還都是一個單機版的思路,主要的問題集中在,持久化方式不夠成熟,複製機制存在比較大的缺陷,這時咱們又開始從新思考Redis的定位:Cache仍是Storage?

若是做爲Cache的話,彷佛除了有些很是特殊的業務場景,必需要使用Redis的某種數據結構以外,咱們使用Memcached可能更合適,畢竟Memcached不管客戶端包和服務器自己更久經考驗。

若是是做爲存儲Storage的話,咱們面臨的最大的問題是不管是持久化仍是複製都沒有辦法解決Redis單點問題,即一臺Redis掛掉了,沒有太好的辦法可以快速的恢復,一般幾十G的持久化數據,Redis重啓加載須要幾個小時的時間,而複製又有缺陷,如何解決呢?

Redis可擴展集羣搭建

1. 主動複製避開Redis複製缺陷。

既然Redis的複製功能有缺陷,那麼咱們不妨放棄Redis自己提供的複製功能,咱們能夠採用主動複製的方式來搭建咱們的集羣環境。

所謂主動複製是指由業務端或者經過代理中間件對Redis存儲的數據進行雙寫或多寫,經過數據的多份存儲來達到與複製相同的目的,主動複製不只限於用在Redis集羣上,目前不少公司採用主動複製的技術來解決MySQL主從之間複製的延遲問題,好比Twitter還專門開發了用於複製和分區的中間件gizzard(https://github.com/twitter/gizzard) 。

主動複製雖然解決了被動複製的延遲問題,但也帶來了新的問題,就是數據的一致性問題,數據寫2次或屢次,如何保證多份數據的一致性呢?若是你的應用對數據一致性要求不高,容許最終一致性的話,那麼一般簡單的解決方案是能夠經過時間戳或者vector clock等方式,讓客戶端同時取到多份數據並進行校驗,若是你的應用對數據一致性要求很是高,那麼就須要引入一些複雜的一致性算法好比Paxos來保證數據的一致性,可是寫入性能也會相應降低不少。

經過主動複製,數據多份存儲咱們也就再也不擔憂Redis單點故障的問題了,若是一組Redis集羣掛掉,咱們可讓業務快速切換到另外一組Redis上,下降業務風險。

注意事項

http://www.infoq.com/cn/articles/tq-redis-copy-build-scalable-cluster

Redis的複製功能沒有增量複製,每次重連都會把主庫整個內存快照發給從庫,因此須要避免向在線服務的壓力較大的主庫上增長從庫。

Redis的複製因爲會使用快照持久化方式,因此若是你的Redis持久化方式選擇的是日誌追加方式(aof),那麼系統有可能在同一時刻既作aof日誌文件的同步刷寫磁盤,又作快照寫磁盤操做,這個時候Redis的響應能力會受到影響。因此若是選用aof持久化,則加從庫須要更加謹慎。

可使用主動複製和presharding方法進行Redis集羣搭建與在線擴容。

Python標準庫系列之Redis模塊

http://python.jobbole.com/87305/

Redis經常使用命令

1)  鏈接操做命令

 

quit:關閉鏈接(connection)

auth:簡單密碼認證

help cmd: 查看cmd幫助,例如:help quit

http://www.jb51.net/article/61793.htm

 

2)持久化

save:將數據同步保存到磁盤

bgsave:將數據異步保存到磁盤

lastsave:返回上次成功將數據保存到磁盤的Unix時戳

shundown:將數據同步保存到磁盤,而後關閉服務


3)遠程服務控制

info:提供服務器的信息和統計

monitor:實時轉儲收到的請求

slaveof:改變複製策略設置

config:在運行時配置Redis服務器


4)對value操做的命令

exists(key):確認一個key是否存在

del(key):刪除一個key

type(key):返回值的類型

keys(pattern):返回知足給定pattern的全部key

randomkey:隨機返回key空間的一個

keyrename(oldname, newname):重命名key

dbsize:返回當前數據庫中key的數目

expire:設定一個key的活動時間(s)

ttl:得到一個key的活動時間

select(index):按索引查詢

move(key, dbindex):移動當前數據庫中的key到dbindex數據庫

flushdb:刪除當前選擇數據庫中的全部key

flushall:刪除全部數據庫中的全部key


5)String

set(key, value):給數據庫中名稱爲key的string賦予值value

get(key):返回數據庫中名稱爲key的string的value

getset(key, value):給名稱爲key的string賦予上一次的value

mget(key1, key2,…, key N):返回庫中多個string的value

setnx(key, value):添加string,名稱爲key,值爲value

setex(key, time, value):向庫中添加string,設定過時時間time

mset(key N, value N):批量設置多個string的值

msetnx(key N, value N):若是全部名稱爲key i的string都不存在

incr(key):名稱爲key的string增1操做

incrby(key, integer):名稱爲key的string增長integer

decr(key):名稱爲key的string減1操做

decrby(key, integer):名稱爲key的string減小integer

append(key, value):名稱爲key的string的值附加value

substr(key, start, end):返回名稱爲key的string的value的子串


6)List 

rpush(key, value):在名稱爲key的list尾添加一個值爲value的元素

lpush(key, value):在名稱爲key的list頭添加一個值爲value的 元素

llen(key):返回名稱爲key的list的長度

lrange(key, start, end):返回名稱爲key的list中start至end之間的元素

ltrim(key, start, end):截取名稱爲key的list

lindex(key, index):返回名稱爲key的list中index位置的元素

lset(key, index, value):給名稱爲key的list中index位置的元素賦值

lrem(key, count, value):刪除count個key的list中值爲value的元素

lpop(key):返回並刪除名稱爲key的list中的首元素

rpop(key):返回並刪除名稱爲key的list中的尾元素

blpop(key1, key2,… key N, timeout):lpop命令的block版本。

brpop(key1, key2,… key N, timeout):rpop的block版本。

rpoplpush(srckey, dstkey):返回並刪除名稱爲srckey的list的尾元素,並將該元素添加到名稱爲dstkey的list的頭部


7)Set

sadd(key, member):向名稱爲key的set中添加元素member

srem(key, member) :刪除名稱爲key的set中的元素member

spop(key) :隨機返回並刪除名稱爲key的set中一個元素

smove(srckey, dstkey, member) :移到集合元素

scard(key) :返回名稱爲key的set的基數

sismember(key, member) :member是不是名稱爲key的set的元素

sinter(key1, key2,…key N) :求交集

sinterstore(dstkey, (keys)) :求交集並將交集保存到dstkey的集合

sunion(key1, (keys)) :求並集

sunionstore(dstkey, (keys)) :求並集並將並集保存到dstkey的集合

sdiff(key1, (keys)) :求差集

sdiffstore(dstkey, (keys)) :求差集並將差集保存到dstkey的集合

smembers(key) :返回名稱爲key的set的全部元素

srandmember(key) :隨機返回名稱爲key的set的一個元素


8)Hash

hset(key, field, value):向名稱爲key的hash中添加元素field

hget(key, field):返回名稱爲key的hash中field對應的value

hmget(key, (fields)):返回名稱爲key的hash中field i對應的value

hmset(key, (fields)):向名稱爲key的hash中添加元素field 

hincrby(key, field, integer):將名稱爲key的hash中field的value增長integer

hexists(key, field):名稱爲key的hash中是否存在鍵爲field的域

hdel(key, field):刪除名稱爲key的hash中鍵爲field的域

hlen(key):返回名稱爲key的hash中元素個數

hkeys(key):返回名稱爲key的hash中全部鍵

hvals(key):返回名稱爲key的hash中全部鍵對應的value

hgetall(key):返回名稱爲key的hash中全部的鍵(field)及其對應的value



 

Redis高級應用

一、安全性

    設置客戶端鏈接後進行任何操做指定前須要密碼,一個外部用戶能夠再一秒鐘進行150W次訪問,具體操做密碼修改設置redis.conf裏面的requirepass屬性給予密碼,固然我這裏給的是primos

以後若是想操做能夠採用登錄的時候就受權使用:

sudo /opt/java/redis/bin/redis-cli -a primos

或者是進入之後auth primos而後就能夠隨意操做了

 

二、主從複製

作這個操做的時候我準備了兩個虛擬機,ip分別是192.168.15.128和192.168.15.133

    經過主從複製能夠容許多個slave server擁有和master server相同的數據庫副本

具體配置是在slave上面配置slave

slaveof 192.168.15.128 6379

masterauth primos

若是沒有主從同步那麼就檢查一下是否是防火牆的問題,我用的是ufw,設置一下sudo ufw allow 6379就能夠了

這個時候能夠經過info查看具體的狀況

 

三、事務處理

redis對事務的支持還比較簡單,redis只能保證一個client發起的事務中的命令能夠連續執行,而中間不會插入其餘client的命令。當一個client在一個鏈接中發出multi命令時,這個鏈接會進入一個事務的上下文,鏈接後續命令不會當即執行,而是先放到一個隊列中,當執行exec命令時,redis會順序的執行隊列中的全部命令。

好比我下面的一個例子

set age 100

multi

set age 10

set age 20

exec

 

get age --這個內容就應該是20

multi

set age 20

set age 10

exec 

 

get age --這個時候的內容就成了10,充分體現了一下按照隊列順序執行的方式

discard  取消全部事務,也就是事務回滾

不過在redis事務執行有個別錯誤的時候,事務不會回滾,會把不錯誤的內容執行,錯誤的內容直接放棄,目前最新的是2.6.7也有這個問題的

 

樂觀鎖

watch key若是沒watch的key有改動那麼outdate的事務是不能執行的

 

四、持久化機制 

redis是一個支持持久化的內存數據庫

snapshotting快照方式,默認的存儲方式,默認寫入dump.rdb的二進制文件中,能夠配置redis在n秒內若是超過m個key被修改過就自動作快照

append-only file aof方式,使用aof時候redis會將每一次的函數都追加到文件中,當redis重啓時會從新執行文件中的保存的寫命令在內存中。

五、發佈訂閱消息 sbusribe publish操做,其實就相似linux下面的消息發佈

  訂閱命令:

SUBSCRIBE redisChat
發佈命令:
PUBLISH redisChat "Redis is a great caching technique"

六、虛擬內存的使用

能夠配置vm功能,保存路徑,最大內存上線,頁面多少,頁面大小,最大工做線程

臨時修改ip地址ifconfig eth0 192.168.15.129

 

Redis-cli參數

Usage: redis-cli [OPTIONS] [cmd [arg [arg ...]]]
  -h <hostname>    Server hostname (default: 127.0.0.1)
  -p <port>        Server port (default: 6379)
  -s <socket>      Server socket (overrides hostname and port)
  -a <password>    Password to use when connecting to the server
  -r <repeat>      Execute specified command N times
  -i <interval>    When -r is used, waits <interval> seconds per command.
                   It is possible to specify sub-second times like -i 0.1
  -n <db>          Database number
  -x               Read last argument from STDIN
  -d <delimiter>   Multi-bulk delimiter in for raw formatting (default: \n)
  -c               Enable cluster mode (follow -ASK and -MOVED redirections)
  --raw            Use raw formatting for replies (default when STDOUT is not a  tty)
  --latency        Enter a special mode continuously sampling latency
  --slave          Simulate a slave showing commands received from the master
  --pipe           Transfer raw Redis protocol from stdin to server
  --bigkeys        Sample redis keys looking for big keys
  --eval <file>    Send an EVAL command using the Lua script at <file>
  --help           Output this help and exit
  --version        Output version and exit

Examples:
  cat /etc/passwd | redis-cli -x set mypasswd
  redis-cli get mypasswd
  redis-cli -r 100 lpush mylist x
  redis-cli -r 100 -i 1 info | grep used_memory_human:
  redis-cli --eval myscript.lua key1 key2 , arg1 arg2 arg3
  (Note: when using --eval the comma separates KEYS[] from ARGV[] items)

 

 

經常使用命令:

1) 查看keys個數

keys *      // 查看全部keys

keys prefix_*     // 查看前綴爲"prefix_"的全部keys

 

2) 清空數據庫

flushdb   // 清除當前數據庫的全部keys

flushall    // 清除全部數據庫的全部keys

相關文章
相關標籤/搜索