Python開發【第十篇】:Redis,python第十篇redis
快取資料庫介紹
NoSQL(Not Only SQL),即"不僅僅是SQL",泛指非關係型的資料庫。隨著互連網web2.0網站的興起,傳統的關聯式資料庫在應對web2.0網站,特別是超大規模和高並發的SNS類型的web2.0純動態網站已經顯得力不從心,暴露了很多難以克服的問題,而非關係型的資料庫則由於其本身的特點得到了非常迅速的發展。NoSQL資料庫的產生就是為瞭解決大規模資料集合多重資料種類帶來的挑戰,尤其是大資料應用難題。
NoSQL資料庫的四大分類
有四大NoSQL類型:KVStore for Redis(key-value store)、檔案儲存體(document store)、列導向的資料庫(Column-Oriented Database)和圖形資料庫(graph database)。每種類型都解決了傳統關聯式資料庫無法解決的問題。實際的實現往往是這些組合的組合。例如結合NoSQL類型,Orientdb是一個多模式的資料庫。Orientdb是圖形資料庫,每個節點都是一個文檔。
索引值(key-value)儲存資料庫
這一類資料庫主要會使用到一個雜湊表,這個表中有一個特定的鍵和一個指標指向特定的資料。Key/Value模型對於IT系統來說的優勢在於簡單、易部署。但是如果DBA只對部分值進行查詢或更新的時候,Key/Value就顯得效率低下了。代表資料庫Tokyo Cabinet/Tyrant、Redis、Voldemort、Oracle BDB。
列儲存資料庫
這部分資料庫通常是用來應對分布式儲存的海量資料。鍵任然存在,但是它們的特點是指向了多個列。這些列是由列家族來安排的。代表資料庫Cassandra、HBase、Riak。
文檔型資料庫
文檔型資料庫的靈感來自於Lotus Notes辦公軟體,而且它同第一種KVStore for Redis類似。該類型的資料模型是版本化的文檔,半結構化的文檔以特定的格式儲存,比如JSON。文檔型資料庫可以看作是索引值資料庫的升級版,允許之間嵌套索引值。而且文檔型資料庫比索引值資料庫的查詢效率更高。代表資料庫CouchDB、MongoDB,國內SequoiaDB。
圖形(Graph)資料庫
圖形結構的資料庫同其他行列以及剛性結構的SQL資料庫不同,它是使用靈活的圖形模型,並且能夠擴充到多台伺服器上。NoSQL資料庫沒有標準的查詢語言(SQL),因此進行資料庫查詢需要制定資料模型。許多NoSQL資料庫都有REST式的資料介面或者查詢API。代表資料庫Neo4J、InfoGrid、Infinite Graph。
因此NoSQL資料庫在以下幾種情況比較適用:1、資料模型比較簡單;2、需要靈活性更強的IT系統;3、對資料庫效能要求較高;4、不需要高度的資料一致性;5、對於給定key比較容易映射複雜值的環境。
NoSQL資料庫的四大分類對比
分類 |
代表資料庫 |
典型應用情境 |
資料模型 |
優點 |
缺點 |
索引值(key-value) |
Tokyo Cabinet/Tyrant、Redis、Voldemort、Oracle BDB |
內容緩衝,主要用於處理大量資料的高訪問負載,也用於一些日子系統。 |
key指向value的索引值對,通常用hash table來實現。 |
尋找速度快 |
資料無結構化,通常只被當作字串或者二級制資料。 |
列儲存資料庫 |
Cassandra、HBase、Riak |
分布式的檔案系統 |
以列簇式儲存,將同一列資料存在一起 |
尋找速度快,可擴充性強,更容易進行分布式擴充 |
功能相對局限性 |
文檔型資料庫 |
CouchDB、MongoDB |
web應用(與key-value類似,value是結構化的,不同的是資料庫能夠瞭解value的內容) |
key-value對應的索引值對,value為結構化資料 |
資料結構要求不嚴格,表結構可變,不需要像關聯式資料庫一樣需要預先定義表結構 |
查詢效能不高,而且缺乏統一的查詢文法 |
圖形(Graph)資料庫 |
Neo4J、InfoGrid、Infinite Graph |
社交網路、推薦系統等。專註於構建關係圖譜 |
圖結構 |
利用圖結構相關演算法。比如最短路徑定址,N度關係尋找等。 |
很多時候需要對整個圖做計算才能得出需要的資訊,而且這種結構不太好做分布式的叢集方案。 |
Redis介紹
redis是業界主流的key-value nosql資料庫之一。與Memcached類似,它支援儲存的value類型相對更多,包括string(字串)、list(鏈表)、set(集合)、zset(sorted set有序集合)和hash(雜湊類型)。這些資料類型都支援push/pop、add/remove及取交集和差集及更豐富的操作,而且這些操作都是原子性的。在此基礎上,redis支援各種不同方式的排序。與memcached一樣,為了保證效率,資料都是緩衝在記憶體中。區別是redis會周期性的把更新的資料寫入磁碟或者把修改操作寫入追加的記錄檔案,並且在此基礎上實現了master-slave(主從)同步。
Redis優點
異常快速:Redis是非常快的,每秒可以執行大約110000設定作業,81000個/秒的讀取操作。
支援豐富的資料類型:Redis支援最大多數開發人員已經知道資料類型,如列表、集合、可排序集合、雜湊等。這使得在應用中很容易解決各種問題,因為我們知道哪些問題處理使用哪些資料類型更好解決。
操作都是原子的:所有Redis的操作都是原子,從而確保當兩個客戶同時訪問Redis伺服器得到的是更新後的值(最新值)。
MultiUtility工具:Redis是一個多功能工具 + 生產力,可以在緩衝、訊息傳遞佇列中使用(Redis原生支援發布/訂閱),在應用程式中web應用程式工作階段,網站網頁點擊數等任何短暫的資料。
Redis缺點
持久化:Redis直接將資料存放區到記憶體中,要將資料儲存到磁碟上,Redis可以使用兩種方式實現持久化過程。定時快照(snapshot),每隔一段時間將整個資料庫寫到磁碟上,每次均是寫全部資料,代價非常高。基於語句追加(aof),只追蹤變化的資料,但是追加的log可能過大,同時所有的操作均重新執行一遍,回複速度慢。
佔用記憶體過高。
修改設定檔,進行重啟,將硬碟中的資料載入進記憶體,時間比較久。這個過程中redis不能提供服務。
Redis不具備自動容錯和恢複功能,主機從機的宕機都會導致前端部分讀寫請求失敗,需要等待機器重啟或者手動切換前端的IP才能恢複。
主機宕機,宕機前有部分資料未能及時同步到從機,切換IP後還會引入資料不一致的問題,降低了系統的可用性。
Redis較難支援線上擴容,在叢集容量達到上限時線上擴容會變得很複雜。為了避免這一問題,營運人員在系統上線時必須確保有足夠的空間,這對資源造成了很大的浪費。
安裝Redis環境
redis官網:https://redis.io/
下載源碼,解壓後編譯源碼
編譯完成後,進入redis-3.0.6目錄。將redis.conf中的daemonize配置為yes(以守護進程方式運行)。
進入src目錄啟動redis,指定設定檔位置,並後台運行。
查看是否啟動
運行redis用戶端redis-cli。
至這說明已經成功安裝了redis。
Redis API使用串連方式
操作模式
Redis-py提供兩個類Redis和StrictRedis用於實現Redis的命令,StrictRedis用於實現大部分官方的命令,並使用官方的文法和命令,Redis是StrictRedis的子類,用於向後相容舊版本的redis-py。
串連池
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)
在Redis中設定值,預設不存在則建立,存在則修改。
ex到期時間(秒);px到期時間(毫秒);nx如果設定為True,則只有name不存在時,當前set操作才執行;xx如果設定為True,則只有name存在時,當前set操作才執行。
setnx(name,value)
設定值,只有name不存在時,執行設定作業(添加)。
psetex(name,time_ms,value)
time_ms到期時間(毫秒)
mset(*args,**kwargs)
大量設定值mset(k1='v1',k2='v2')或mget({'k1':'v1','k2':'v2'})。
get(name)
擷取值。
mget(keys,*args)
批量擷取值,mget('name1','name2')或r.mget(['name1','name2']。
getset(name,value)
設定新增並擷取原來的值。
getrange(key,start,end)
擷取子序列(根據位元組擷取,非字元)。一個漢字3個位元組。
setrange(name,offset,value)
修改字串內容,從指定字串索引開始向後替換(新值太長時,則向後添加)。
setbit(name,offset,value)
對name對應值的二進位表示的位進行操作。offset位的索引將值變換成二進位後再進行索引,value值只能是1或0。例如setbit('name',6,1)表示將字串name用二進位表示後,將第六位設定為1。
getbit(name,offset)
擷取name對應的值的二進位表示中的某位的值。
bitcount(key,start=None,end=None)
擷取name對應的值的二進位表示中1的個數。
strlen(name)
擷取name對應值的位元組長度(一個漢字3個位元組)。
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)
在key對應的值後面追加內容value。
Hash操作
Hash表現形式上有些像python中的dict,可以儲存一組關聯性較強的資料,redis中的Hash在記憶體中的儲存格式如。
hset(name,key,value)
name對應的hash中設定一個索引值對,不存在則建立,否則修改。
hmset(name,mapping)
在name對應的hash中大量設定索引值對。
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。amount自增數(整數)。
hincrbyfloat(name,key,amount=1.0)
自增name對應的hash中的指定key的值,不存在則建立key=amount。amount自增數(浮點數)。
hscan_iter(name,match=None,count=None)
利用yield封裝hscan建立產生器,實現分批去redis中擷取資料。
List
Redis中的List在記憶體中安裝一個name對應一個List來儲存。如所示。
lpush(name,values)
在name對應的list中添加元素,每個新的元素都添加到列表的最左邊。如r.lpush('test',11,22,33),保持順序為33,22,11。rpush(name,values)表示從右向左操作。
lpushx(name,value)
在name對應的list中添加元素,只有name已經存在時,值添加到列表的最左邊。rpushx(name,value)表示從右向左操作。
llen(name)
name對應的list元素的個數。
linsert(name,where,refvalue,value)
在name對應的列表的某一個值前或後插入一個新值。where:BEFORE或AFTER;refvalue:標杆值,即在它前後插入資料;value:要插入的資料。
r.lset(name,index,value)
對name對應的list中的某一個索引位置重新賦值。index:list的索引位置。
r.lrem(name,num,value)
在name對應的list中刪除指定的值。value:要刪除的值;num>0:從表頭開始向表尾搜尋,移除與value相等的元素,刪除數量為num個;num<0:從表尾開始向表頭搜尋,移除與value相等的元素,刪除數量為num個;num=0:移除表中所有與value相等的值。
lpop(name)
在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對應列表的元素。timeout:逾時時間,當元素所有列表的元素擷取完之後,阻塞等待列表內有資料的時間(秒),0表示永遠阻塞。
brpoplpush(src,dst,timeout=0)
從一個列表的右側移除一個元素並將其添加到另一個列表的左側。
set
Redis的set是string類型的無序集合。集合成員是唯一的,這就意味著集合中不能出現重複的資料。
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對應的集合中。
有序集合,在集合的基礎上為每個元素排序,元素的排序需要根據另一個值來進行比較,所以,對於有序集合每一個元素有兩個值,即值和分數,分數用來做排序。
zadd(name,*args,**kwargs)
在name對應的有序集合中添加元素。
zcard(name)
擷取name對應的有序集合元素的數量。
zcount(name,min,max)
擷取name對應的有序集合中分數在min和max之間的個數。
zincrby(name,value,amount)
對指定成員的分數加上增量value。
r.zrange(name,start,end,desc=False,score_cast_func=float)
按照索引範圍擷取name對應的有序集合的元素。
start:有序集合索引起始位置(非分數)。
end:有序集合索引結束位置(非分數)。
desc:定序,預設按照分數從小到大排序。
withscores:是否擷取元素的分數,預設只擷取元素的值。
score_cast_func:對分數進行資料轉換的函數。
zrevrange(name,start,end,withscores=False,score_cast_func=float)
從大到小排序。
zrangebyscore(name,min,max,start=None,num=None,withscores=False,score_cast_func=float)
按照分數範圍擷取name對應的有序集合的元素。
zrevrangebyscore(name,max,min,start=None,num=None,withscores=False,score_cast_func=float)
從大到小排序。
zrank(name,value)
擷取某個值在name對應的有序集合中的排行
zrevrank(name,value)
從大到小排序。
zrem(name,values)
刪除name對應的有序集合中值是values的成員。
zremrangebyrank(name,min,max)
根據排行範圍刪除。
zremrangebyscore(name,min,max)
根據分數範圍刪除。
zscore(name,value)
擷取name對應有序集合中value對應的分數。
zinterstore(dest,keys,aggregate=None)
擷取兩個有序集合的交集,如果遇到相同值不同分數則按照aggregate進行操作,aggregate的值為SUM、MIN、MAX。
其他動作
delete(*names)
根據name刪除redis中的任意資料類型。
exists(name)
檢測redis的name是否存在。
keys(pattern='*')
根據key匹配擷取redis的name。
expire(name,time)
為某個redis的某個name設定逾時時間。
rename(src,dst)
對redis的name重新命名。
move(name,db)
將redis的某個值移動到指定的db下。
randomkey()
隨機擷取一個redis的name(不刪除)。
type(name)
擷取name對應值的類型。
管道
redis-py預設在執行每次請求都會建立(串連池申請串連)和斷開(歸還串連池)一次串連操作。如果想要在一次請求中指定多個命令,則可以使用pipline實現一次請求指定多個命令,並且預設情況下一次pipline是原子性操作。(原子性操作是指不會被線程調度機制打斷的操作,這種操作一旦開始就一直運行到結束,中間不會有任何context switch(切換到另一個線程))
發布訂閱
Redis發布訂閱(pub/sub)是一種訊息通訊模式,寄件者(pub)發送訊息,訂閱者(sub)接收訊息。Redis用戶端可以訂閱任意數量的頻道。展示了頻道channel1,以及訂閱這個頻道的三個用戶端client1、client2和client5之間的關係。
當有新訊息通過publish命令發送給頻道channel1時,這個訊息就會被發送給訂閱它的三個用戶端。
RedisHelper_basic
發行者
訂閱者
結果