Python學習筆記 - day12 - Python操作NoSQL

Python學習筆記 - day12 - Python操作NoSQL

NoSQL(非關係型數據庫)

  NoSQL,指的是非關係型的數據庫。NoSQL有時也稱作Not Only SQL的縮寫,是對不同於傳統的關係型數據庫的數據庫管理系統的統稱。用於超大規模數據的存儲。(例如谷歌或Facebook每天爲他們的用戶收集萬億比特的數據)。這些類型的數據存儲不需要固定的模式,無需多餘操作就可以橫向擴展。redis、memcached是典型的並且使用比較多的NoSQL之一。

NoSQL之Redis

  Redis是一個key-value存儲系統。和Memcached類似,它支持存儲的value類型相對更多,包括string(字符串)、list(鏈表)、set(集合)、zset(sorted set --有序集合)和hash(哈希類型)。這些數據類型都支持push/pop、add/remove及取交集並集和差集及更豐富的操作,而且這些操作都是原子性的。在此基礎上,redis支持各種不同方式的排序。與memcached一樣,爲了保證效率,數據都是緩存在內存中。區別的是redis會週期性的把更新的數據寫入磁盤或者把修改操作寫入追加的記錄文件,並且在此基礎上實現了master-slave(主從)同步。 Redis的出現,很大程度補償了memcached這類key/value存儲的不足,在部分場合可以對關係數據庫起到很好的補充作用。它提供了Python,Ruby,Erlang,PHP客戶端,使用很方便,並且Redis還支持主從同步。數據可以從主服務器向任意數量的從服務器上同步,從服務器可以是關聯其他從服務器的主服務器。這使得Redis可執行單層樹複製。從盤可以有意無意的對數據進行寫操作。由於完全實現了發佈/訂閱機制,使得從數據庫在任何地方同步樹時,可訂閱一個頻道並接收主服務器完整的消息發佈記錄。

redis模塊

  redis提供了對Python的支持,Python利用redis模塊對Redis數據庫進行操作。

安裝redis模塊

  pip命令是Python提供的非常好用的包管理工具,在2.7+以上的版本默認安裝,這裏使用pip命令安裝模塊

pip install redis

  安裝完畢後可以在解釋器中輸入 import redis 進行測試,如果沒有報錯,則表示安裝成功

連接redis

  redis模塊提供兩個類:Redis和StrictRedis,用於實現Redis的命令,StrictRedis用於實現大部分官方的命令,並使用官方的語法和命令,Redis是StrictRedis的子類,用於向後兼容舊版本的redis模塊,但Redis的部分語法參數順序和實際使用redis-cli操作時是不同的,官方建議使用StricRedis來連接數據庫,其用法和Redis實例化的對象大部分時相同的,這裏使用Redis類來完成相關舉例操作,生產上可以使用StrictRedis來實例化redis對象。

import redis

redis_obj = redis.Redis(host='10.0.0.3',port='6379',db=0)
redis_obj.set('name','daxin')
print(redis_obj.get('name'))


# 利用redis.Redis實例化一個redis數據庫,host表示地址,port表示端口,db表示數據庫
# set 表示設置一個key,name表示key的名稱,daxin表示對應的值
# get 表示獲取一個key的值

鏈接池

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

import redis

pool = redis.ConnectionPool(host='10.0.0.3',port='6379',db=0)
r = redis.Redis(connection_pool=pool)
r.set('age','18')
print(r.get('age'))

# 使用ConnectionPool 創建一個鏈接池
# 實例化redis對象的時候,把鏈接池傳入即可

管道(pipline)

  Redis是一個cs模式的tcp server,使用和http類似的請求響應協議。一個client可以通過一個socket連接發起多個請求命令。每個請求命令發出後client通常會阻塞並等待redis服務處理,redis處理完後請求命令後會將結果通過響應報文返回給client。基本的通信過程如下:

基本上兩個命令需要4個tcp報文才能完成。由於通信會有網絡延遲,假如從client和server之間的包傳輸時間需要0.25秒。那麼上面的兩個命令4個報文至少會需要1秒才能完成。這樣即使redis每秒能處理100個命令,而我們的client也只能一秒鐘發出兩個命令。這顯示沒有充分利用 redis的處理能力。除了可以利用mget,mset 之類的單條命令處理多個key的命令外我們還可以利用pipeline的方式從client打包多條命令一起發出,不需要等待單條命令的響應返回,而redis服務端會處理完多條命令後會將多條命令的處理結果打包到一起返回給客戶端。

import redis

pool = redis.ConnectionPool(host='10.0.0.3',port='6379',db=0)
r = redis.Redis(connection_pool=pool)

pipline = r.pipeline(transaction=True)

pipline.set('hobby','girl')
pipline.set('hobby','girl')
pipline.set('hobby','girl')
pipline.set('hobby','girl')
pipline.set('hobby','girl')

pipline.execute()

# 利用redis對象的pipline創建pipline對象
# 利用pipeline對象執行set命令
# 利用pipline對象的execute函數批量執行

理解:pipline對象就是一個容器,我們把要執行的語句放入其中,調用其excute函數去批量執行。

注意:在get請求的時候,執行pipe.exceute(),會批量返回,但是是有序的,我們可以定義多個變量去接受

 1 import redis
 2 
 3 pool = redis.ConnectionPool(host='10.0.0.3',port='6379',db=0)
 4 r = redis.Redis(connection_pool=pool)
 5 
 6 pipline = r.pipeline(transaction=True)
 7 
 8 pipline.get('name')
 9 pipline.get('age')
10 pipline.get('job')
11 pipline.get('hobby')
12 
13 name,age,job,hobby = pipline.execute()  # 定義4個變量接受
14 
15 print(name,age,job,hobby)
16  
pipline批量get請求

redis數據類型及操作

  redis執行的數據類型衆多,但常用的比如string,list,set等,這裏我們將列舉不同類型的相關操作

String

1、set 設置值,不存在創建,存在則更新。

import redis

pool = redis.ConnectionPool(host='10.0.0.3',port='6379',db=0)
r = redis.Redis(connection_pool=pool)
r.set(name, value, ex=None, px=None, nx=False, xx=False)

# name  表示key的名稱
# value 表示對應的值
# ex    過期時間(秒)
# px    過期時間(毫秒)
# nx    如果設置爲True,則只有name不存在時,當前set操作才執行,同setnx(name, value)
# xx    如果設置爲True,則只有name存在時,當前set操作才執行

PS:針對過期時間還可以用另外兩個函數進行單獨調整

setex(name, value, time)     #設置過期時間(秒)
psetex(name, time_ms, value)    #設置過期時間(豪秒)

2、mset 批量設置值,是set的升級款

r.mset(name='daxin', age='18')
r.mset({"name":'daxin', "age":'18'})

# mset接受 *args和 **kwargs。
# 所以只能傳遞dict 或者 命名關鍵詞參數,否則會報RedisError異常

3、get/mget 獲取/批量獲取key值

r.get(name)
r.mget(name1,name2)

# get函數只有一個參數:key名稱
# mget可以列舉多個key名稱,或者傳遞一個list

li = ['name','age']
print(r.mget(li))

4、getset 設置新值,打印原值

r.getset(name,value)  

print(r.getset('name','dachenzi'))

# 打印name的值,並設置其的值爲dachenzi

5、getrange 字符串的截取(類似字符串的切片)

r.getrange(name,start,end)  # key 起始位置  結束位置

r.set('name','dachenzi')
print(r.getrange('name',2,5))
# 則會打印 chen

6、setrange(name, offset, value) 字符串的分片替換

# r.setrange(name,offset,value)  # key,偏移位置(起始位置), 要替換的字符串
#修改字符串內容,從指定字符串索引開始向後替換,如果新值太長時,則向後添加

r.set('name','dachenzi')
r.setrange('name',5,'a')
print(r.get('name'))  # 打印dacheazi

7、strlen 統計字符串的字節長度

# r.strlen(name)  

r.set('name','dachenzi')
print(r.strlen('name'))   # 打印8

# 注意:一個漢字佔用3個字節

8、incr/decr 自增/自減對應的值(整型) 

# r.incr(name,amount) amount默認值爲1
# 自增mount對應的值,當mount不存在時,則創建mount=amount,否則,則自增,amount爲自增數(整數,也可以理解爲步長)

print(r.incr("mount",amount=2)) #輸出:2
print(r.incr("mount"))  #輸出:3
print(r.incr("mount",amount=3)) #輸出:6
print(r.incr("mount",amount=6)) #輸出:12
print(r.get("mount"))   #輸出:12

# r.decr(name,amount) amount默認值爲1
# 自減name對應的值,當name不存在時,則創建name=amount,否則,則自減,amount爲自增數(整數)

9、append 在對應的key後面進行追加

# r.append(name,value)   在 name的對應的值後面追加value
# name存在會追加,不存在會創建

r.set('name','daxin')
r.append('name',' hello world')
print(r.get('name))   # 打印: 'daxin hello world'

List

1、lpush(name,values) 向list中添加元素,FIFO模式(先進先出)

# 在name對應的list中添加元素,每個新的元素都添加到列表的最左邊
r.lpush("daxin_list",2)
r.lpush("daxin_list",3,4,5)

print(r.lrange('daxin_list',0,-1))        保存在列表中的順序爲5,4,3,2

2、rpush(name,values) 向list中添加元素,棧模式(先進後出)

# 在name對應的list中添加元素,每個新的元素都添加到列表的最右邊
r.rpush("daxin_list2",2)
r.rpush("daxin_list2",3,4,5)  

print(r.lrange('daxin_list2',0,-1))     # 2,3,4,5

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

4、rpushx(name,value)  在name對應的list中添加元素,只有name已經存在時,值添加到列表的最右邊

5、llen(name)  獲取name對應的list元素的個數

print(r.llen("daxin_list"))

6、linsert(name, where, refvalue, value)) 插入元素

# 在name對應的列表的某一個值前或後插入一個新值
r.linsert("daxin_list","BEFORE","2","SS")#在列表內找到第一個元素2,在它前面插入SS

# 參數:
#     name: redis的name
#     where: BEFORE(前)或AFTER(後)
#     refvalue: 列表內的值
#     value: 要插入的數據

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

r.lset("daxin_list",0,"bbb")

8、lrem(name, value, count)  刪除name對應的list中的指定值

r.lrem(name,value,count)

# 參數:
#    name:  redis的name
#    value: 要刪除的值
#    count > 0 : 從表頭開始向表尾搜索,移除與 VALUE 相等的元素,數量爲 COUNT 。
#    count < 0 : 從表尾開始向表頭搜索,移除與 VALUE 相等的元素,數量爲 COUNT 的絕對值。
#    count = 0 : 移除表中所有與 VALUE 相等的值。

9、lpop(name)  移除列表的左側第一個元素,返回值則是第一個元素

print(r.lpop("daxin_list"))

10、lindex(name, index)  根據索引獲取列表內元素

print(r.lindex("daxin_list",1))

11、lrange(name, start, end)  分片獲取元素

print(r.lrange("daxin_list",0,-1))

# 0 表示 其實位置,-1表示末尾
# 所以 0,-1 就表示列表的所有

12、ltrim(name, start, end)  移除列表內沒有在該索引之內的值

r.lrange('daxin_list')   # 2,3,4,5
r.ltrim("daxin_list",0,2)  # 會刪除 5

13、rpoplpush(src, dst)  從src取出最右邊的元素,同時將其添加至dst的最左邊

r.lpush('dachenzi_list',1,2,3,4,5)
r.lpush('dachenzi_list2',5,4,3,2,1)
r.rpoplpush('dachenzi_list','dachenzi_list2')  
# dachenzi_list: 2,3,4,5
# dachenzi_list2: 1,1,2,3,4,5

14、brpoplpush(src, dst, timeout=0) 

#同rpoplpush,多了個timeout, timeout:取數據的列表沒元素後的阻塞時間,0爲一直阻塞
r.brpoplpush("dachenzi_list","dachezi_list2",timeout=0)

15、blpop(keys, timeout)

#將多個列表排列,按照從左到右去移除各個列表內的元素
r.lpush("dachenzi_list",3,4,5)
r.lpush("dachenzi_list2",3,4,5)

while True:
    print(r.blpop(["dachenzi_list","dachenzi_list2"],timeout=0))
    print(r.lrange("dachenzi_list",0,-1),r.lrange("dachenzi_list2",0,-1))
# 先挨個刪除dachenzi_list,然後再挨個刪除dachenzi_list2。


# r.blpop(keys,timout)
# 參數:
#   keys: redis的name的集合
#   timeout: 超時時間,獲取完所有列表的元素之後,阻塞等待列表內有數據的時間(秒), 0 表示永遠阻塞'''

16、brpop(keys, timeout)  同blpop,將多個列表排列,按照從右像左去移除各個列表內的元素

set之無序集合

Set集合就是不允許重複的列表,在redis中,集合分爲無序和有序,set表示無序集合,sortes set表示有序集合。

1、sadd(name,values)  給name對應的集合中添加元素

r.sadd('daxin_set1',1,2,3,4,5)
r.sadd('daxin_set1',1,2,3,4,5,6,7)

print(r.smembers('daxin_set1'))

2、smembers(name)  獲取name對應的集合的所有成員

3、scard(name)  獲取name對應的集合中的元素個數

r.sadd('daxin_set',1,2,3,4,5)
r.sadd('daxin_set',1,2,3,4,5,6,7)
print(r.scard('daxin_set'))     # 打印7個

4、sdiff(keys, *args)  在第一個name對應的集合中且不在其他name對應的集合的元素集合(第一個set和其他set的差集)

r.sadd('daxin_set',1,2,3)
r.sadd('daxin_set2',2)
r.sadd('daxin_set3',3)

print(r.sdiff('daxin_set','daxin_set2','daxin_set3'))   # 打印1

5、sdiffstore(dest, keys, *args)  相當於把sdiff獲取的值加入到dest對應的集合中

r.sadd('daxin_set',1,2,3)
r.sadd('daxin_set2',2)
r.sadd('daxin_set3',3)

r.sdiffstore('daxin_set4','daxin_set','daxin_set2','daxin_set3')
print(r.smembers('daxin_set4'))    # 打印1

6、sinter(keys, *args)  獲取同時存在指定集合中的元素

r.sadd('daxin_set',1,2,3)
r.sadd('daxin_set2',2,1)
r.sadd('daxin_set3',3,1)

print(r.sinter('daxin_set','daxin_set2','daxin_set3'))  # 打印1

7、sinterstore(dest, keys, *args)  獲取多個name對應集合的並集,再講其加入到dest對應的集合中

8、sismember(name, value)  檢查value是否是name對應的集合內的元素

r.sadd('daxin_set','a','b','c')
print(r.sismember('daxin_set','c'))     # true
print(r.sismember('daxin_set','d'))     # false

9、smove(src, dst, value)  將某個元素從一個集合中移動到另外一個集合

r.sadd('daxin_set','a','b','c')
r.sadd('daxin_set2',1,2)

r.smove('daxin_set','daxin_set2','c')

print(r.smembers('daxin_set2'))   # 打印1,2,c

10、spop(name)  從集合的右側移除一個元素,並將其返回

11、srandmember(name, numbers)  從name對應的集合中隨機獲取numbers個元素

r.sadd('daxin_set','a','b','c','d','e','f','g')

print(r.srandmember('daxin_set',2))   # 隨機取兩個,每次都不同

12、srem(name, values) 刪除name對應的集合中的某些值

r.sadd('daxin_set','a','b','c','d','e','f','g')

print(r.srem('daxin_set','a','b','z'))    # 返回實際刪除的members個數,這裏返回2
print(r.smembers('daxin_set'))

13、sunion(keys, *args) 獲取多個name對應的集合的並集

r.sadd('daxin_set','a','b')
r.sadd('daxin_set1','c','d')
r.sadd('daxin_set2','e','f','g')

print(r.sunion('daxin_set','daxin_set2','daxin_set1'))
# 打印 {b'c', b'a', b'f', b'e', b'g', b'd', b'b'}

14、sunionstore(dest,keys, *args)  獲取多個name對應的集合的並集,並將結果保存到dest對應的集合中

set之有序集合

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

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

# r.zadd(name,value,score...)
r.zadd('daxin_set','name',1,'age',2,'job',3)
print(r.zrange('daxin_set',0,-1))

# 打印 [b'name', b'age', b'job']

2、zcard(name) 獲取有序集合內元素的數量

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

r.zadd('daxin_set','name',1,'age',6,'job',3)
print(r.zcount('daxin_set',1,2))     # 打印1
print(r.zcount('daxin_set',1,3))     # 打印2

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

r.zadd('daxin_set','name',1,'age',6,'job',3)
print(r.zrange('daxin_set',0,-1))    # 順序是 [b'name', b'job', b'age']
r.zincrby('daxin_set','name',amount=7)   # 改變了name的score,影響排序
print(r.zrange('daxin_set',0,-1))    # 順序是 [b'job', b'age', b'name']

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

r.zrange("name",start,end,desc=False,withscores=True,score_cast_func=int)
# 參數:
#    name    redis的name
#    start   有序集合索引起始位置
#    end     有序集合索引結束位置
#    desc    排序規則,默認按照分數從小到大排序
#    withscores  是否獲取元素的分數,默認只獲取元素的值
#    score_cast_func 對分數進行數據轉換的函數

r.zadd('daxin_set','name',1,'age',6,'job',3)
print(r.zrange('daxin_set',0,1,withscores=True))

6、zrevrange(name, start, end, withscores=False, score_cast_func=float)  同zrange,集合是從大到小排序的

7、zrank(name, value)、zrevrank(name, value)  獲取value值在name對應的有序集合中的排行位置(從0開始)

# zrank(name,value)  # 正序(從左邊開始)
r.zadd('daxin_set','name',1,'age',6,'job',3)
print(r.zrank('daxin_set','job'))   # 打印2

# zrevrank(name,value)   # 倒序(從右邊開始)
r.zadd('daxin_set','name',1,'age',6,'job',3)
print(r.zrevrank('daxin_set','age'))   # 打印0

8、zscore(name, value)  獲取name對應有序集合中 value 對應的分數

r.zadd('daxin_set','name',1,'age',6,'job',3)

print(r.zscore('daxin_set','age'))    # 打印 6.0

9、zrem(name, values)  刪除name對應的有序集合中值是values的成員

r.zadd('daxin_set','name',1,'age',6,'job',3)
r.zrem('daxin_set','name','age')

print(r.zrange('daxin_set',0,-1))  # 打印 'job'

10、zremrangebyrank(name, min, max)  根據排行範圍刪除

r.zadd('daxin_set','name',1,'age',6,'job',3)
print(r.zrange('daxin_set',0,-1,withscores=True))

r.zremrangebyrank('daxin_set',0,1)  # 按照排序後的索引進行刪除,0對應第一個元素,就是name,1對應第二個元素就是job 
print(r.zrange('daxin_set',0,-1))   # 打印 age

11、zremrangebyscore(name, min, max)  根據分數範圍刪除

hash

redis中的Hash 在內存中類似於一個name對應一個dic來存儲 

1、hset(name, key, value)  name對應的hash中設置一個鍵值對(不存在,則創建,否則,修改)

r.hset('dachenzi_hash','name','daxin')
print(r.hget('dachenzi_hash','name'))

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

r.hset('dachenzi_hash','name','daxin')
print(r.hget('dachenzi_hash','name'))

3、hgetall(name)  獲取name對應hash的所有鍵值

r.hset('dachenzi_hash','name','daxin')
r.hset('dachenzi_hash','nage',18)
print(r.hgetall('dachenzi_hash'))

3、hmset(name, mapping)  在name對應的hash中批量設置鍵值對,mapping:字典

dic = {'name':'daxin','age':18}
r.hmset('dachenzi_hash',dic)

print(r.hgetall('dachenzi_hash'))

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

dic = {'name':'daxin','age':18}
r.hmset('dachenzi_hash',dic)
print(r.hmget('dachenzi_hash','name','age'))

# 支持傳遞list類型
li = ['name','age']
print(r.hmget('dachenzi_hash',li))

5、hlen(name)  獲取hash中鍵值對的個數,hkeys(name)  獲取hash中所有的key的值,hvals(name)  獲取hash中所有的value的值

dic = {'name':'daxin','age':18,'job':'Linux','Like':'python'}
r.hmset('dachenzi_hash',dic)

print(r.hlen('dachenzi_hash'))    # 4
print(r.hkeys('dachenzi_hash'))  # [b'name', b'age', b'job', b'Like']
print(r.hvals('dachenzi_hash'))   # [b'daxin', b'18', b'Linux', b'python']

6、hexists(name, key)  檢查name對應的hash是否存在當前傳入的key

dic = {'name':'daxin','age':18,'job':'Linux','Like':'python'}
r.hmset('dachenzi_hash',dic)

print(r.hexists('dachenzi_hash','name'))   # True

7、hdel(name,*keys)  刪除指定name對應的key所在的鍵值對

dic = {'name':'daxin','age':18,'job':'Linux','Like':'python'}
r.hmset('dachenzi_hash',dic)

r.hdel('dachenzi_hash','name')
print(r.hgetall('dachenzi_hash'))

8、hincrby(name, key, amount=1)  自增hash中key對應的值,不存在則創建key=amount(amount爲整數)

dic = {'name':'daxin','age':18,'job':'Linux','Like':'python'}
r.hmset('dachenzi_hash',dic)

r.hincrby('dachenzi_hash','age',amount=100)
print(r.hgetall('dachenzi_hash'))

9、hincrbyfloat(name, key, amount=1.0)  自增hash中key對應的值,不存在則創建key=amount(amount爲浮點數)

其他常用操作

1、delete(*names) 根據name刪除redis中的任意數據類型

r.set('name_str','daxin')  # str
r.sadd('name_set','daxin') # set
r.lpush('name_list','daxin')  # list
r.zadd('name_sorted','daxin',1)  # sorted set

print(r.keys())
r.delete('name_str','name_set','name_list','name_sorted')
print(r.keys())

2、exists(name)  檢測redis的name是否存在

3、keys(pattern='*')  根據* ?等通配符匹配獲取redis的name

r.set('name_str','daxin')  # str
r.sadd('name_set','daxin') # set
r.lpush('name_list','daxin')  # list
r.zadd('name_sorted','daxin',1)  # sorted set

print(r.keys('name_sor*'))    # 僅僅支持通配符

4、expire(name ,time) 爲某個name設置超時時間

5、rename(src, dst)  重命名

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

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

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

r.set('name_str','daxin')  # str
r.sadd('name_set','daxin') # set
r.lpush('name_list','daxin')  # list
r.zadd('name_sorted','daxin',1)  # sorted set

print(r.type('name_str'))
print(r.type('name_set'))
print(r.type('name_list'))
print(r.type('name_sorted'))

NoSQL之Memcached

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

安裝Memcached

在Linux下主要有兩種安裝方式,即:yum安裝,源碼編譯安裝。這裏僅使用yum進行memcached的安裝

使用root用戶,執行如下命令即可安裝完畢

yum install memcached

啓動Memcached

memcached非常輕量級,在安裝完畢後,連啓動腳本都沒有,使用命令直接進行啓動即可

memcached -d -m 10 -u root -l 0.0.0.0 -p 12000 -c 256 -P /tmp/memcached.pid
 
# 參數說明:
#     -d 是啓動一個守護進程(使用守護進程模式)
#     -m 是分配給Memcache使用的內存數量,單位是MB(默認手64M)
#     -u 是運行Memcache的用戶
#     -l 是監聽的服務器IP地址
#     -p 是設置Memcache監聽的端口,最好是1024以上的端口(一般是11211)
#     -c 選項是最大運行的併發連接數,默認是1024,按照你服務器的負載量來設定
#     -P 是設置保存Memcache的pid文件

PS:指定不同的端口啓動,即可完成多實例的部署。  

Python操作memcached

Python想要操作Memcached,首先需要安裝工具模塊

# python 2
pip install python-memcached

# python3 
pip3 install python3-memcached

基本操作:

import memcache

mc = memcache.Client(['10.0.0.3:11211'],debug=True)   # 實例化一個memcache.Client類,列表爲memcached服務器的地址及端口

mc.set('name','daxin')     #設置key

name = mc.get('name')   # 獲取key

print(name)

集羣模式

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

  IP  權重值
節點1 10.0.0.3 1
節點2 10.0.0.5 2
節點3 10.0.0.6 1

 

  那麼內存中主機列表爲:host_list = ["10.0.0.3", "10.0.0.5","10.0.0.5","10.0.0.6"]

用戶如果要在內存中創建一個鍵值對(如:k1 = "value1"),那麼要執行以下步驟:

  1. 根據算法將k1轉換成一個數字
  2. 將數字和主機列表長度求餘數,得到一個值N(0 <= N < 長度)
  3. 在主機列表中根據第二步得到的值爲索引獲取主機,例如: host_list[N]
  4. 連接將第三步中獲取的主機,將k1 = "value1" 放置在該服務器的內存中

代碼如下:

這裏使用多實例模擬多臺獨立的主機

import memcache

mc = memcache.Client([('10.0.0.3:11211',1),('10.0.0.3:11212',2),('10.0.0.3:11213',1)],debug=True)    # 配置多個節點的memcached集羣

mc.set('name','daxin')       # 和單節點添加數據相同
name = mc.get('name')

print(name)


mc1 = memcache.Client(['10.0.0.3:11212'],debug=True)   # 數據存放到了節點1上
name1 = mc1.get('name')    # 這裏連接節點2,會獲取不到數據

print(name1)

memcached操作

1、add  添加一條鍵值對,如果已經存在的key,重複執行add操作會出現異常。

import memcache

mc = memcache.Client(['10.0.0.3:11211'],debug=True)

mc.add('name','daxin')
mc.add('name','dachenzi')    # 重複添加name,那麼會警告提示

PS:MemCached: while expecting 'b'STORED'', got unexpected response 'b'NOT_STORED'' 

2、replace   修改某個key的值,如果key不存在,則get時會返回None

mc = memcache.Client(['10.0.0.3:11211'],debug=True)

mc.add('name','daxin')
print(mc.get('name')) 

mc.replace('name','dachenzi')    # 修改name的值
print(mc.get('name'))

mc.replace('name123','daxin')    # 修改一個不存在的name123的值
print(mc.get('name123'))    # 獲取會返回None

3、set 和 set_multi

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

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

import memcache
mc = memcache.Client(['10.0.0.3:11211'],debug=True)

print(mc.get('name'))
mc.set('name','daxin')      #存在就替換,不存在就新增
print(mc.get('name'))

dic = {'name':'dachenzi','age':18,'job':'Linux'}
mc.set_multi(dic)        # 單次設置多個key,存在即更新

print(mc.get('name'))
print(mc.get('age'))
print(mc.get('job'))

4、delete 和 delete_multi

delete : 在Memcached中刪除指定的一個鍵值對,不存在也不會報錯。

delete_multi : 在Memcached中刪除指定多個鍵值對。

import memcache
mc = memcache.Client(['10.0.0.3:11211'],debug=True)

mc.delete('name234234')   #隨便寫的key,不會異常
mc.delete('name2342131')

# mc.delete_multi(['key1', 'key2'])   後面指定多個key列表即可

5、get 和 get_multi

get : 獲取一個鍵值對。

get_multi : 獲取多個鍵值對。

import memcache

mc = memcache.Client(['10.0.0.3:11211'],debug=True)
mc.set('name','daxin')
mc.set('age',18)
mc.set_multi({'job':'Linux','Like':'Python'})

print(mc.get('name'))
print(mc.get_multi(['age','job','Like']))

6、append 和 prepend

append : 修改指定key的值,在該值後面追加內容。

prepend : 修改指定key的值,在該值前面插入內容。

import memcache

mc = memcache.Client(['10.0.0.3:11211'],debug=True)

print(mc.get('name'))      # 'daxin'

mc.append('name','nihao') 
print(mc.get('name'))          # 'daxinnihao'

mc.prepend('name','wojiao')
print(mc.get('name'))     # wojiaodaxinnihao

7、decr 和 incr

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

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

import memcache

mc = memcache.Client(['10.0.0.3:11211'],debug=True)
mc.set('age',1)

mc.incr('age',2)         # 自增2
print(mc.get('age'))   # 打印3
 
mc.decr('age')     # 自減1
print(mc.get('age'))    # 打印2

8、gets 和 cas

使用緩存系統共享數據資源就必然繞不開數據爭奪和髒數據(數據混亂)的問題,比如兩個業務同時操作一個key。獲取的時候value相同,同時修改的話必然會出現數據混亂。如果想要避免這種情況的發生,則可以使用  gets 和 cas。

本質上每次執行gets時,會從memcache中獲取一個自增的數字,通過cas去修改gets的值時,會攜帶之前獲取的自增和memcache中的自增值進行比較,如果相等,則可以提交,如果不相等,那麼表示在gets和cas執行之間,又有其他人執行了gets,則不允許修改。

備註:實驗沒有成功,不知道爲啥,後續補充例子。

 

posted @ 2017-11-26 18:20 Dahlhin 閱讀( ...) 評論( ...) 編輯 收藏