Redis筆記:使用python操作redis

Redis筆記:使用python操作redis

1、Redis安裝

Redis是一個key-value存儲系統。和Memcached類似,它支持存儲的value類型相對更多,包括string(字符串)、list(鏈表)、set(集合)、zset(sorted set –有序集合)和hash(哈希類型)。這些數據類型都支持push/pop、add/remove及取交集並集和差集及更豐富的操作,而且這些操作都是原子性的。在此基礎上,Redis支持各種不同方式的排序。與Memcached一樣,爲了保證效率,數據都是緩存在內存中。區別的是redis會週期性的把更新的數據寫入磁盤或者把修改操作寫入追加的記錄文件,並且在此基礎上實現了master-slave(主從)同步。

#Centos下安裝
sudo yum install redis
#Ubuntu下安裝
sudo apt install redis-server

#以下爲測試Redis指令
#開啓服務,添加“&“使服務後臺運行,建議使用systemctl開啓服務
redis-server
#使用測試客戶端連接
redis-cli
#打開後使用ping測試是否連接,連接成功返回pong
ping

2、redis-py安裝

#使用pip安裝redis-py
sudo pip install redis
#允許遠程註釋掉config中的bind參數,並修改requirepass參數設置密碼  

3、redis-py API使用

1、連接方式

redis-py提供兩個類Redis和StrictRedis用於實現Redis的命令,StrictRedis用於實現大部分官方的命令,並使用官方的語法和命令,Redis是StrictRedis的子類,用於向後兼容舊版本的redis-py 。

redis連接實例是線程安全的,可以直接將redis連接實例設置爲一個全局變量,直接使用。如果需要另一個Redis實例(or Redis數據庫)時,就需要重新創建redis連接實例來獲取一個新的連接。同理,python的redis沒有實現select命令。

1、直接連接

連接redis,加上decode_responses=True,寫入的鍵值對中的value爲str類型,不加這個參數寫入的則爲字節類型。

#!/usr/bin/python
# -*- coding:utf-8 -*-
import redis

# host是redis主機,需要redis服務端和客戶端都啓動 redis默認端口是6379
r = redis.Redis(host='localhost', port=6379, decode_responses=True)   
r.set('name', 'junxi')  # key是"foo" value是"bar" 將鍵值對存入redis緩存
print(r['name'])
print(r.get('name'))  # 取出鍵name對應的值
print(type(r.get('name')))

2、線程池連接

redis-py使用connection pool來管理對一個redis server的所有連接,避免每次建立、釋放連接的開銷。默認,每個Redis實例都會維護一個自己的連接池。可以直接建立一個連接池,然後作爲參數Redis,這樣就可以實現多個Redis實例共享一個連接池。

#!/usr/bin/python
# -*- coding:utf-8 -*-
import redis

# host是redis主機,需要redis服務端和客戶端都起着 redis默認端口是6379
pool = redis.ConnectionPool(host='localhost', port=6379, decode_responses=True)   
r = redis.Redis(connection_pool=pool)
r.set('gender', 'male')     # key是"gender" value是"male" 將鍵值對存入redis緩存
print(r.get('gender'))      # gender 取出鍵male對應的值

2、操作

1、String操作

redis中的String在內存中按照一個name對應一個value來存儲

name -> value
n1 -> v1
n2 -> v2
n3 -> v3
'''
在Redis中設置值,默認,不存在則創建,存在則修改

參數:
	ex,過期時間(秒)
	px,過期時間(毫秒)
	nx,如果設置爲True,則只有name不存在時,當前set操作才執行
    xx,如果設置爲True,則只有name存在時,崗前set操作才執行
'''
set(name, value, ex=None, px=None, nx=False, xx=False)
'''
設置值

只有name不存在時,執行設置操作(添加)
'''
setnx(name, value)
'''
設置值

參數:
	time,過期時間(數字秒 或 timedelta對象)
'''
setex(name, value, time)
'''
設置值

參數:
    time_ms,過期時間(數字毫秒 或 timedelta對象)
'''
psetex(name, time_ms, value)
'''
批量獲取

如:
    mget('ylr', 'wupeiqi')
    或
    r.mget(['ylr', 'wupeiqi'])
'''
mget(keys, *args)
'''
設置新值並獲取原來的值
'''
getset(name, value)
'''
獲取子序列(根據字節獲取,非字符)

參數:
    name,Redis 的 name
    start,起始位置(字節)
    end,結束位置(字節)
如: "測試" ,0-3表示 "測"
'''
getrange(key, start, end)
'''
修改字符串內容,從指定字符串索引開始向後替換(新值太長時,則向後添加)

參數:
    offset,字符串的索引,字節(一個漢字三個字節)
    value,要設置的值
'''
setrange(name, offset, value)
'''
對name對應值的二進制表示的位進行操作

參數:
    name,redis的name
    offset,位的索引(將值變換成二進制後再進行索引)
    value,值只能是 1 或 0
 
注:
    如果在Redis中有一個對應: n1 = "foo",
    那麼字符串foo的二進制表示爲:01100110 01101111 01101111
    所以,如果執行 setbit('n1', 7, 1),則就會將第7位設置爲1,
    那麼最終二進制則變成 01100111 01101111 01101111,即:"goo"
 
擴展,轉換二進制表示:
source = "測試"
source = "foo"
for i in source:
	num = ord(i)
	print bin(num).replace('b','')
特別的,如果source是漢字 "測試"怎麼辦?
答:對於utf-8,每一個漢字佔 3 個字節,那麼 "測試" 則有 6個字節
對於漢字,for循環時候會按照 字節迭代,那麼在迭代時,將每一個字節轉換 十進制數,然後再將十進制數轉換成二進制
'''
setbit(name, offset, value)
'''
獲取name對應的值的二進制表示中的某位的值 (0或1)
'''
getbit(name, offset)
'''
獲取name對應的值的二進制表示中 1 的個數

參數:
    key,Redis的name
    start,位起始位置
    end,位結束位置
'''
bitcount(key, start=None, end=None)
'''
獲取多個值,並將值做位運算,將最後的結果保存至新的name對應的值

參數:
    operation,AND(並) 、 OR(或) 、 NOT(非) 、 XOR(異或)
    dest, 新的Redis的name
    *keys,要查找的Redis的name
如:
   bitop("AND", 'new_name', 'n1', 'n2', 'n3')
   獲取Redis中n1,n2,n3對應的值,然後講所有的值做位運算(求並集),然後將結果保存 new_name 對應的值中
'''
bitop(operation, dest, *keys)
'''
返回name對應值的字節長度(一個漢字3個字節)
'''
strlen(name)
'''
自增 name對應的值,當name不存在時,則創建name=amount,否則,則自增。
 
參數:
    name,Redis的name
    amount,自增數(必須是整數)
 
注:同incrby
'''
incr(self, name, amount=1)
'''
自增 name對應的值,當name不存在時,則創建name=amount,否則,則自增。

參數:
    name,Redis的name
    amount,自增數(浮點型)
'''
incrbyfloat(self, name, amount=1.0)
'''
自減 name對應的值,當name不存在時,則創建name=amount,否則,則自減。

參數:
    name,Redis的name
    amount,自減數(整數)
'''
decr(self, name, amount=1)
'''
在redis name對應的值後面追加內容

參數:
    key, redis的name
    value, 要追加的字符串
'''
append(key, value)

2、Hash操作

name -> hash
n1 -> k1->v1
k2->v2
k3->v3
n2 -> k9->v9
k20->v20
k31->v31
'''
name對應的hash中設置一個鍵值對(不存在,則創建;否則,修改)

參數:
    name,redis的name
    key,name對應的hash中的key
    value,name對應的hash中的value
 
注:
    hsetnx(name, key, value),當name對應的hash中不存在當前key時則創建(相當於添加)
'''
hset(name, key, value)
'''
在name對應的hash中批量設置鍵值對

參數:
    name,redis的name
    mapping,字典,如:{'k1':'v1', 'k2': 'v2'}
 
如:
    r.hmset('xx', {'k1':'v1', 'k2': 'v2'})
'''
hmset(name, mapping)
'''
在name對應的hash中獲取根據key獲取value
'''
hget(name,key)
'''
在name對應的hash中獲取多個key的值

參數:
    name,reids對應的name
    keys,要獲取key集合,如:['k1', 'k2', 'k3']
    *args,要獲取的key,如:k1,k2,k3

如:
    r.mget('xx', ['k1', 'k2'])
    或
    print r.hmget('xx', 'k1', 'k2')
'''
hmget(name, keys, *args)
'''
獲取name對應hash的所有鍵值
'''
hgetall(name)
'''
獲取name對應的hash中鍵值對的個數
'''
hlen(name)
'''
獲取name對應的hash中所有的key的值
'''
hkeys(name)
'''
獲取name對應的hash中所有的value的值
'''
hvals(name)
'''
檢查name對應的hash是否存在當前傳入的key
'''
hexists(name, key)
'''
將name對應的hash中指定key的鍵值對刪除
'''
hdel(name,*keys)
'''
自增name對應的hash中的指定key的值,不存在則創建key=amount

參數:
    name,redis中的name
    key, hash對應的key
    amount,自增數(整數)
'''
hincrby(name, key, amount=1)
'''
自增name對應的hash中的指定key的值,不存在則創建key=amount
 
參數:
    name,redis中的name
    key, hash對應的key
    amount,自增數(浮點數)
'''
hincrbyfloat(name, key, amount=1.0)
'''
增量式迭代獲取,對於數據大的數據非常有用,hscan可以實現分片的獲取數據,並非一次性將數據全部獲取完,從而放置內存被撐爆
 
參數:
    name,redis的name
    cursor,遊標(基於遊標分批取獲取數據)
    match,匹配指定key,默認None 表示所有的key
    count,每次分片最少獲取個數,默認None表示採用Redis的默認分片個數
 
如:
    第一次:cursor1, data1 = r.hscan('xx', cursor=0, match=None, count=None)
    第二次:cursor2, data1 = r.hscan('xx', cursor=cursor1, match=None, count=None)
    ...
    直到返回值cursor的值爲0時,表示數據已經通過分片獲取完畢
'''
hscan(name, cursor=0, match=None, count=None)
'''
利用yield封裝hscan創建生成器,實現分批去redis中獲取數據
 
參數:
    match,匹配指定key,默認None 表示所有的key
    count,每次分片最少獲取個數,默認None表示採用Redis的默認分片個數
 
如:
    for item in r.hscan_iter('xx'):
        print item
'''
hscan_iter(name, match=None, count=None)

3、List操作

name -> list
n1 -> v1,v2,v3,…
n2 -> v15,v16,v20,…
'''
在name對應的list中添加元素,每個新的元素都添加到列表的最左邊
 
如:
    r.lpush('oo', 11,22,33)
    保存順序爲: 33,22,11
 
擴展:
    rpush(name, values) 表示從右向左操作
'''
lpush(name,values)
'''
在name對應的list中添加元素,只有name已經存在時,值添加到列表的最左邊
 
更多:
    rpushx(name, value) 表示從右向左操作
'''
lpushx(name,value)
'''
name對應的list元素的個數
'''
llen(name)
'''
在name對應的列表的某一個值前或後插入一個新值
 
參數:
    name,redis的name
    where,BEFORE或AFTER
    refvalue,標杆值,即:在它前後插入數據
    value,要插入的數據
'''
linsert(name, where, refvalue, value)
'''
對name對應的list中的某一個索引位置重新賦值
 
參數:
    name,redis的name
    index,list的索引位置
    value,要設置的值
'''
r.lset(name, index, value)
'''
在name對應的list中刪除指定的值
 
參數:
    name,redis的name
    value,要刪除的值
    num,  num=0,刪除列表中所有的指定值;
           num=2,從前到後,刪除2個;
           num=-2,從後向前,刪除2個
'''
r.lrem(name, value, num)
'''
在name對應的列表的左側獲取第一個元素並在列表中移除,返回值則是第一個元素
 
更多:
    rpop(name) 表示從右向左操作
'''
lpop(name)
'''
在name對應的列表中根據索引獲取列表元素
'''
lindex(name, index)
'''
在name對應的列表分片獲取數據
參數:
    name,redis的name
    start,索引的起始位置
    end,索引結束位置
'''
lrange(name, start, end)
'''
在name對應的列表中移除沒有在start-end索引之間的值
參數:
    name,redis的name
    start,索引的起始位置
    end,索引結束位置
'''
ltrim(name, start, end)
'''
從一個列表取出最右邊的元素,同時將其添加至另一個列表的最左邊
參數:
    src,要取數據的列表的name
    dst,要添加數據的列表的name
'''
rpoplpush(src, dst)
'''
將多個列表排列,按照從左到右去pop對應列表的元素
 
參數:
    keys,redis的name的集合
    timeout,超時時間,當元素所有列表的元素獲取完之後,阻塞等待列表內有數據的時間(秒), 0 表示永遠阻塞
 
更多:
    r.brpop(keys, timeout),從右向左獲取數據
'''
blpop(keys, timeout)
'''
從一個列表的右側移除一個元素並將其添加到另一個列表的左側
 
參數:
    src,取出並要移除元素的列表對應的name
    dst,要插入元素的列表對應的name
    timeout,當src對應的列表中沒有數據時,阻塞等待其有數據的超時時間(秒),0 表示永遠阻塞
'''
brpoplpush(src, dst, timeout=0)
'''
自定義增量迭代(非常重要,原生的模塊沒有提供支持)

由於redis類庫中沒有提供對列表元素的增量迭代,如果想要循環name對應的列表的所有元素,那麼就需要:
    1、獲取name對應的所有列表
    2、循環列表
但是,如果列表非常大,那麼就有可能在第一步時就將程序的內容撐爆,所有有必要自定義一個增量迭代的功能:
''' 
def list_iter(name):
    ''' 
    自定義redis列表增量迭代
    :param name: redis中的name,即:迭代name對應的列表
    :return: yield 返回 列表元素
    ''' 
    list_count = r.llen(name)
    for index in xrange(list_count):
        yield r.lindex(name, index)
 
# 使用
for item in list_iter('pp'):
    print item

4、Set操作

Set操作,Set集合就是不允許重複的列表

'''
name對應的集合中添加元素
'''
sadd(name,values)
'''
獲取name對應的集合中元素個數
'''
scard(name)
'''
在第一個name對應的集合中且不在其他name對應的集合的元素集合
'''
sdiff(keys, *args)
'''
獲取第一個name對應的集合中且不在其他name對應的集合,再將其新加入到dest對應的集合中
'''
sdiffstore(dest, keys, *args)
'''
獲取多一個name對應集合的並集
'''
sinter(keys, *args)
'''
獲取多一個name對應集合的並集,再將其加入到dest對應的集合中
'''
sinterstore(dest, keys, *args)
'''
檢查value是否是name對應的集合的成員
'''
sismember(name, value)
'''
獲取name對應的集合的所有成員
'''
smembers(name)
'''
將某個成員從一個集合中移動到另外一個集合
'''
smove(src, dst, value)
'''
從集合的右側(尾部)移除一個成員,並將其返回
'''
spop(name)
'''
從name對應的集合中隨機獲取 numbers 個元素
'''
srandmember(name, numbers)
'''
在name對應的集合中刪除某些值
'''
srem(name, values)
'''
獲取多一個name對應的集合的並集
'''
sunion(keys, *args)
'''
獲取多一個name對應的集合的並集,並將結果保存到dest對應的集合中
'''
sunionstore(dest,keys, *args)
'''
同字符串的操作,用於增量迭代分批獲取元素,避免內存消耗太大
'''
sscan(name, cursor=0, match=None, count=None)
sscan_iter(name, match=None, count=None)

5、Sort Set操作

有序集合,在集合的基礎上,爲每元素排序;元素的排序需要根據另外一個值來進行比較,所以,對於有序集合,每一個元素有兩個值,即:值和分數,分數專門用來做排序 。

'''
在name對應的有序集合中添加元素
如:
     zadd('zz', 'n1', 1, 'n2', 2)
     或
     zadd('zz', n1=11, n2=22)
'''
zadd(name, args, *kwargs)
'''
獲取name對應的有序集合元素的數量
'''
zcard(name)
'''
獲取name對應的有序集合中分數 在 [min,max] 之間的個數
'''
zcount(name, min, max)
'''
自增name對應的有序集合的 name 對應的分數
'''
zincrby(name, value, amount)
'''
按照索引範圍獲取name對應的有序集合的元素
 
參數:
    name,redis的name
    start,有序集合索引起始位置(非分數)
    end,有序集合索引結束位置(非分數)
    desc,排序規則,默認按照分數從小到大排序
    withscores,是否獲取元素的分數,默認只獲取元素的值
    score_cast_func,對分數進行數據轉換的函數
 
更多:
    從大到小排序
    zrevrange(name, start, end, withscores=False, score_cast_func=float)
 
    按照分數範圍獲取name對應的有序集合的元素
    zrangebyscore(name, min, max, start=None, num=None, withscores=False, score_cast_func=float)
    從大到小排序
    zrevrangebyscore(name, max, min, start=None, num=None, withscores=False, score_cast_func=float)
'''
r.zrange( name, start, end, desc=False, withscores=False, score_cast_func=float)
'''
獲取某個值在 name對應的有序集合中的排行(從 0 開始)
 
更多:
    zrevrank(name, value),從大到小排序
'''
zrank(name, value)
'''
當有序集合的所有成員都具有相同的分值時,有序集合的元素會根據成員的 值 (lexicographical ordering)來進行排序,而這個命令則可以返回給定的有序集合鍵 key 中, 元素的值介於 min 和 max 之間的成員
對集合中的每個成員進行逐個字節的對比(byte-by-byte compare), 並按照從低到高的順序, 返回排序後的集合成員。 如果兩個字符串有一部分內容是相同的話, 那麼命令會認爲較長的字符串比較短的字符串要大
 
參數:
    name,redis的name
    min,左區間(值)。 + 表示正無限; - 表示負無限; ( 表示開區間; [ 則表示閉區間
    min,右區間(值)
    start,對結果進行分片處理,索引位置
    num,對結果進行分片處理,索引後面的num個元素
 
如:
    ZADD myzset 0 aa 0 ba 0 ca 0 da 0 ea 0 fa 0 ga
    r.zrangebylex('myzset', "-", "[ca") 結果爲:['aa', 'ba', 'ca']
 
更多:
    從大到小排序
    zrevrangebylex(name, max, min, start=None, num=None)
'''
zrangebylex(name, min, max, start=None, num=None)
'''
刪除name對應的有序集合中值是values的成員
 
如:zrem('zz', ['s1', 's2'])
'''
zrem(name, values)
'''
根據排行範圍刪除
'''
zremrangebyrank(name, min, max)
'''
根據分數範圍刪除
'''
zremrangebyscore(name, min, max)
'''
根據值返回刪除
'''
zremrangebylex(name, min, max)
'''
獲取name對應有序集合中 value 對應的分數
'''
zscore(name, value)
'''
獲取兩個有序集合的交集,如果遇到相同值不同分數,則按照aggregate進行操作
aggregate的值爲:  SUM  MIN  MAX
'''
zinterstore(dest, keys, aggregate=None)
'''
獲取兩個有序集合的並集,如果遇到相同值不同分數,則按照aggregate進行操作
aggregate的值爲:  SUM  MIN  MAX
'''
zunionstore(dest, keys, aggregate=None)
'''
同字符串相似,相較於字符串新增score_cast_func,用來對分數進行操作
'''
zscan(name, cursor=0, match=None, count=None, score_cast_func=float)
zscan_iter(name, match=None, count=None,score_cast_func=float)

3、其他常用操作

'''
根據刪除redis中的任意數據類型
'''
delete(*names)
'''
檢測redis的name是否存在
'''
exists(name)
'''
根據模型獲取redis的name

更多:
    KEYS * 匹配數據庫中所有 key 。
    KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
    KEYS h*llo 匹配 hllo 和 heeeeello 等。
    KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo
'''
keys(pattern=*)
'''
爲某個redis的某個name設置超時時間
'''
expire(name ,time)
'''
對redis的name重命名爲
'''
rename(src, dst)
'''
將redis的某個值移動到指定的db下
'''
move(name, db)
'''
隨機獲取一個redis的name(不刪除)
'''
randomkey()
'''
獲取name對應值的類型
'''
type(name)
'''
同字符串操作,用於增量迭代獲取key
'''
scan(cursor=0, match=None, count=None)
scan_iter(match=None, count=None)

4、管道

redis-py默認在執行每次請求都會創建(連接池申請連接)和斷開(歸還連接池)一次連接操作,如果想要在一次請求中指定多個命令,則可以使用pipline實現一次請求指定多個命令,並且默認情況下一次pipline 是原子性操作。

#!/usr/bin/python
# -*- coding:utf-8 -*-
import redis
 
pool = redis.ConnectionPool(host='localhost', port=6379)
 
r = redis.Redis(connection_pool=pool)
 
# pipe = r.pipeline(transaction=False)
pipe = r.pipeline(transaction=True)
 
r.set('name', 'alex')
r.set('role', 'sb')
 
pipe.execute()

5、發佈訂閱

發佈者:服務器

訂閱者:Dashboad和數據處理

Demo如下:

#!/usr/bin/python
# -*- coding:utf-8 -*-
import redis


class RedisHelper:

    def __init__(self):
        self.__conn = redis.Redis(host='localhost')
        self.chan_sub = 'fm104.5'
        self.chan_pub = 'fm104.5'

    def public(self, msg):
        self.__conn.publish(self.chan_pub, msg)
        return True

    def subscribe(self):
        pub = self.__conn.pubsub()
        pub.subscribe(self.chan_sub)
        pub.parse_response()
        return pub

訂閱者:

#!/usr/bin/python
# -*- coding:utf-8 -*-
from monitor.RedisHelper import RedisHelper
 
obj = RedisHelper()
redis_sub = obj.subscribe()
 
while True:
    msg= redis_sub.parse_response()
    print msg

發佈者:

#!/usr/bin/python
# -*- coding:utf-8 -*-
from monitor.RedisHelper import RedisHelper
 
obj = RedisHelper()
obj.public('hello')

4、配置自啓動

可以自己編譯安裝。

複製配置文件至/usr/local/etc/ 目錄,編寫service文件:

sudo nano /usr/lib/systemd/system/redis.service

內容如下:

[Unit]
Description=redis
After=network.target

[Service]
TimeoutStartSec=10
Restart=always
RestartSec=10
ExecStart=/usr/local/bin/redis-server /usr/local/etc/redis.conf
ExecStop=/bin/kill $MAINPID

[Install]
WantedBy=multi-user.target
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章