1. 程式人生 > >memcache、redis

memcache、redis

memcache compare graph web msg 兩個 over 編寫 二進制

Memcached

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

  • 啟動memcached

    memcached -d -m 10 -u root -l 10.211.55.4 -p 12000 -c 256 -P /tmp/memcached.pid
    參數說明:
    -d 是啟動一個守護進程
    -m 是分配給Memcache使用的內存數量,單位是MB

    -u 是運行Memcache的用戶
    -l 是監聽的服務器IP地址
    -p 是設置Memcache監聽的端口,最好是1024以上的端口
    -c 選項是最大運行的並發連接數,默認是1024,按照你服務器的負載量來設定
    -P 是設置保存Memcache的pid文件

  • 添加

    set 創建一個鍵值對,如果不存在,則創建,否則修改
    set_multi 創建多個鍵值對,如果不存在,則創建,否則修改
    add 添加一個鍵值對,如果存在相同的key,報錯
    append 修改指定key的值,在指定key對應的值的後面添加內容
    prepend 修改指定key的值,在指定key對應的值的前面添加內容

    mc = memcache.Client([‘127.0.0.1:11211‘,],debug=True)
    mc.set(‘k1‘,‘v1‘) # k1=v1
    mc.set_multi({‘k2‘:‘v2‘,‘k3‘:‘v3‘}) # k1=v1,k2=v2
    mc.add(‘k4‘,‘v4‘) # k4=v4
    mc.append(‘k1‘,‘after‘) # k1=v1after
    mc.prepend(‘k2‘,‘before‘) # k2=beforev2
  • 刪除

    delte 刪除指定的鍵值對
    delete_multi 刪除多個鍵值對

    mc = memcache.Client([‘127.0.0.1:11211‘,],debug=True)
    mc.delete(‘k1‘)
    mc.delete_multi([‘k2‘,‘k3‘])
  • 修改

    replace 修改指定key的值,如果key不存在,則報錯
    incr 將指定key對應的值自增n,如果為指定n,n為1
    decr 將指定key對應的值自減n,如果為指定n,n為1
    cas 修改指定key對應的值,避免產生臟數據

    mc = memcache.Client([‘127.0.0.1:11211‘,],debug=True)
    mc.replace(‘k1‘,‘123‘) # k1=123
    mc.replace(‘k2‘,‘123‘) # k2=123
    mc.replace(‘k3‘,‘123‘) # k2=123
    mc.incr(‘k1‘,2) # k1=125
    mc.decr(‘k2‘,2)# k2=121
    mc.cas(‘k3‘,‘456‘) # k3=456
  • 查詢

    get 獲取指定key對應的值
    get_multi 獲取多個建對應的值
    gets 獲取指定key對應的值,避免產生臟數據

    mc = memcache.Client([‘127.0.0.1:11211‘,],debug=True)
    print(mc.get(‘k1‘)) # 125
    print(mc.get_multi([‘k1‘,‘k2‘,‘k3‘])) # {‘k1‘: ‘125‘, ‘k2‘: ‘121‘, ‘k3‘: ‘456‘}
    print(mc.gets(‘k1‘)) # 125

    Redis

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

  • 啟動redis

    啟動服務端
    src/redis-server
    啟動客戶端
    src/redis-cli

  • 連接池

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

  • String操作 redis中的String在在內存中按照一個name對應一個value來存儲
    • set(name, value, ex=None, px=None, nx=False, xx=False) 設置值,ex,過期時間(秒) px,過期時間(毫秒)nx為Ture,只有name不存在,當前操作執行,xx為True,只有name存在,當前操作執行

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

    • setex(name, value, time) 設置值,time表示過期時間(秒)

    • psetex(name, time_ms, value) 設置值,time_ms表示過期時間(毫秒)

    • mset(*args, **kwargs) 批量設置值

    • get(name) 獲取值

    • mget(keys, *args) 批量獲取

    • getset(name, value) 設置新值並獲取舊值

    • getrange(key, start, end) 獲取子序列,start表示開始位置(字節),end表示結束位置(字節)

    • setrange(name, offset, value) 修改字符串內容,從指定字符串索引開始向後替換

    • setbit(name, offset, value) 修改字符串,offset表示字節索引,value只能設置1或0

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

    • bitcount(key, start=None, end=None) 獲取name對應的值的二進制表示中1的個數,start為開始位置,end為結束位置

    • bitop(operation, dest, keys) 獲取多個值,並將值做位運算,將最後的結果保存至新的name對應的值,operation,AND(並)、OR(或)、 NOT(非)、XOR(異或)dest,新的name,key,要查找的name

    • strlen(name) 返回name的字節長度

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

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

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

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

      pool = redis.ConnectionPool(host=‘127.0.0.1‘,port=6379)
      r = redis.Redis(connection_pool=pool)
      r.set(‘k1‘,‘v1‘,ex=None,px=None,nx=False,xx=False)
      r.setnx(‘k2‘,‘k2‘)
      r.setex(‘k3‘,‘v3‘,time=1)
      r.psetex(‘k4‘,time_ms=1000,value=‘v4‘)
      r.mset({‘k5‘:‘v5‘,‘k6‘:‘v6‘})
      print(r.get(‘k1‘))
      print(r.mget([‘k2‘,‘k3‘,‘k4‘]))
      print(r.getset(‘k5‘,‘V5‘))
      print(r.getrange(‘k1‘,start=0,end=1))
      r.setrange(‘k2‘,offset=0,value=1)
      r.setbit(‘k3‘,offset=1,value=1)
      print(r.getbit(‘k4‘,offset=1))
      print(r.bitcount(‘k5‘,start=0,end=-1))
      r.bitop(‘AND‘,‘k6‘,‘k1‘,‘k2‘)
      print(r.strlen(‘k1‘))
      r.incr(‘k2‘, amount=1)
      r.incrbyfloat( ‘k2‘, amount=1.0)
      r.decr(‘k3‘,amount=1)
      r.append(‘k4‘, ‘after‘)
  • Hash操作 redis中的List在在內存中按照一個name對應一個Dict來存儲
    • hset(name, key, value) name對應的hash中設置一個鍵值對(不存在,則創建;否則,修改)

    • hmset(name, mapping) 在name對應的hash中批量設置鍵值對

    • hget(name,key) 在name對應的hash中獲取根據key獲取value

    • hmget(name, keys, *args) 在name對應的hash中獲取多個key的值

    • 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可以實現分片的獲取數據,並非一次性將數據全部獲取完,從而放置內存被撐爆

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

      pool = redis.ConnectionPool(host=‘127.0.0.1‘,port=6379)
      r = redis.Redis(connection_pool=pool)
      r.hset(‘hash‘, ‘k1‘, ‘v1‘)
      r.hmset(‘hash‘,{‘k2‘:‘v2‘,‘k3‘:‘v3‘})
      print(r.hget(‘bash‘,‘k1‘))
      print(r.hmget(‘hash‘,‘k1‘,‘k2‘,‘k3‘))
      print(r.hgetall(‘bash‘))
      print(r.hlen(‘bash‘))
      print(r.hkeys(‘bash‘))
      print(r.hvals(‘bash‘))
      print(r.hexists(‘bash‘,‘k1‘))
      print(r.hdel(‘bash‘,‘k1‘,‘k2‘,‘k3‘))
      r.hincrby(‘bash‘, ‘k1‘, amount=1)
      r.hincrbyfloat(‘bash‘, ‘k2‘, amount=1.0)
      print(r.hscan(‘bash‘, cursor=0, match=None, count=None))
      print(r.hscan_iter(‘bash‘, match=None, count=None))
      
  • List操作 redis中的List在在內存中按照一個name對應一個List來存儲
    • lpush(name,values) 在name對應的list中添加元素,每個新的元素都添加到列表的最左邊

    • lpushx(name,value) 在name對應的list中添加元素,只有name已經存在時,值添加到列表的最左邊

    • llen(name) name對應的list元素的個數

    • linsert(name, where, refvalue, value)) 在name對應的列表的某一個值前或後插入一個新值

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

    • lrem(name, value, num) 在name對應的list中刪除指定的值

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

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

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

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

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

    • blpop(keys, timeout) 將多個列表排列,按照從左到右去pop對應列表的元素

    • brpoplpush(src, dst, timeout=0) 從一個列表的右側移除一個元素並將其添加到另一個列表的左側

      pool = redis.ConnectionPool(host=‘127.0.0.1‘,port=6379)
      r = redis.Redis(connection_pool=pool)
      r.lpush(‘list‘,11,22,33)
      r.lpush(‘rlist‘,44,55,66)
      r.lpushx(‘list‘,44)
      print(r.llen(‘list‘))
      r.linsert(‘list‘, ‘after‘, 33, 44)
      r.lset(‘list‘,0 , 111)
      r.lrem(‘list‘, 22, 0)
      print(r.lpop(‘list‘))
      print(r.lindex(‘list‘, 0))
      print(r.lrange(‘list‘, 0, -1))
      print(r.ltrim(‘list‘, 0, -1))
      r.rpoplpush(‘list‘, ‘rlist‘)
      r.blpop([‘list‘,‘rlist‘], timeout=1)
      r.brpoplpush(‘list‘, ‘rlist‘, timeout=0)
      def list_iter(name):
       """
       自定義redis列表增量叠代
       :param name: redis中的name,即:叠代name對應的列表
       :return: yield 返回 列表元素
       """
       list_count = r.llen(name)
       for index in range(list_count):
           yield r.lindex(name, index)
      
      for item in list_iter(‘list‘):
       print(item)
  • 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(‘set1‘,‘111,222,333‘)
      r.sadd(‘set2‘,‘222,333,444‘)
      r.sadd(‘set3‘,‘333,444,555‘)
      r.sadd(‘set4‘,‘444,555,666‘)
      print(r.scard(‘set‘))
      r.sdiff(‘set1‘,‘set2‘,‘set3‘)
      r.sdiffstore(dest=‘set4‘,keys=[‘set1‘,‘set2‘,‘set3‘])
      r.sinter(‘set1‘,‘ste2‘,‘set3‘)
      r.sinterstore(dest=‘set4‘,keys=[‘set1‘,‘set2‘,‘set3‘])
      r.sismember(‘set1‘,‘111‘)
      r.smembers(‘set1‘)
      r.smove(‘set1‘, ‘set2‘, ‘111‘)
      r.spop(‘set1‘)
      r.srandmember(‘set1‘, 1)
      r.srem(‘set1‘, ‘111‘)
      r.sunion(‘set1‘,‘ste2‘,‘set3‘)
      r.sunionstore(dest=‘set4‘,keys=[‘set1‘,‘set2‘,‘set3‘])
      r.sscan(‘set1‘, cursor=0, match=None, count=None)
      r.sscan_iter(‘set1‘, match=None, count=None)

    有序集合

    • zadd(name, *args, **kwargs) 在name對應的有序集合中添加元素

    • zcard(name) 獲取name對應的有序集合元素的數量

    • zcount(name, min, max) 獲取name對應的有序集合中分數 在 [min,max] 之間的個數

    • zincrby(name, value, amount) 自增name對應的有序集合的 name 對應的分數

    • r.zrange( name, start, end, desc=False, withscores=False, score_cast_func=float) 按照索引範圍獲取name對應的有序集合的元素

    • zrank(name, value) 獲取某個值在 name對應的有序集合中的排行(從 0 開始)

    • zrangebylex(name, min, max, start=None, num=None) 當有序集合的所有成員都具有相同的分值時,有序集合的元素會根據成員的 值 (lexicographical ordering)來進行排序,而這個命令則可以返回給定的有序集合鍵 key 中, 元素的值介於 min 和 max 之間的成員,對集合中的每個成員進行逐個字節的對比(byte-by-byte compare), 並按照從低到高的順序, 返回排序後的集合成員,如果兩個字符串有一部分內容是相同的話, 那麽命令會認為較長的字符串比較短的字符串要大

    • 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進行操作

    • zunionstore(dest, keys, aggregate=None) 獲取兩個有序集合的並集,如果遇到相同值不同分數,則按照aggregate進行操作

    • zscan(name, cursor=0, match=None, count=None, score_cast_func=float) 同字符串相似,相較於字符串新增score_cast_func,用來對分數進行操作

    • 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(‘set‘,n1=11,n2=22,n3=33)
      r.zcard(‘set‘)
      r.zcount(‘set‘, 11, 33)
      r.zincrby(‘set‘, 11, 1)
      r.zrange(‘set‘, 0, -1, desc=False, withscores=False, score_cast_func=float)
      r.zrank(‘set‘, 11)
      r.zrangebylex(‘set‘,11,33,0,2)
      r.zrem(‘set1‘, [‘n1‘,‘n2‘])
      r.zremrangebyrank(‘set‘, min, max)
      r.zremrangebyscore(‘set‘, min, max)
      r.zremrangebylex(‘set‘, min, max)
      r.zscore(‘set‘, 11)
      r.zinterstore(‘set1‘, [‘n1‘,‘n2‘], aggregate=None)
      r.zunionstore(‘set1‘, [‘n1‘,‘n2‘], aggregate=None)
      r.zscan(‘set‘, cursor=0, match=None, count=None, score_cast_func=float)
      r.zscan_iter(‘set‘, match=None, count=None,score_cast_func=float)
  • 管道 redis-py默認在執行每次請求都會創建(連接池申請連接)和斷開(歸還連接池)一次連接操作,如果想要在一次請求中指定多個命令,則可以使用pipline實現一次請求指定多個命令,並且默認情況下一次pipline 是原子性操作

    import redis
    pool = redis.ConnectionPool(host=‘10.211.55.4‘, port=6379)
    r = redis.Redis(connection_pool=pool)
    pipe = r.pipeline(transaction=False)
    pipe = r.pipeline(transaction=True)
    pipe.multi()
    pipe.set(‘name‘, ‘kernel‘)
    pipe.set(‘score‘, ‘good‘) 
    pipe.execute()
  • 發布訂閱

    發布者:
    from monitor.RedisHelper import RedisHelper
    obj = RedisHelper()
    obj.public(‘hello‘)
    訂閱者:
    from monitor.RedisHelper import RedisHelper
    obj = RedisHelper()
    redis_sub = obj.subscribe()
    while True:
      msg = redis_sub.parse_response()
      print(msg)
  • sentinel

    redis重的sentinel主要用於在redis主從復制中,如果master顧上,則自動將slave替換成master

    from redis.sentinel import Sentinel
    # 連接哨兵服務器(主機名也可以用域名)
    sentinel = Sentinel([(‘10.211.55.20‘, 26379),
                       (‘10.211.55.20‘, 26380),
                       ],
                      socket_timeout=0.5)
    # 獲取主服務器地址
    master = sentinel.discover_master(‘mymaster‘)
    print(master)
    # 獲取從服務器地址
    slave = sentinel.discover_slaves(‘mymaster‘)
    print(slave)
    # 獲取主服務器進行寫入
    master = sentinel.master_for(‘mymaster‘)
    master.set(‘foo‘, ‘bar‘)
    # 獲取從服務器進行讀取(默認是round-roubin)
    slave = sentinel.slave_for(‘mymaster‘, password=‘redis_auth_pass‘)
    r_ret = slave.get(‘foo‘)
    print(r_ret)

memcache、redis