Python操作Redis和Memcached

Memcached

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

Memcached安裝:

wget http://memcached.org/latest
tar -zxvf memcached-1.x.x.tar.gz
cd memcached-1.x.x
./configure && make && make test && sudo make install

PS:依賴libevent
      yum install libevent-devel
      apt-get install libevent-dev

啓動Memcached:


memcached -d -m 10    -u root -l IP -p 12000 -c 256 -P /tmp/memcached.pid

 

參數說明:

    -d 是啓動一個守護進程

    -m 是分配給Memcache使用的內存數量,單位是MB

    -u 是運行Memcache的用戶

    -l 是監聽的服務器IP地址

    -p 是設置Memcache監聽的端口,最好是1024以上的端口

    -c 選項是最大運行的併發連接數,默認是1024,按照你服務器的負載量來設定

    -P 是設置保存Memcache的pid文件


Python操作Memcached

python操作Memcached使用Python-memcached模塊

下載安裝:https://pypi.python.org/pypi/python-memcached

1.第一次操作

import memcache

mc = memcache.Client(['ip:port'], debug=True)
mc.set("foo", "bar")
ret = mc.get('foo')
print(ret)

注:debug = True 表示運行出現錯誤時,現實錯誤信息,上線後移除該參數。

2.add

import memcache

mc = memcache.Client(['ip:port'], debug=True)
mc.add('k1', 'v1')
# mc.add('k1', 'v2') # 報錯,對已經存在的key重複添加,失敗!!!

3.replace

import memcache

mc = memcache.Client(['ip:port'], debug=True)
# 如果memcache中存在kkkk,則替換成功,否則異常
mc.replace('kkkk','999')

4.set 和 set_multi

set            設置一個鍵值對,如果key不存在,則創建,如果key存在,則修改
set_multi   設置多個鍵值對,如果key不存在,則創建,如果key存在,則修改

import memcache

mc = memcache.Client(['ip:port'], debug=True)

mc.set('key0', 'value1')

mc.set_multi({'key1': 'val1', 'key2': 'val2'})

5.delete 和 delete_multi

delete             在Memcached中刪除指定的一個鍵值對
delete_multi    在Memcached中刪除指定的多個鍵值對

import memcache

mc = memcache.Client(['ip:port'], debug=True)

mc.delete('key0')
mc.delete_multi(['key1', 'key2'])

6. get 和 get_multi

get            獲取一個鍵值對
get_multi   獲取多一個鍵值對

import memcache

mc = memcache.Client(['ip:port'], debug=True)

val = mc.get('key0')
item_dict = mc.get_multi(["key1", "key2", "key3"])

7. append 和 prepend

append    修改指定key的值,在該值 後面 追加內容
prepend   修改指定key的值,在該值 前面 插入內容

import memcache

mc = memcache.Client(['ip:port'], debug=True)
# k1 = "v1"

mc.append('k1', 'after')
# k1 = "v1after"

mc.prepend('k1', 'before')
# k1 = "beforev1after"

8. decr 和 incr  

incr  自增,將Memcached中的某一個值增加 N ( N默認爲1 )
decr 自減,將Memcached中的某一個值減少 N ( N默認爲1 )

import memcache

mc = memcache.Client(['ip:port'], debug=True)
mc.set('k1', '777')

mc.incr('k1')
# k1 = 778

mc.incr('k1', 10)
# k1 = 788

mc.decr('k1')
# k1 = 787

mc.decr('k1', 10)
# k1 = 777

9. gets 和 cas

如商城商品剩餘個數,假設改值保存在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來操作以上過程,那麼程序就會如上述所示情況!

如果想要避免此情況的發生,只要使用 gets 和 cas 即可,如:


import memcache
mc = memcache.Client(['ip:port'], debug=True, cache_cas=True)

v = mc.gets('product_count')
# ...
#
如果有人在gets之後和cas之前修改了product_count,那麼,下面的設置將會執行失敗,剖出異常,從而避免非正常數據的產生
mc.cas('product_count', "899")

本質上每次執行gets時,會從memcache中獲取一個自增的數字,通過cas去修改gets的值時,會攜帶之前獲取的自增值和memcache中的自增值進行比較,如果相等,則可以提交,如果不想等,那表示在gets和cas執行之間,又有其他人執行了gets(獲取了緩衝的指定值), 如此一來有可能出現非正常數據,則不允許修改。


Redis

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

一、Redis安裝和基本使用

wget http://download.redis.io/releases/redis-3.0.6.tar.gz
tar xzf redis-3.0.6.tar.gz
cd redis-3.0.6
make

啓動服務端

src/redis-server

啓動客戶端

src/redis-cli
redis> set foo bar
OK
redis> get foo
"bar"

二、Python操作Redis

sudo pip install redis
or
sudo easy_install redis
or
源碼安裝

詳見:
https://github.com/WoLpH/redis-py

API使用

redis-py 的API的使用可以分類爲:

  • 連接方式

  • 連接池

  • 操作

    • String 操作

    • Hash 操作

    • List 操作

    • Set 操作

    • Sort Set 操作

  • 管道

  • 發佈訂閱

1. 操作模式

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

import redis

r = redis.Redis(host='serverIP', port=6379)  # 默認端口
r.set('foo', 'Bar')
print(r.get('foo'))


2. 連接池

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

import redis

pool = redis.ConnectionPool(host='serverIP', port=6379) # 默認端口

r = redis.Redis(connection_pool=pool)
r.set('foo', 'Bar')
print(r.get('foo'))


一、key pattern 查詢相應的key

    (1)redis允許模糊查詢key  有3個通配符  *、?、[]

  (2)randomkey:返回隨機key  

  (3)type key:返回key存儲的類型

  (4)exists key:判斷某個key是否存在

  (5)del key:刪除key

  (6)rename key newkey:改名

  (7)renamenx key newkey:如果newkey不存在則修改成功

  (8)move key 1:將key移動到1數據庫

  (9)ttl key:查詢key的生命週期(秒)

  (10)expire key 整數值:設置key的生命週期以秒爲單位

  (11)pexpire key 整數值:設置key的生命週期以毫秒爲單位

  (12)pttl key:查詢key 的生命週期(毫秒)

  (13)persist key:把指定key設置爲永久有效


二、字符串類型的操作

    String操作,redis中的String在在內存中按照一個name對應一個value來存儲,即 key-value 模式

1. set key value [ex 秒數] [px 毫秒數] [nx/xx]

    在Redis中設置值,默認,不存在則創建,存在則修改,如果ex和px同時寫,則以後面的有效期爲準。

    參數:

         ex,過期時間(秒)

         px,過期時間(毫秒)

         nx,如果設置爲True,則只有name不存在時,當前set操作才執行

         xx,如果設置爲True,則只有name存在時,當前set操作才執行

            例:

        127.0.0.1:6379> set name shuoming ex 5

        OK

        127.0.0.1:6379> get name

        "shuoming"

        127.0.0.1:6379> get name

        (nil)      


2. mset key1 value1 key2 value2 :   一次設置多個值

            批量設置值

        如:

            127.0.0.1:6379> mset n2 name2 n3 name3

            或

            127.0.0.1:6379> mget n2 n3     一次獲取多個值


3. getset key newvalue  : 設置新值獲取舊值

        127.0.0.1:6379> get foo

        "a"

        127.0.0.1:6379> getset foo b

        "a"

        127.0.0.1:6379> get foo

        "b"

4. getrange key start stop : 獲取字符串中[start, stop]範圍的值

                    對於字符串的下標,左數從0開始,右數從-1開始

                    注意:當start>length,則返回空字符串

                       當stop>=length,則截取至字符串尾

                       如果start所處位置在stop右邊,則返回空字符串

        127.0.0.1:6379> set n1 abc123def456

        OK

        127.0.0.1:6379> getrange n1 1 3

        "bc1"

5. setrange key offset value    把字符串的offset偏移字節改成value,如果偏移量 > 字符串長度,

                                         該字符自動補0x00

        127.0.0.1:6379> setrange n1 3 AAA

        (integer) 12

        127.0.0.1:6379> get n1

        "abcAAAdef456"

6. setbit key offset value設置offset對應二進制上的值,返回該位上的舊值

                   注意:如果offset過大,則會在中間填充0

                      offset最大到多少

                      2^32-1,即可推出最大的字符串爲512M

    bitcount key start end 統計設置的二進制值的個數

import redis

pool = redis.ConnectionPool(host='ServerIP', port=6379)

r = redis.Redis(connection_pool=pool)
r.setbit('uv_count',2,1)
r.setbit('uv_count',2,1)
r.setbit('uv_count',4,1)
r.setbit('uv_count,5,1)
print('n count :',r.bitcount('n'))
n count : 3

7. strlen key取指定key的value值的長度


8. incr keyincr(self, name, amount=1) 

        # 自增 name對應的值,當name不存在時,則創建name=amount,否則,則自增。     

         # 參數:

                name,Redis的name

                amount,自增數(必須是整數)

         # 注:同incrby

        >>> import redis

        >>> r = redis.Redis()

        >>> r.incr('pv_count',1)

        1

        >>> r.incr('pv_count',1)

        2

        >>> r.incr('pv_count',1)

        3

        >>> r.get('pv_count')

        b'3'

9. append key value把value追加到key 的原值上


三、 hashes類型及操作

        Redis hash 是一個string類型的field和value的映射表,它的添加、刪除操作都是O(1)(平均)。hash特別適用於存儲對象,將一個對象存儲在hash類型中會佔用更少的內存,並且可以方便的存取整個對象。


配置:   hash_max_zipmap_entries 64  #配置字段最多64個

      hash_max_zipmap_value 512   #配置value最大爲512字節

  (1)hset myhash field value:   設置myhash的field爲value

  (2)hsetnx myhash field value: 不存在的情況下設置myhash的field爲value

  (3)hmset myhash field1 value1 field2 value2:同時設置多個field

  (4)hget myhash field:         獲取指定的hash field

  (5)hmget myhash field1 field2:一次獲取多個field

  (6)hincrby myhash field 5:    指定的hash field加上給定的值

  (7)hexists myhash field:      測試指定的field是否存在

  (8)hlen myhash:               返回hash的field數量

  (9)hdel myhash field:         刪除指定的field

  (10)hkeys myhash:             返回hash所有的field

  (11)hvals myhash:             返回hash所有的value

  (12)hgetall myhash:           獲取某個hash中全部的field及value


例:

    >>> import redis

    >>> r = redis.Redis()

    >>> r.hset('redis','k1','v1')

    >>> r.hmset('redis',{'k2':'v2','k3':'v3'})

    >>> r.hget('redis','k1')

    >>> r.hmget('redis',['k1','k2'])

    >>> r.hgetall('redis')

    >>> r.hlen('redis')

    >>> r.hkeys('redis')

    >>> r.hvals('redis')

    >>> r.hexists('redis','k1')

    >>> r.hdel('redis','k1')

    >>> r.hincrby('redis','k4',1)


四、鏈表操作

        Redis的list類型其實就是一個每個子元素都是string類型的雙向鏈表,鏈表的最大長度是2^32。list既可以用做棧,也可以用做隊列,List操作,redis中的List在在內存中按照一個name對應一個List來存儲。

                                                                    wKioL1bzSpzB2nE0AABYPvDAoJo052.png


list的pop操作還有阻塞版本,主要是爲了避免輪詢

   (1)lpush key value:把值插入到鏈表頭部

  (2)rpush key value:把值插入到鏈表尾部

  (3)lpop key :返回並刪除鏈表頭部元素

  (4)rpop key: 返回並刪除鏈表尾部元素

  (5)lrange key start stop:返回鏈表中[start, stop]中的元素

  (6)lrem key value count: 從鏈表中刪除value值,刪除count的絕對值個value後結束

                count > 0 從表頭刪除  count < 0 從表尾刪除  count=0 全部刪除

  (7)ltrim key start stop:剪切key對應的鏈接,移除沒有在[start, stop]之間的值

  (8)lindex key index:返回index索引上的值

  (9)llen key:計算鏈表的元素個數

   (10)linsert key after|before search value:在key鏈表中尋找search,並在search值之前|之後插入value

    (11)rpoplpush source dest:把source 的末尾拿出,放到dest頭部,並返回單元值


                應用場景: task + bak 雙鏈表完成安全隊列

                業務邏輯: rpoplpush task bak

         接收返回值並做業務處理

         如果成功則rpop bak清除任務,如果不成功,下次從bak表取任務


    (12)brpop,blpop key timeout:等待彈出key的尾/頭元素

                              timeout爲等待超時時間,如果timeout爲0則一直等待下去

            應用場景:長輪詢ajax,在線聊天時能用到

例:

    >>> r.lpush('redis_list','v1','v2','v3')

    >>> r.lpop('redis_list')

    >>> r.lrange('redis_list',0,-1)

    >>> r.lrem('redis_list','v1',1)

    >>> r.ltrim('redis_list',1,5)

    >>> r.lindex('redis_list',1)

    >>> r.llen('redis_list')

    >>> r.linsert('redis_list','after','v2','new_value')

    >>> r.rpoplpush('redis_list','new_list')

    >>> r.brpop('redis_list',3)

    

五、集合結構操作

            特點:無序性、確定性、唯一性

    (1)sadd key value1 value2:往集合裏面添加元素

  (2)smembers key:獲取集合所有的元素

  (3)srem key value:刪除集合某個元素

  (4)spop key:返回並刪除集合中1個隨機元素(可以做抽獎,不會重複抽到某人)   

  (5)srandmember key:隨機取一個元素

  (6)sismember key value:判斷集合是否有某個值

  (7)scard key:返回集合元素的個數

  (8)smove source dest value:把source的value移動到dest集合中

  (9)sinter key1 key2 key3:求key1 key2 key3的交集

  (10)sunion key1 key2:求key1 key2 的並集

  (11)sdiff key1 key2:求key1 key2的差集

  (12)sinterstore res key1 key2:求key1 key2的交集並存在res裏

例:

    >>> r.sadd('redis_set','s1','s2','s3')

    >>> r.smembers('redis_set')

    >>> r.srem('redis_set','s1')

    >>> r.spop('redis_set')

    >>> r.srandmember('redis_set')

    >>> r.sismember('redis_set','s1')

    >>> r.scard('redis_set')

    >>> r.smove('redis_set','new_set','s3')

    >>> r.sinter('redis_set','set1','set2')

    >>> r.sunion('redis_set','set1','set2')

    >>> r.sdiff('redis_set','set1')

    >>> r.sinterstore('redis_set','set1','set2')

    

六、有序集合

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


(1)zadd key score1 value1:添加元素

(2)zrange key start stop [withscore]:把集合排序後,返回名次[start,stop]的元素  默認是升續排列                                               withscores 是把score也打印出來

     r.zrange( name, start, end, desc=False, withscores=False, score_cast_func=float)

    # 參數:

        # 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)

(3)zrank key member:查詢member的排名(升序0名開始)

(4)zrem key value1 value2:刪除集合中的元素

(5)zremrangebyrank key start end:按排名刪除元素,刪除名次在[start, end]之間的

(6)zcard key:返回集合元素的個數

(7)zcount key min max:返回[min, max]區間內元素數量

例:

    >>> r.zadd('zset','k1',1,'k2',2)

    >>> r.zadd('zset',k3=3,k4=4)

    >>> r.zcard('zset')

    >>> r.zcount('zset',2,3)

    >>> r.zincrby('zset','k1',1)

    >>> r.zrank('zset','k1')


七、發佈訂閱   

     #  定義公共類


import redis


class RedisHelper(object):   # 公共類,發佈訂閱同時調用

   
def __init__(self):
       
self.__conn = redis.Redis(host='192.168.117.10')
       
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

訂閱者



from redis_helper import RedisHelper

obj = RedisHelper()
redis_sub = obj.subscribe()

while True:
   
msg = redis_sub.parse_response()
   
print(msg)

發佈者

from redis_helper import RedisHelper

obj = RedisHelper()
obj.public('hello')


八、服務器相關命令

    (1)ping:測定連接是否存活

  (2)echo:在命令行打印一些內容

  (3)select:選擇數據庫

  (4)quit:退出連接

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

  (6)info:獲取服務器的信息和統計

  (7)monitor:實時轉儲收到的請求

  (8)config get 配置項:獲取服務器配置的信息

     config set 配置項  值:設置配置項信息

  (9)flushdb:刪除當前選擇數據庫中所有的key

  (10)flushall:刪除所有數據庫中的所有的key

  (11)time:顯示服務器時間,時間戳(秒),微秒數

  (12)bgrewriteaof:後臺保存rdb快照

  (13)bgsave:後臺保存rdb快照

  (14)save:保存rdb快照

  (15)lastsave:上次保存時間

  (16)shutdown [save/nosave]

      注意:如果不小心運行了flushall,立即shutdown nosave,關閉服務器,然後手工編輯aof文件,去

掉文件中的flushall相關行,然後開啓服務器,就可以倒回原來是數據。如果flushall之後,系統恰好bgwriteaof

了,那麼aof就清空了,數據丟失。

  (17)showlog:顯示慢查詢

      問:多慢才叫慢?

      答:由slowlog-log-slower-than 10000,來指定(單位爲微秒)

      問:服務器存儲多少條慢查詢記錄

      答:由slowlog-max-len 128,來做限制 




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