python對緩存(memcached,redis)的操作


1.Memcached

Memcached 是一個高性能的分布式內存對象緩存系統,用於動態Web應用以減輕數據庫負載。它通過在內存中緩存數據和對象來減少讀取數據庫的次數,從而提高動態、數據庫驅動網站的速度。Memcached基於一個存儲鍵/值對的hashmap。其守護進程(daemon )是用C寫的,但是客戶端可以用任何語言來編寫,並通過memcached協議與守護進程通信

memcached服務端安裝部署

安裝libevent
memcached依賴於libevent API,因此要事先安裝之,項目主頁:http://libevent.org/,讀者可自行選擇需要的版本下載。本文采用的是目前最新版本的源碼包libevent-2.0.16-stable.tar.gz。安裝過程:

# tar xf libevent-2.0.20-stable.tar.gz
# cd libevent-2.0.20
# ./configure --prefix=/usr/local/libevent
# make && make install

# echo "/usr/local/libevent/lib" > /etc/ld.so.conf.d/libevent.conf
# ldconfig 

安裝配置memcached

1、安裝memcached
# tar xf memcached-1.4.15.tar.gz 
# cd memcached-1.4.15
# ./configure --prefix=/usr/local/memcached --with-libevent=/usr/local/libevent
# make && make install

 

memcached的常用選項說明
-l <ip_addr>:指定進程監聽的地址;
-d: 以服務模式運行;
-u <username>:以指定的用戶身份運行memcached進程;
-m <num>:用於緩存數據的最大內存空間,單位為MB,默認為64MB;
-c <num>:最大支持的並發連接數,默認為1024;
-p <num>: 指定監聽的TCP端口,默認為11211;
-U <num>:指定監聽的UDP端口,默認為11211,0表示關閉UDP端口;
-t <threads>:用於處理入站請求的最大線程數,僅在memcached編譯時開啟了支持線程才有效;
-f <num>:設定Slab Allocator定義預先分配內存空間大小固定的塊時使用的增長因子;
-M:當內存空間不夠使用時返回錯誤信息,而不是按LRU算法利用空間;
-n: 指定最小的slab chunk大小;單位是字節;
-S: 啟用sasl進行用戶認證;

 

python操作memcached

安裝python的memcached的模塊
pip install python-memcached

  • 常用方法
    • Client() 連接memcache服務器
    • set設置key/value key存在則更新,不存在則創建
    • set_multi 設置多個鍵值對,如果key不存在,則創建,如果key存在,則修改
    • get 根據單個key獲取value
    • get_multi 根據key列表獲取value,最終返回一個key/value的字典
    • add 添加一對key/value,如果已經存在的 key,則添加失敗
    • replace 修改某個key的value ,key不存在則失敗
    • delete 根據key刪除對應的key/value
    • delete_multi 根據key 列表刪除對應的key/value
    • append 根據key在對應的value后面追加值
    • prepend 根據key在對應的value前面追加值
    • incr 自增,將Memcached中的某一個值增加 N ( N默認為1 )
    • decr 自減,將Memcached中的某一個值減少 N ( N默認為1 )
  • 舉例
import memcache

host_port = '127.0.0.1:11211'    #memcache的ip和端口

mc = memcache.Client([host_port], debug=True)        #debug表示開啟調試,

mc.set('name','fuzj')               #設置key
mc.set('name','jeck')               #設置key,key存在則更新
mc.set('name1','test')               #設置key,key存在則更新
mc.set('name2','test')               #設置key,key存在則更新
mc.set_multi({'k1':'v1','k2':'v2'})     #批量設置key

print(mc.get('name'))   #獲取key
print(mc.get('123'))    #獲取不存在的key,返回None
print(mc.get_multi('k1','k2'))

mc.add('age','22')
mc.add('age','123')  #key存在會抱錯

mc.replace('age','26')    #修改key的value
mc.replace('abc','26')    #修改不存在的key,會抱錯

mc.delete('name1')     #刪除age鍵值對
mc.delete_multi('k1','k2')  #刪除k1 k2 鍵值對

mc.append('name','你好')    #在name的value 的后面加 '你好'
mc.prepend('name','hi')   #在name的value 的前面加'hi'

mc.incr('age')          #age對應的value增加1
mc.decr('age',10)           #age對應的value 減少10
mc.decr('name')     #name的value不能被加減,會抱錯

 

  • 支持集群

python-memcached模塊原生支持集群操作,其原理是在內存維護一個主機列表,且集群中主機的權重值和主機在列表中重復出現的次數成正比

     主機    權重
    1.1.1.1   1
    1.1.1.2   2
    1.1.1.3   1
 
那么在內存中主機列表為:
    host_list = ["1.1.1.1", "1.1.1.2", "1.1.1.2", "1.1.1.3", ]
 

如果用戶根據如果要在內存中創建一個鍵值對(如:k1 = v1),那么要執行一下步驟:

根據算法將 k1 轉換成一個數字
將數字和主機列表長度求余數,得到一個值 N( 0 <= N < 列表長度 )
在主機列表中根據 第2步得到的值為索引獲取主機,例如:host_list[N]
連接 將第3步中獲取的主機,將 k1 = v1 放置在該服務器的內存中
代碼實現如下:

mc = memcache.Client([('1.1.1.1:12000', 1), ('1.1.1.2:12000', 2), ('1.1.1.3:12000', 1)], debug=True)
 
mc.set('k1', 'v1')

 

  • 解決樂觀鎖 gets/cas

如果兩個客戶端同時操作一個key,就會導致數據沖突
如商城商品剩余個數,假設改值保存在memcache中,product_count = 900
A用戶刷新頁面從memcache中讀取到product_count = 900
B用戶刷新頁面從memcache中讀取到product_count = 900

如果A、B用戶均購買商品

A用戶修改商品剩余個數 product_count=899
B用戶修改商品剩余個數 product_count=899

如此一來緩存內的數據便不在正確,兩個用戶購買商品后,商品剩余還是 899
如果使用python的set和get來操作以上過程,那么程序就會如上述所示情況!

如果采用CAS協議,則是如下的情景。
第一步,A取出product_count=900,並獲取到CAS-ID1;
第二步,B取出product_count=900,並獲取到CAS-ID2;
第三步,A購買時,修改product_count=899,在寫入緩存前,檢查CAS-ID與緩存空間中該數據的CAS-ID是否一致。結果是“一致”,就將修改后的帶有CAS-ID1的product_count寫入到緩存。
第四步,B購買時,修改product_count=899,在寫入緩存前,檢查CAS-ID與緩存空間中該數據的CAS-ID是否一致。結果是“不一致”,則拒絕寫入,返回存儲失敗。

2.Redis

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

redis服務端安裝部署

1. 下載Redis
目前,最新的Redist版本為3.0,使用wget下載,命令如下:

# wget http://download.redis.io/releases/redis-3.0.4.tar.gz

2. 解壓Redis
下載完成后,使用tar命令解壓下載文件:

# tar -xzvf redis-3.0.4.tar.gz

3. 編譯安裝Redis
切換至程序目錄,並執行make命令編譯:

# cd redis-3.0.4
# make
執行安裝命令

# make install
make install安裝完成后,會在/usr/local/bin目錄下生成下面幾個可執行文件,它們的作用分別是:

redis-server:Redis服務器端啟動程序
redis-cli:Redis客戶端操作工具。也可以用telnet根據其純文本協議來操作
redis-benchmark:Redis性能測試工具
redis-check-aof:數據修復工具
redis-check-dump:檢查導出工具

備注

有的機器會出現類似以下錯誤:

make[1]: Entering directory `/root/redis/src'
You need tcl 8.5 or newer in order to run the Redis test
……
這是因為沒有安裝tcl導致,yum安裝即可:

yum install tcl

4. 配置Redis
復制配置文件到/etc/目錄:

# cp redis.conf /etc/
為了讓Redis后台運行,一般還需要修改redis.conf文件:

vi /etc/redis.conf
修改daemonize配置項為yes,使Redis進程在后台運行:

daemonize yes

5. 啟動Redis
配置完成后,啟動Redis:

# cd /usr/local/bin
# ./redis-server /etc/redis.conf
檢查啟動情況:

# ps -ef | grep redis
看到類似下面的一行,表示啟動成功:

root     18443     1  0 13:05 ?        00:00:00 ./redis-server *:6379 

6. 添加開機啟動項
讓Redis開機運行可以將其添加到rc.local文件,也可將添加為系統服務service。本文使用rc.local的方式,添加service請參考:Redis 配置為 Service 系統服務 。

為了能讓Redis在服務器重啟后自動啟動,需要將啟動命令寫入開機啟動項:

echo "/usr/local/bin/redis-server /etc/redis.conf" >>/etc/rc.local

7. Redis配置參數
在前面的操作中,我們用到了使Redis進程在后台運行的參數,下面介紹其它一些常用的Redis啟動參數:

daemonize:是否以后台daemon方式運行
pidfile:pid文件位置
port:監聽的端口號
timeout:請求超時時間
loglevel:log信息級別
logfile:log文件位置
databases:開啟數據庫的數量
save * *:保存快照的頻率,第一個*表示多長時間,第三個*表示執行多少次寫操作。在一定時間內執行一定數量的寫操作時,自動保存快照。可設置多個條件。
rdbcompression:是否使用壓縮
dbfilename:數據快照文件名(只是文件名)
dir:數據快照的保存目錄(僅目錄)
appendonly:是否開啟appendonlylog,開啟的話每次寫操作會記一條log,這會提高數據抗風險能力,但影響效率。
appendfsync:appendonlylog如何同步到磁盤。三個選項,分別是每次寫都強制調用fsync、每秒啟用一次fsync、不調用fsync等待系統自己同步

 

python操作redis

安裝redis模塊
pip install redis

連接方式

  • 單連接

    使用redis.Redis()方法,直接和redis建立連接,操作完成之后,連接釋放

import redis

r = redis.Redis(host='127.0.0.1', port=6379)
r.set('foo', 'Bar')
print(r.get('foo'))

 

  • 連接池 redis-py使用connection pool來管理對一個redis server的所有連接,避免每次建立、釋放連接的開銷。默認,每個Redis實例都會維護一個自己的連接池。可以直接建立一個連接池,然后作為參數Redis,這樣就可以實現多個Redis實例共享一個連接池。
import redis
pool = redis.ConnectionPool(host='127.0.0.1', port=6379,)
r = redis.Redis(connection_pool=pool)
r.set('foo1', 'Bar')
print(r.get('foo1'))

 

操作

  • 字符串存儲操作

    • 存儲形式:

    • set(name, value, ex=None, px=None, nx=False, xx=False)

      參數:
      name:key的名稱
      value:key的值
      ex,過期時間(秒)
      px,過期時間(毫秒)
      nx,如果設置為True,則只有name不存在時,當前set操作才執行
      xx,如果設置為True,則只有name存在時,當前set操作才執行

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

    • setex(name, value, time) 設置值,同時支持設置過期時間,單位:秒s

    • psetex(name, time_ms, value) 設置值,同時支持設置過期時間,單位:毫秒ms

    • mset(*args, **kwargs) 批量設置值 ,支持 字典形式{'k1':'v1','k2':'v2'}和key=value形式k1=v1,k2=v2

    • get(name) 獲取key的value

    • mget(keys, *args) 批量獲取值 支持k1,k2,k3形式和[k1,k2]形式

    • getset(name, value) 獲取原來的值,並設置新的值

    • getrange(key, start, end) 獲取指定key的value的子序列 start和end為value的起始位置

    • setrange(name, offset, value) 修改字符串內容,從指定字符串索引開始向后替換(新值太長時,則向后添加),offset,字符串的索引,字節(一個漢字三個字節),value,要設置的值

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

    • getbit(name, offset) 獲取name對應的值的二進制表示中的某位的值 (0或1)

    • bitcount(key, start=None, end=None) 獲取name對應的值的二進制表示中 1 的個數

    • bitop(operation, dest, *keys) 獲取多個值,並將值做位運算,將最后的結果保存至新的name對應的值

      參數:
      operation,AND(並) 、 OR(或) 、 NOT(非) 、 XOR(異或)
      dest, 新的Redis的name
      *keys,要查找的Redis的name

    • strlen(name) 返回name對應值的字節長度(一個漢字3個字節)

    • incr(name, amount=1) 自增 name對應的值,當name不存在時,則創建name=amount,否則,則自增 ,amount必須為整數

    • incrbyfloat(name, amount=1.0) 自增 name對應的值,當name不存在時,則創建name=amount,否則,則自增,amount為浮點數

    • decr(name, amount=1) 自減 name對應的值,當name不存在時,則創建name=amount,否則,則自減

    • decrbyfloat(name, amount=1.0) 自減 name對應的值,當name不存在時,則創建name=amount,否則,則自減,amount為浮點數

    • append(key, value) 在redis name對應的值后面追加內容

    • 用法示例:

import redis
pool = redis.ConnectionPool(host='127.0.0.1', port=6379,)
r = redis.Redis(connection_pool=pool)


r.set('k1','v1',ex=2)
r.setnx('k2','v2')  #設置k1 v1

r.mset({'k3':'v3','k4':'v4'})           #批量設置kv
print(r.get('k1'))       #獲取k1的value
print(r.mget(['k2','k3']))         #批量獲取k2,k3
print(r.getset('k2','v22'))        #獲取k2的值,並將k2的值設置為v22
print(r.get('k2'))
print(r.getrange('k2',1,2))     #獲取k2的value的子序列1-2,默認從0開始
r.setrange('k4',2,'12313')
print(r.get('k4'))
print(r.strlen('k4'))         #獲取k4的value長度
r.set('k5','123')
r.incr('k5',10)      #設置k5的值自增10
r.append('k5','')   #在k5的值后面追加元
print(r.get('k5').decode())
View Code

 

  • hash存儲操作

    • 存儲形式

    • hset(name, key, value) name對應的hash中設置一個鍵值對(不存在,則創建;否則,修改
    • hmset(name, mapping) 在name對應的hash中批量設置鍵值對,mapping,字典,如:{'k1':'v1', 'k2': 'v2'}
    • hget(name,key) 在name對應的hash中獲取根據key獲取value
    • hmget(name, keys, *args) 在name對應的hash中獲取多個key的值 keys,要獲取key集合,如:['k1', 'k2', 'k3'], *args,要獲取的key,如:k1,k2,k3
    • hgetall(name) 獲取name對應hash的所有鍵值
    • hlen(name) 獲取name對應的hash中鍵值對的個數
    • hkeys(name) 獲取name對應的hash中所有的key的值
    • hvals(name) 獲取name對應的hash中所有的value的值
    • hexists(name, key) 檢查name對應的hash是否存在當前傳入的key
    • hdel(name,*keys) 將name對應的hash中指定key的鍵值對刪除
    • hincrby(name, key, amount=1) 自增name對應的hash中的指定key的值,不存在則創建key=amount
    • hincrbyfloat(name, key, amount=1.0) 自增name對應的hash中的指定key的值,不存在則創建key=amount
    • hscan(name, cursor=0, match=None, count=None) 增量式迭代獲取,對於數據大的數據非常有用,hscan可以實現分片的獲取數據,並非一次性將數據全部獲取完,從而放置內存被撐爆

      參數:
      name,redis的name
      cursor,游標(基於游標分批取獲取數據)
      match,匹配指定key,默認None 表示所有的key
      count,每次分片最少獲取個數,默認None表示采用Redis的默認分片個數

    • hscan_iter(name, match=None, count=None) 利用yield封裝hscan創建生成器,實現分批去redis中獲取數據

    參數:
    match,匹配指定key,默認None 表示所有的key
    count,每次分片最少獲取個數,默認None表示采用Redis的默認分片個數

    • 用法示例:
pool = redis.ConnectionPool(host='127.0.0.1', port=6379,)
r = redis.Redis(connection_pool=pool)
r.hset('k1','hk1','hv1')            #設置k1 對應的鍵值對 hk1 hv1
print(r.hget('k1','hk1'))           #獲取k1下的對應鍵值對hk1的值
r.hmset('k22',{'hk2':'hv2','hhk2':'hhv2'})
print(r.hmget('k22',['hk2','hhk2']))
print(r.hgetall('k22'))     #返回一個字典
print(r.hlen('k22'))            #獲去k22對應的鍵值對個數
print(r.hkeys('k22'))       #獲取k22對應鍵值對的keys
print(r.hvals('k22'))       #獲取k22對應鍵值對的values
print(r.hexists('k22','jkkk'))  #判斷k22對應鍵值對中是否存在jkkk的key 返回True和False

 

  • list存儲操作

    • 存儲形式

    • lpush(name,values) 在name對應的list中添加元素,每個新的元素都添加到列表的最左邊
    • rpush(name, values) 表示從右向左操作添加元素
    • lpushx(name,value) 在name對應的list中添加元素,只有name已經存在時,值添加到列表的最左邊
    • llen(name) name對應的list元素的個數
    • linsert(name, where, refvalue, value)) 在name對應的列表的某一個值前或后插入一個新值

      參數:
      name,redis的name
      where,BEFORE或AFTER
      refvalue,標桿值,即:在它前后插入數據
      value,要插入的數據

    • lset(name, index, value) 對name對應的list中的某一個索引位置重新賦值
      參數:
      name,redis的name
      index,list的索引位置
      value,要設置的值

    • lrem(name, value, num) 在name對應的list中刪除指定的值
      參數:
      name,redis的name
      value,要刪除的值
      num, num=0,刪除列表中所有的指定值;
      num=2,從前到后,刪除2個;
      num=-2,從后向前,刪除2個

    • lpop(name) 在name對應的列表的左側獲取第一個元素並在列表中移除,返回值則是第一個元素

    • rpop(name) 表示從右向左操作

    • lindex(name, index) 在name對應的列表中根據索引獲取列表元素

    • lrange(name, start, end) 在name對應的列表分片獲取數據

    • ltrim(name, start, end) 在name對應的列表中移除沒有在start-end索引之間的值

    • rpoplpush(src, dst) 從一個列表取出最右邊的元素,同時將其添加至另一個列表的最左邊,src,要取數據的列表的name,dst,要添加數據的列表的name

    • blpop(keys, timeout) 將多個列表排列,按照從左到右去pop對應列表的元素
      參數:
      keys,redis的name的集合
      timeout,超時時間,當元素所有列表的元素獲取完之后,阻塞等待列表內有數據的時間(秒), 0 表示永遠阻塞;r.brpop(keys, timeout),從右向左獲取數據

    • brpoplpush(src, dst, timeout=0) 從一個列表的右側移除一個元素並將其添加到另一個列表的左側
      參數:
      src,取出並要移除元素的列表對應的name
      dst,要插入元素的列表對應的name
      timeout,當src對應的列表中沒有數據時,阻塞等待其有數據的超時時間(秒),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

     

    • 用法舉例:
pool = redis.ConnectionPool(host='127.0.0.1', port=6379,)
r = redis.Redis(connection_pool=pool)

# 在name對應的list中添加元素,每個新的元素都添加到列表的最左邊
r.lpush("k11",2)
r.lpush("k11",3,4,5)#保存在列表中的順序為5,4,3,2
r.rpush('k11',0)   #從右邊添加
r.lpushx('k11','abc')    #從左邊添加元素,切name必須存在
print(r.llen('k11'))   #打印k11對應的列表長度
print(r.lrange('k11',0,r.llen('k11')))   #獲取k11對應的列表所有值
print(r.lindex('k11',0))   #根據索引去值
print(r.lpop('k11'))   #移除第一個元素,並返回
r.ltrim('k11',0,5)          #刪除索引0-5之外的值
print(r.lrange('k11',0,-1))
View Code

 

  • set集合存儲操作

    Set集合就是不允許重復的列表

    • sadd(name,values) name對應的集合中添加元素
    • scard(name) 獲取name對應的集合中元素個數
    • sdiff(keys, *args) 在第一個name對應的集合中且不在其他name對應的集合的元素集合
    • sdiffstore(dest, keys, *args) 獲取第一個name對應的集合中且不在其他name對應的集合,再將其新加入到dest對應的集合中
    • sinter(keys, *args) 獲取多一個name對應集合的並集
    • sinterstore(dest, keys, *args) 獲取多一個name對應集合的並集,再講其加入到dest對應的集合中
    • sismember(name, value) 檢查value是否是name對應的集合的成員
    • smembers(name)獲取name對應的集合的所有成員
    • smove(src, dst, value) 將某個成員從一個集合中移動到另外一個集合
    • spop(name) 從集合的右側(尾部)移除一個成員,並將其返回
    • srandmember(name, numbers) 從name對應的集合中隨機獲取 numbers 個元素
    • srem(name, values) 在name對應的集合中刪除某些值
    • sunion(keys, *args) 獲取多一個name對應的集合的並集
    • sunionstore(dest,keys, *args) 獲取多一個name對應的集合的並集,並將結果保存到dest對應的集合中
    • sscan(name, cursor=0, match=None, count=None)

      sscan_iter(name, match=None, count=None)
      同字符串的操作,用於增量迭代分批獲取元素,避免內存消耗太大

    • 用法舉例:

pool = redis.ConnectionPool(host='127.0.0.1', port=6379,)
r = redis.Redis(connection_pool=pool)

r.sadd("k12","aa")
r.sadd("k12","aa","bb",'123')     #往k12對應的集合添加元素
r.sadd("k13","aa","bb",'cc','00')     #往k12對應的集合添加元素
print(r.smembers('k12'))   #獲取對應的集合的所有成員
print(r.scard('k12'))   #獲取k12對應集合元素的個數
print(r.sdiff('k12','k13'))    #比較k12中不在k13 的元素
r.sdiffstore('k123','k12','k13')   #比較k12中不在k13 的元素 ,並將結果添加到k123中
print(r.smembers('k123'))
print(r.sinter('k12','k13'))   #取k12和k13的並集
print(r.sismember('k12','555'))     #判斷555是否是k12的元素
r.smove('k12','k13','123')  #將k12下的123移動到k13
print(r.smembers('k12'))
print(r.smembers('k13'))
View Code

 

  • 有序集合存儲操作

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

    • zadd(name, *args, **kwargs) 在name對應的有序集合中添加元素 如: zadd('zz', 'n1', 1, 'n2', 2) 或 zadd('zz', n1=11, n2=22)
    • zcard(name) 獲取name對應的有序集合元素的數量
    • zcount(name, min, max) 獲取name對應的有序集合中分數 在 [min,max] 之間的個數
    • zincrby(name, value, amount) 自增name對應的有序集合的 name 對應的分數
    • zrange( name, start, end, desc=False, withscores=False, score_cast_func=float)
    # 按照索引范圍獲取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)

     

    • zrank(name, value) 獲取某個值在 name對應的有序集合中的排行(從 0 開始),更多:zrevrank(name, value),從大到小排序
    • zrangebylex(name, min, max, start=None, num=None)
    # 當有序集合的所有成員都具有相同的分值時,有序集合的元素會根據成員的 值 (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)

     

    • zrem(name, values) 刪除name對應的有序集合中值是values的成員
    • zremrangebyrank(name, min, max) 根據排行范圍刪除
    • zremrangebyscore(name, min, max) 根據分數范圍刪除
    • zremrangebylex(name, min, max) 根據值返回刪除
    • zscore(name, value) 獲取name對應有序集合中 value 對應的分數
    • zinterstore(dest, keys, aggregate=None) 獲取兩個有序集合的交集,如果遇到相同值不同分數,則按照aggregate進行操作 ,aggregate的值為: SUM MIN MAX
    • zunionstore(dest, keys, aggregate=None) 獲取兩個有序集合的並集,如果遇到相同值不同分數,則按照aggregate進行操作 aggregate的值為: SUM MIN MAX
    • zscan(name, cursor=0, match=None, count=None, score_cast_func=float) zscan_iter(name, match=None, count=None,score_cast_func=float) 同字符串相似,相較於字符串新增score_cast_func,用來對分數進行操作
  • 使用舉例:

pool = redis.ConnectionPool(host='127.0.0.1', port=6379,)
r = redis.Redis(connection_pool=pool)
r.zadd("k999", "a1", 6, "a2", 2,"a3",5)     #往k999有序集合中添加值
#
r.zadd('k888', b1=10, b2=5)

print(r.zcard('k999'))   #獲取k999的元素個數
print(r.zcount('k999',4,6))  #獲取k999中分數在4-6之間的元素個數
r.zincrby('k999','a1',10)  #自增k999中a1的分數
print(r.zrevrank('k999','a3'))   #獲取a3的分數在k999所有元素的排行,默認從0開始
View Code
  • 其他操作
    •  delete(*names) 根據刪除redis中的任意數據類型
    • exists(name) 檢測redis的name是否存在
    • keys(pattern='*')  根據模型獲取redis的name 

      更多:

           KEYS * 匹配數據庫中所有 key

           KEYS h?llo 匹配 hello , hallo 和 hxllo 等。

           KEYS h*llo 匹配 hllo 和 heeeeello 等。

           KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo

    • expire(name ,time)   為某個redis的某個name設置超時時間

    • rename(src, dst)  對redis的name重命名為

    • move(name, db))   將redis的某個值移動到指定的db

    • randomkey()   隨機獲取一個redis的name(不刪除)

    • type(name)  獲取name對應值的類型

    • scan(cursor=0, match=None, count=None)

    • scan_iter(match=None, count=None)  同字符串操作,用於增量迭代獲取key

  • 管道 Redis是一種基於客戶端-服務端模型以及請求/響應協議的TCP服務。這意味着通常情況下一個請求會遵循以下步驟: 客戶端向服務端發送一個查詢請求,並監聽Socket返回,通常是以阻塞模式,等待服務端響應。 服務端處理命令,並將結果返回給客戶端。 Redis 管道技術可以在服務端未響應時,客戶端可以繼續向服務端發送請求,並最終一次性讀取所有服務端的響應。
import redis
import time
pool = redis.ConnectionPool(host='203.130.45.173', port=6379,)
r = redis.Redis(connection_pool=pool)

def without_pipeline():
    for i in range(1000):
        r.ping()
    return
def with_pipeline():
    pipeline=r.pipeline()
    for i in range(1000):
        pipeline.ping()
    pipeline.execute()
    return
def bench(desc):
    start=time.clock()
    desc()
    stop=time.clock()
    diff=stop-start
    print("%s has token %s" % (desc.__name__,str(diff)))

bench(without_pipeline)     #不使用管道
bench(with_pipeline)            #使用管道

輸出結果:發現差別很大,不使用管道耗時將近是使用管道的10倍 without_pipeline has token
0.349842 with_pipeline has token 0.03180000000000005
  •  實現消息訂閱模型

     

    所謂消息訂閱模型,類似於廣播電台,電台中心發送廣播內容,凡事在此頻道內的收音機都可以收到消息

     

     

    發布者訂閱者類

     

    ```python
    
    import redis
    
     
    
    class RedisHelper:
    
        def __init__(self):
    
            self.pool = redis.ConnectionPool(host='127.0.0.1', port=6379,)
    
            self.__connect = redis.Redis(connection_pool=self.pool)
    
     
    
        def public(self,msg,channel):
    
        '''發布者方法'''
    
            try:
    
                self.__connect.publish(channel,msg)
    
                return True
    
            except Exception as e:
    
                return False
    
     
    
        def subscript(self,channel):
    
        '''訂閱者方法'''
    
            pub = self.__connect.pubsub()
    
            pub.subscribe(channel)
    
            pub.parse_response()
    
            return pub
    
     
    
    ```

     

     

    訂閱者:

     

    import RedisHelper
    
     
    
    obj = RedisHelper.RedisHelper()
    
    data = obj.subscript('fm110')
    
    print(data.parse_response())

     

     

    發布者:

    import RedisHelper
    
     
    
    obj = RedisHelper.RedisHelper()
    
    obj.public('alex','fm110')

     


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM