python操作redis

非關係型數據庫和關係型數據庫的差別:

非關係型數據庫的優勢:

1. 性能NOSQL是基於鍵值對的,可以想象成表中的主鍵和值的對應關係,而且不需要經過SQL層的解析,所以性能非常高。

2. 可擴展性同樣也是因爲基於鍵值對,數據之間沒有耦合性,所以非常容易水平擴展。

關係型數據庫的優勢:

1. 複雜查詢可以用SQL語句方便的在一個表以及多個表之間做非常複雜的數據查詢。

2. 事務支持使得對於安全性能很高的數據訪問要求得以實現。對於這兩類數據庫,對方的優勢就是自己的弱勢,反之亦然。但是近年來這兩種數據庫都在向着另外一個方向進化。

例如:NOSQL數據庫慢慢開始具備SQL數據庫的一些複雜查詢功能的雛形,比如Couchbase的index以及MONGO的複雜查詢。對於事務的支持也可以用一些系統級的原子操作來實現例如樂觀鎖之類的方法來曲線救國。SQL數據庫也開始慢慢進化,比如HandlerSocker技術的實現,可以在MYSQL上實現對於SQL層的穿透,用NOSQL的方式訪問數據庫,性能可以上可以達到甚至超越NOSQL數據庫。可擴展性上例如Percona Server,可以實現無中心化的集羣。雖然這兩極都因爲各自的弱勢而開始進化出另一極的一些特性,但是這些特性的增加也會消弱其本來具備的優勢,比如Couchbase上的index的增加會逐步降低數據庫的讀寫性能。所以怎樣構建系統的短期和長期存儲策略,用好他們各自的強項是架構師需要好好考慮的重要問題。

python操作redis

redis的概念:

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

Redis 是一個高性能的key-value數據庫。 redis的出現,很大程度補償了memcached這類key/value存儲的不足,在部 分場合可以對關係數據庫起到很好的補充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客戶端,使用很方便。

Redis支持主從同步。數據可以從主服務器向任意數量的從服務器上同步,從服務器可以是關聯其他從服務器的主服務器。這使得Redis可執行單層樹複製。存盤可以有意無意的對數據進行寫操作。由於完全實現了發佈/訂閱機制,使得從數據庫在任何地方同步樹時,可訂閱一個頻道並接收主服務器完整的消息發佈記錄。同步對讀取操作的可擴展性和數據冗餘很有幫助。

Redis的安裝

Redis一般都是安裝在linux系統中,具體安裝步驟如下:

#cd /usr/local/src

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

#tar xzf redis-3.0.1.tar.gz 

#cd redis-3.0.1 

#make

#src/redis-server &

檢查redis是否正常啓動

Ps –ef |grep redis

Netstat –lnp |grep 6379

python安裝redis的模塊

Pip install redis

redis是以key-value的形式存儲的,所以我們在操作的時候。首先我們將redis所在主機的ip和發佈端口作爲參數實例化了一個對象r,然後執行set('hello','world'),這樣我們就在內存中存儲了一個key爲hello,值爲‘world’的項。我們可以理解爲{'hello':'world'},當我們要讀取的之後,keys()就是獲得多有key值。

例子:

import redis

r = redis.Redis(host="192.168.1.1", port=6379)

r.set("hello", "world")

print(r.get("hello"))

print(r.keys())

# print(dir(r))

2.連接池

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

例子:

import redis

pool = redis.ConnectionPool(host="192.168.1.1")

r = redis.Redis(connection_pool=pool)

r.set("python", "study")

print(r.get("python"))

管道

redis-py默認在執行每次請求都會創建(連接池申請連接)和斷開(歸還連接池)一次連接操作,如果想要在一次請求中指定多個命令,則可以使用pipline實現一次請求指定多個命令,並且默認情況下一次pipline 是原子性操作。減少功耗

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

Client: INCR X

Server: 1

Client: INCR X

Server: 2

Client: INCR X

Server: 3

Client: INCR X

Server: 4

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

Client: INCR X

Client: INCR X

Client: INCR X

Client: INCR X

Server: 1

Server: 2

Server: 3

Server: 4

假設不會因爲tcp報文過長而被拆分。可能兩個tcp報文就能完成四條命令,client可以將四個命令放到一個tcp報文一起發送,server則可以將四條命令的處理結果放到一個tcp報文返回。通過pipeline方式當有大批量的操作時候。我們可以節省很多原來浪費在網絡延遲的時間。需要注意到是用 pipeline方式打包命令發送,redis必須在處理完所有命令前先緩存起所有命令的處理結果。打包的命令越多,緩存消耗內存也越多。所以並是不是打包的命令越多越好。具體多少合適需要根據具體情況測試

例子:

import datetime

import redis

def withpipe(r):

    pipe = r.pipeline(transaction=True)

    for i in xrange(1, 1000):

        key = "test1" +str(i)

        value = "test1" + str(i)

        pipe.set(key, value)

    pipe.execute()

def withoutpipe(r):

    # pipe = r.pipeline(transaction=True)

    for i in xrange(1, 1000):

        key = "test1" + str(i)

        value = "test1" + str(i)

        r.set(key, value)

if __name__ == "__main__":

    pool = redis.ConnectionPool(host="192.168.1.1", port=6379, db=0)

    r1 = redis.Redis(connection_pool=pool)

    r2 = redis.Redis(connection_pool=pool)

    start = datetime.datetime.now()

    # print(start)

    withpipe(r1)

    end = datetime.datetime.now()

    # print((end-start).microseconds)

    # print(end-start)

    t_time = (end - start).microseconds

    print("withpipe time is : {0}".format(t_time))

    start = datetime.datetime.now()

    withoutpipe(r2)

    end = datetime.datetime.now()

    t_time = (end - start).microseconds

    print("withoutpipe time is : {0}".format(t_time))

結果:

withpipe time is : 28000

withoutpipe time is : 253000



string操作

redis中的string在內存中都是按照一個key對應一個value來存儲的。如:


r.set(“name”, “hahaxiangxiang”)

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

#ex,過期時間(秒)

#px,過期時間(毫秒)

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

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


get(name)獲取redis中的key值

print(r.get("name"))


mset()批量設置值

r.mset(name1="shang", name2="haha")

r.mset({"name3": "kong", "name4": "gu"})


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

print(r.mget("name1", "name2", "name3", "name4"))

append(name, value)

r.append("name","lisi")

print(r.get("name"))



list操作

redis中的List在在內存中按照一個name對應一個List來存儲

#在name對應的list中添加元素,每個新的元素都添加到列表的最左邊

lpush(name,values)

r.lpush("list_name",2)

#保存在列表中的順序爲5,4,3,2

r.lpush("list_name",3,4,5)

#同lpush,但每個新的元素都添加到列表的最右邊

rpush(name,values)

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

lpushx(name,value)

#在name對應的list中添加元素,只有name已經存在時,值添加到列表的最右邊

rpushx(name,value)

#name對應的list元素的個數

llen(name)

print(r.llen("list_name"))


#參數:name,redis的name;where,BEFORE或AFTER;refvalue,標杆值,即:在它前後插入數據;value,要插入的數據

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

#參數:name,redis的name;index,list的索引位置;value,要設置的值

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

# 參數:name,redis的name;value,要刪除的值;num:num=0,刪除列表中所有的指定值;num=2,從前到後,刪除2個;num=-2,從後向前,刪除2個

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

#擴展: rpop(name) 表示從右向左操作

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


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

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

例子:

import redis


pool = redis.ConnectionPool(host="192.168.1.1", port=6379, db=0)

r = redis.Redis(connection_pool=pool)

# lpush  在list的左邊增加一個元素           left

# rpush  在list的右邊增加一個元素           right

r.lpush("list1", "test1")

r.rpush("list1", "haha")

r.lpush("list1", "test2")

r.lpush("list1", "test3")

r.lpush("list1", "test2")

r.lpush("list1", 2, 3, 4)

r.lpush("list1", "test2")

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


# 最終的list結果是    [4, 3, 2, "test3", "test2", "test1", "haha"]


# 在中間位置增加一個元素

# linsert(name,where,refvalue,value)

# name   代表的是list對應的key值

# where     AFTER   BEFORE

# refvalue    list中的某個元素

# value      你要增加的值是什麼

r.linsert("list1", "AFTER",  "test2", "hello")

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

r.lset("list1", 2, "world")

# lset  更改某個元素的值

# r.lset(name,index,value)

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

print(r.lindex("list1", 2))

# lindex   查看list某個下標的值

print(r.lpop("list1"))

# lpop從list的最左邊刪除一個元素,返回刪除元素的值

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

r.lrem("list1", "world")

# r.lrem(name,value,num):

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

結果:

['test2', '4', '3', '2', 'test2', 'test3', 'test2', 'test1', 'haha']

['test2', 'hello', '4', '3', '2', 'test2', 'test3', 'test2', 'test1', 'haha']

['test2', 'hello', 'world', '3', '2', 'test2', 'test3', 'test2', 'test1', 'haha']

world

test2

['hello', 'world', '3', '2', 'test2', 'test3', 'test2', 'test1', 'haha']

['hello', '3', '2', 'test2', 'test3', 'test2', 'test1', 'haha']



set操作

set集合就是不重複的列表


sadd(name,values)

#給name對應的集合中添加元素


r.sadd("set_name","aa")

r.sadd("set_name","aa","bb")

smembers(name)

#獲取name對應的集合的所有成員

scard(name)

#獲取name對應的集合中的元素個數

r.scard("set_name")

sinter(keys, *args)

# 獲取多個name對應集合的並集

r.sadd("set_name","aa","bb")

r.sadd("set_name1","bb","cc")

r.sadd("set_name2","bb","cc","dd")


print(r.sinter("set_name","set_name1","set_name2"))

#輸出:{bb}

sismember(name, value)

#檢查value是否是name對應的集合內的元素

spop(name)

#從集合的右側移除一個元素,並將其返回

sunion(keys, *args)

#獲取多個name對應的集合的並集

r.sunion("set_name","set_name1","set_name2")

srem(name, value)  刪除集合中的某個元素

r.srem("set_name", "aa")


Hash類型操作

Redis在內存中存儲hash類型是以name對應一個字典形式存儲的

hset(name,key,value)

    name:redis的name

    key:key1對應的hash中的key

    value:value1對應的hash中的value

name對應的hash中設置一個鍵值對(不存在,則創建;否則,修改),當name對應的hash中不存在當前key則創建(相當於添加)

hget(name,key)

在name對應的hash中獲取根據key獲取value

hmset(name,mapping)

    name:redis的name

    mapping:字典,如{'k1':'v1','k2','v2'}

在name對應的hash中批量設置鍵值對

例子:

import redis

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

r = redis.Redis(connection_pool=pool)

r.hmset('test',{'k1':'v1','k2':'v2'})

print(r.hmget('test','k1','k2'))

輸出:

[b'v1', b'v2']

hmget(name,keys,*args)

    name:redis對應的name

    keys:要獲取key的集合,如:['k1','k2','k3']

    *args:要獲取的key,如:k1,k2,k3

在name對應的hash中獲取多個key的值

例子:

import redis

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

r = redis.Redis(connection_pool=pool)

r.hmset('test',{'k1':'v1','k2':'v2'})

#獲取多個值

print(r.hmget('test','k1','k2'))

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

例子:

import redis

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

r = redis.Redis(connection_pool=pool)

r.hmset('test',{'k1':'v1','k2':'v2'})

print(r.hgetall('test'))

輸出:

{b'k2': b'v2', b'k1': b'v1'}

hlen(name):獲取name對應的hash中鍵值的個數

例子:

import redis

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

r = redis.Redis(connection_pool=pool)

r.hmset('test',{'k1':'v1','k2':'v2'})

print(r.hlen('test'))

輸出:

2

hkeys(name):獲取name對應的hash中所有的key的值

hvals(name):獲取name對應的hash中所有的value的值

例子:

import redis

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

r = redis.Redis(connection_pool=pool)

r.hmset('test',{'k1':'v1','k2':'v2'})

print(r.hkeys('test'))

print(r.hvals('test'))

輸出:

[b'k2', b'k1']

[b'v2', b'v1']

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

hdel(name,*keys):將name對應的hash中指定key的鍵值對刪除

例子:

import redis

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

r = redis.Redis(connection_pool=pool)

r.hmset('test',{'k1':'v1','k2':'v2'})

print(r.exists('test'))

print(r.hdel('test','k1'))

print(r.hmget('test','k1','k2'))

 

輸出:

True

1

其他常用操作

刪除某個hash值

delete(*names)

根據name刪除redis中的任意數據類型

exists(name)

檢測redis的name是否存在

keys(pattern='*')

根據* ?等通配符匹配獲取redis的name

expire(name ,time)

爲某個name設置超時時間

rename(src, dst)

將redis的某個值移動到指定的db下

move 0

獲取name對應值的類型

type(name)


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