Python運維項目中用到的redis經驗及數據類型
先感嘆下,學東西一定要活學活用! 我用redis也有幾年的歷史了,今個才想到把集合可以當python list用。 最近做了幾個項目都摻雜了redis, 遇到了一些個問題和開發中提高性能的方法,這都分享出來,共同學習。
下面先簡單講講Redis集合的數據類型。
Sets 就是一個集合,集合的概念就是一堆不重復值的組合。利用Redis提供的Sets數據結構,可以存儲一些集合性的數據,比如在微博應用中,可以將一個用戶所有的關注人存在一個集合中,將其所有粉絲存在一個集合。Redis還為集合提供了求交集、并集、差集等操作,可以非常方便的實現如共同關注、共同喜好、二度好友等功能,對上面的所有集合操作,你還可以使用不同的命令選擇將結果返回給客戶端還是存集到一個新的集合中。 上面說的是新浪微博的應用。
sadd,創建一個集合,并添加數據。
- [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集合是不能寫重復的內容的
- 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>
兩個集合的交集之處
- 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>
可以把集合當成redis list隊列用,需要注意的是set集合的成員模式是不能有重復的值的。如果你的值不重復,你又蛋疼,還真的可以把set集合當成隊列使用。
- 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>
前兩天和朋友說,我那監控平臺的內存吃的厲害,他一下子蹦出一句,redis吃內存肯定很大了。。。 nima,哥只是用他的大隊列。這里說下,redis做隊列的強度。一把來說100w條的隊列數據,占用73M 內存左 右。200w條數據內存在154M內存左右。
redis的堵塞取任務,最好少用,超過5個線程去brpop的話,會把redis的cpu使用率頂到80%左右,而且嚴重會影響別的進程的訪問,如果確定任務不是每時每刻都有的情況下,最好在你的程序控制下他的訪問頻次和時間的間隔。
python處理redis的時候,最好要用pool,速度和資源明顯的節省。
- >>> pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
- >>> r = redis.Redis(connection_pool=pool)
新版的redis是支持管道的,pipline ! 有朋友不太理解,這里的管道有什么好處。 pyhton 雖然連接redis的時候用了連接池,但是這也只是連接方面做了keepalive而已,但是每次的命令推送,他還是一次命令一個交互的。 用了pipline管道堵塞后,他會把所有的命令合成一個管道符推送到redis服務端。這樣的話就省事了很多。 這個特別適用于并發大的時候。
對于redis的pub sub通信性能的問題,可以用gevent來搞定。直接導入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()
當然對于普通的set get sadd hset 也是可以配合redis來使用的。但是,沒啥優勢,因為redis只啟用了一個進程針對數據的讀寫,咱們從程序中復用的那幾個連接,最后取數據,還是需要調用那進程,你還不如讓他老老實實的干活,別搞個多線程,讓他白白折騰。 我這邊做了壓力測試,python2.7用個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