Python運(yùn)維項(xiàng)目中用到的redis經(jīng)驗(yàn)及數(shù)據(jù)類型
先感嘆下,學(xué)東西一定要活學(xué)活用! 我用redis也有幾年的歷史了,今個(gè)才想到把集合可以當(dāng)python list用。 最近做了幾個(gè)項(xiàng)目都摻雜了redis, 遇到了一些個(gè)問題和開發(fā)中提高性能的方法,這都分享出來,共同學(xué)習(xí)。
下面先簡(jiǎn)單講講Redis集合的數(shù)據(jù)類型。
Sets 就是一個(gè)集合,集合的概念就是一堆不重復(fù)值的組合。利用Redis提供的Sets數(shù)據(jù)結(jié)構(gòu),可以存儲(chǔ)一些集合性的數(shù)據(jù),比如在微博應(yīng)用中,可以將一個(gè)用戶所有的關(guān)注人存在一個(gè)集合中,將其所有粉絲存在一個(gè)集合。Redis還為集合提供了求交集、并集、差集等操作,可以非常方便的實(shí)現(xiàn)如共同關(guān)注、共同喜好、二度好友等功能,對(duì)上面的所有集合操作,你還可以使用不同的命令選擇將結(jié)果返回給客戶端還是存集到一個(gè)新的集合中。 上面說的是新浪微博的應(yīng)用。
sadd,創(chuàng)建一個(gè)集合,并添加數(shù)據(jù)。
- [root@66 ~]# redis-cli
- redis 127.0.0.1:6379>
- redis 127.0.0.1:6379>
- redis 127.0.0.1:6379> sadd xiaorui aaa
- (integer) 1
- redis 127.0.0.1:6379> sadd xiaorui bbb
- (integer) 1
- redis 127.0.0.1:6379> sadd xiaorui ccc
- (integer) 1
- redis 127.0.0.1:6379>
- redis 127.0.0.1:6379> SMEMBERS xiaorui
- 1) "aaa"
- 2) "ccc"
- 3) "bbb"
- redis 127.0.0.1:6379>
- redis 127.0.0.1:6379>
set集合是不能寫重復(fù)的內(nèi)容的
- redis 127.0.0.1:6379> sadd xiaorui fuck_shencan
- (integer) 1
- redis 127.0.0.1:6379> sadd xiaorui fuck_shencan
- (integer) 0
- redis 127.0.0.1:6379>
查看集合的大小
- redis 127.0.0.1:6379> SCARD xiaorui
- (integer) 3
- redis 127.0.0.1:6379>
刪除
- redis 127.0.0.1:6379> SREM xiaorui aaa
- (integer) 1
- redis 127.0.0.1:6379> SMEMBERS xiaorui
- 1) "ccc"
- 2) "bbb"
- redis 127.0.0.1:6379>
兩個(gè)集合的交集之處
- redis 127.0.0.1:6379> SADD key1 a
- (integer) 1
- redis 127.0.0.1:6379> SADD key1 b
- (integer) 1
- redis 127.0.0.1:6379> SADD key1 c
- (integer) 1
- redis 127.0.0.1:6379> SADD key2 c
- (integer) 1
- redis 127.0.0.1:6379> SADD key2 d
- (integer) 1
- redis 127.0.0.1:6379> SADD key2 e
- (integer) 1
- redis 127.0.0.1:6379> SINTER key1 key2
- 1) "c"
- redis 127.0.0.1:6379>
可以把集合當(dāng)成redis list隊(duì)列用,需要注意的是set集合的成員模式是不能有重復(fù)的值的。如果你的值不重復(fù),你又蛋疼,還真的可以把set集合當(dāng)成隊(duì)列使用。
- redis 127.0.0.1:6379> sadd myset one
- (integer) 1
- redis 127.0.0.1:6379> sadd myset two
- (integer) 1
- redis 127.0.0.1:6379> sadd myset three
- (integer) 1
- redis 127.0.0.1:6379> SPOP myset
- "one"
- redis 127.0.0.1:6379> SMEMBERS myset
- 1) "three"
- 2) "two"
- redis 127.0.0.1:6379>
前兩天和朋友說,我那監(jiān)控平臺(tái)的內(nèi)存吃的厲害,他一下子蹦出一句,redis吃內(nèi)存肯定很大了。。。 nima,哥只是用他的大隊(duì)列。這里說下,redis做隊(duì)列的強(qiáng)度。一把來說100w條的隊(duì)列數(shù)據(jù),占用73M 內(nèi)存左 右。200w條數(shù)據(jù)內(nèi)存在154M內(nèi)存左右。
redis的堵塞取任務(wù),最好少用,超過5個(gè)線程去brpop的話,會(huì)把redis的cpu使用率頂?shù)?0%左右,而且嚴(yán)重會(huì)影響別的進(jìn)程的訪問,如果確定任務(wù)不是每時(shí)每刻都有的情況下,最好在你的程序控制下他的訪問頻次和時(shí)間的間隔。
python處理redis的時(shí)候,最好要用pool,速度和資源明顯的節(jié)省。
- >>> pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
- >>> r = redis.Redis(connection_pool=pool)
新版的redis是支持管道的,pipline ! 有朋友不太理解,這里的管道有什么好處。 pyhton 雖然連接redis的時(shí)候用了連接池,但是這也只是連接方面做了keepalive而已,但是每次的命令推送,他還是一次命令一個(gè)交互的。 用了pipline管道堵塞后,他會(huì)把所有的命令合成一個(gè)管道符推送到redis服務(wù)端。這樣的話就省事了很多。 這個(gè)特別適用于并發(fā)大的時(shí)候。
對(duì)于redis的pub sub通信性能的問題,可以用gevent來搞定。直接導(dǎo)入gevent猴子就可以了。
- import gevent.monkey
- gevent.monkey.patch_all()
- #http://rfyiamcool.blog.51cto.com/1030776/1435539
- import os
- import sys
- import fcntl
- import gevent
- from gevent.socket import wait_read
- from redis import Redis
- PID = os.getpid()
- red = Redis('localhost')
- def echo_stdin():
- # make stdin non-blocking
- fcntl.fcntl(sys.stdin, fcntl.F_SETFL, os.O_NONBLOCK)
- red.publish('echo', "[%i] joined" % (PID,))
- while True:
- wait_read(sys.stdin.fileno())
- l = sys.stdin.readline().strip()
- s = "[%i] %s" % (PID, l)
- # save to log
- red.rpush('echo_log', s)
- # publish message
- red.publish('echo', s)
- if l == 'quit':
- break
- def handler():
- pubsub = red.pubsub()
- # first subscribe, then print log (no race condition this way)
- pubsub.subscribe('echo')
- # print log
- for line in red.lrange('echo_log', 0, -1):
- print '.', line
- # print channel
- for msg in pubsub.listen():
- print '>', msg['data']
- gevent.spawn(handler)
- gevent.spawn(echo_stdin).join()
當(dāng)然對(duì)于普通的set get sadd hset 也是可以配合redis來使用的。但是,沒啥優(yōu)勢(shì),因?yàn)閞edis只啟用了一個(gè)進(jìn)程針對(duì)數(shù)據(jù)的讀寫,咱們從程序中復(fù)用的那幾個(gè)連接,最后取數(shù)據(jù),還是需要調(diào)用那進(jìn)程,你還不如讓他老老實(shí)實(shí)的干活,別搞個(gè)多線程,讓他白白折騰。 我這邊做了壓力測(cè)試,python2.7用個(gè)gevent后,批量的讀寫沒什么突出的增長。
- >>> import geventredis
- >>> redis_client = geventredis.connect('127.0.0.1', 6379)
- >>> redis_client.set('foo', 'bar')
- 'OK'
- >>> for msg in redis_client.monitor():
- print msg