介紹
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安裝
Linux下安裝:
安裝完后
$ wget http://download.redis.io/releases/redis-2.8.17.tar.gz $ tar xzf redis-2.8.17.tar.gz $ cd redis-2.8.17 $ make
安裝完后將配置文件redis.conf拷貝到我們自己的文件夾下,myconf/redis.conf,目的是為了后台運行:
mkdir myconf cp redis.conf myconf/redis.conf
然后修改配置文件中的daemonize為yes,這樣當我們開啟redis時指定我們剛剛拷貝的配置文件,就能在后台運行了:
daemonize yes
然后我們就可以啟動redis了,指定配置文件:
cd src $ ./redis-server ../myconf/redis.conf
如果需要外部連接redis的話就需要設置配置文件,或者直接啟動的時候帶參數 --protected-mode no來取消保護模式:
bind 0.0.0.0 # 允許任何IP訪問 protected-mode no # 取消保護模式,或者設置密碼訪問 requirepass 123456 # 設置密碼登錄
啟動后,就可以使用測試客戶端程序redis-cli和redis服務交互了。如:
$ cd src $ ./redis-cli redis> set foo bar OK redis> get foo "bar"
Ubuntu下安裝
在 Ubuntu 系統安裝 Redis 可以使用以下命令: $sudo apt-get update $sudo apt-get install redis-server 啟動 Redis $ redis-server 查看 redis 是否啟動? $ redis-cli 以上命令將打開以下終端: redis 127.0.0.1:6379> 127.0.0.1 是本機 IP ,6379 是 redis 服務端口。現在我們輸入 PING 命令。 redis 127.0.0.1:6379> ping PONG
Redis API使用
redis基本命令
String set setex psetex mset mget getset getrange setrange setbit getbit bitcount bittop strlen incr incrfloat decr append Hash hset hmset hmget hgetall hlen hkeys hvals hexists hdel hincrby hincrbyfloat hscan hscan_iter List lpush rpush lpushx rpushx linsert lset lrem lpop ltrim lindex rpoplpush brpoplpush blpop 自定義增量迭代 有序集合sort set操作 zadd zcard zrange zcount zincraby zrank zrem zremrangebyrank zscore 其他常用操作 delete exists keys expire rname randomkey type scan scan_iter 管道(pipeline)
String類型
set
set(name, value, ex=None, px=None, nx=False, xx=False) 在Redis中設置值,默認,不存在則創建,存在則修改 參數說明: ex,過期時間(秒) px,過期時間(毫秒) nx,如果設置為True,則只有name不存在時,當前set操作才執行 xx,如果設置為True,則只有name存在時,當前set操作才執行 1.ex,過期時間(秒) 這里過期時間是3秒,3秒后p,鍵food的值就變成None import redis pool = redis.ConnectionPool(host='127.0.0.1', port=6379, decode_responses=True) r = redis.Redis(connection_pool=pool) r.set('food', 'mutton', ex=3) # key是"food" value是"mutton" 將鍵值對存入redis緩存 print(r.get('food')) # mutton 取出鍵food對應的值 2.px,過期時間(豪秒) 這里過期時間是3豪秒,3毫秒后,鍵foo的值就變成None import redis pool = redis.ConnectionPool(host='127.0.0.1', port=6379, decode_responses=True) r = redis.Redis(connection_pool=pool) r.set('food', 'beef', px=3) print(r.get('food')) 3.nx,如果設置為True,則只有name不存在時,當前set操作才執行 (新建) import redis pool = redis.ConnectionPool(host='127.0.0.1', port=6379, decode_responses=True) r = redis.Redis(connection_pool=pool) print(r.set('fruit', 'watermelon', nx=True)) # True--不存在 # 如果鍵fruit不存在,那么輸出是True;如果鍵fruit已經存在,輸出是None 4.xx,如果設置為True,則只有name存在時,當前set操作才執行 (修改) print((r.set('fruit', 'watermelon', xx=True))) # True--已經存在 # 如果鍵fruit已經存在,那么輸出是True;如果鍵fruit不存在,輸出是None 5.setnx(name, value) 設置值,只有name不存在時,執行設置操作(添加) print(r.setnx('fruit1', 'banana')) # fruit1不存在,輸出為True
setex
setex(name, value, time) 設置值 參數: time,過期時間(數字秒 或 timedelta對象) import redis import time pool = redis.ConnectionPool(host='127.0.0.1', port=6379, decode_responses=True) r = redis.Redis(connection_pool=pool) r.setex("fruit2", "orange", 5) time.sleep(5) print(r.get('fruit2')) # 5秒后,取值就從orange變成None
psetex
psetex(name, time_ms, value) 設置值 參數: time_ms,過期時間(數字毫秒 或 timedelta對象) r.psetex("fruit3", 5000, "apple") time.sleep(5) print(r.get('fruit3')) # 5000毫秒后,取值就從apple變成None
mset(*args, **kwargs)
批量設置值 如: r.mget({'k1': 'v1', 'k2': 'v2'}) r.mset(k1="v1", k2="v2") # 這里k1 和k2 不能帶引號 一次設置對個鍵值對 print(r.mget("k1", "k2")) # 一次取出多個鍵對應的值 print(r.mget("k1"))
get(name)
獲取值,如: get age
mget(keys, *args)
mget(keys, *args) 批量獲取 如: print(r.mget('k1', 'k2')) print(r.mget(['k1', 'k2'])) print(r.mget("fruit", "fruit1", "fruit2", "k1", "k2")) # 將目前redis緩存中的鍵對應的值批量取出來
getset(name, value)
getset(name, value) 設置新值並獲取原來的值 print(r.getset("food", "barbecue")) # 設置的新值是barbecue 設置前的值是beef1
getrange(key, start, end)
getrange(key, start, end) 獲取子序列(根據字節獲取,非字符) 參數: name,Redis 的 name start,起始位置(字節) end,結束位置(字節) 如: “君惜大大” ,0-3表示 “君” r.set("cn_name", "君惜大大") # 漢字 print(r.getrange("cn_name", 0, 2)) # 取索引號是0-2 前3位的字節 君 切片操作 (一個漢字3個字節 1個字母一個字節 每個字節8bit) print(r.getrange("cn_name", 0, -1)) # 取所有的字節 君惜大大 切片操作 r.set("en_name","junxi") # 字母 print(r.getrange("en_name", 0, 2)) # 取索引號是0-2 前3位的字節 jun 切片操作 (一個漢字3個字節 1個字母一個字節 每個字節8bit) print(r.getrange("en_name", 0, -1)) # 取所有的字節 junxi 切片操作
setbit(name, offset, value)
setbit(name, offset, value) 對name對應值的二進制表示的位進行操作 參數: name,redis的name offset,位的索引(將值變換成二進制后再進行索引) value,值只能是 1 或 0 注:如果在Redis中有一個對應: n1 = "foo", 那么字符串foo的二進制表示為:01100110 01101111 01101111 所以,如果執行 setbit('n1', 7, 1),則就會將第7位設置為1, 那么最終二進制則變成 01100111 01101111 01101111,即:"goo" 擴展,轉換二進制表示: source = "張三" source = "foo" for i in source: num = ord(i) print bin(num).replace('b','') 特別的,如果source是漢字 "張三"怎么辦? 答:對於utf-8,每一個漢字占 3 個字節,那么 "張三" 則有 6個字節 對於漢字,for循環時候會按照 字節 迭代,那么在迭代時,將每一個字節轉換 十進制數,然后再將十進制數轉換成二進制
getbit(name, offset)
getbit(name, offset) 獲取name對應的值的二進制表示中的某位的值 (0或1) print(r.getbit("foo1", 0)) # 0 foo1 對應的二進制 4個字節 32位 第0位是0還是1
bitcount(key, start=None, end=None)
bitcount(key, start=None, end=None) 獲取name對應的值的二進制表示中 1 的個數 參數: key,Redis的name start 字節起始位置 end,字節結束位置 print(r.get("foo")) # goo1 01100111 print(r.bitcount("foo",0,1)) # 11 表示前2個字節中,1出現的個數
strlen(name)
strlen(name) 返回name對應值的字節長度(一個漢字3個字節) print(r.strlen("foo")) # 4 'goo1'的長度是4
incr(self, name, amount=1)
incr(self, name, amount=1) 自增 name對應的值,當name不存在時,則創建name=amount,否則,則自增。 參數: name,Redis的name amount,自增數(必須是整數) 注:同incrby r.set("foo", 123) print(r.mget("foo", "foo1", "foo2", "k1", "k2")) r.incr("foo", amount=1) print(r.mget("foo", "foo1", "foo2", "k1", "k2")) 應用場景 – 頁面點擊數 假定我們對一系列頁面需要記錄點擊次數。例如論壇的每個帖子都要記錄點擊次數,而點擊次數比回帖的次數的多得多。如果使用關系數據庫來存儲點擊,可能存在大量的行級鎖爭用。所以, 點擊數的增加使用redis的INCR命令最好不過了。 當redis服務器啟動時,可以從關系數據庫讀入點擊數的初始值(12306這個頁面被訪問了34634次) r.set("visit:12306:totals", 34634) print(r.get("visit:12306:totals")) 每當有一個頁面點擊,則使用INCR增加點擊數即可。 r.incr("visit:12306:totals") r.incr("visit:12306:totals") 頁面載入的時候則可直接獲取這個值 print(r.get("visit:12306:totals"))
incrbyfloat(self, name, amount=1.0)
incrbyfloat(self, name, amount=1.0) 自增 name對應的值,當name不存在時,則創建name=amount,否則,則自增。 參數: name,Redis的name amount,自增數(浮點型) r.set("foo1", "123.0") r.set("foo2", "221.0") print(r.mget("foo1", "foo2")) r.incrbyfloat("foo1", amount=2.0) r.incrbyfloat("foo2", amount=3.0) print(r.mget("foo1", "foo2"))
decr(self, name, amount=1)
decr(self, name, amount=1) 自減 name對應的值,當name不存在時,則創建name=amount,否則,則自減。 參數: name,Redis的name amount,自減數(整數) r.decr("foo4", amount=3) # 遞減3 r.decr("foo1", amount=1) # 遞減1 print(r.mget("foo1", "foo4"))
append(key, value)
append(key, value) 在redis name對應的值后面追加內容 參數: key, redis的name value, 要追加的字符串 r.append("name", "haha") # 在name對應的值junxi后面追加字符串haha print(r.mget("name"))
集合set操作
sadd
新增 sadd(name,values) name對應的集合中添加元素 r.sadd("set1", 33, 44, 55, 66) # 往集合中添加元素 print(r.scard("set1")) # 集合的長度是4 print(r.smembers("set1")) # 獲取集合中所有的成員
scard
獲取元素個數 類似於len scard(name) 獲取name對應的集合中元素個數 print(r.scard("set1")) # 集合的長度是4
smembers
獲取集合中所有的成員 smembers(name) 獲取name對應的集合的所有成員 print(r.smembers("set1")) # 獲取集合中所有的成員 獲取集合中所有的成員–元組形式 sscan(name, cursor=0, match=None, count=None) print(r.sscan("set1")) 1 獲取集合中所有的成員–迭代器的方式 sscan_iter(name, match=None, count=None) 同字符串的操作,用於增量迭代分批獲取元素,避免內存消耗太大 for i in r.sscan_iter("set1"): print(i)2
sdiff
差集 sdiff(keys, *args) 在第一個name對應的集合中且不在其他name對應的集合的元素集合 r.sadd("set2", 11, 22, 33) print(r.smembers("set1")) # 獲取集合中所有的成員 print(r.smembers("set2")) print(r.sdiff("set1", "set2")) # 在集合set1但是不在集合set2中 print(r.sdiff("set2", "set1")) # 在集合set2但是不在集合set1中
sdiffstore
差集–差集存在一個新的集合中 sdiffstore(dest, keys, *args) 獲取第一個name對應的集合中且不在其他name對應的集合,再將其新加入到dest對應的集合中 r.sdiffstore("set3", "set1", "set2") # 在集合set1但是不在集合set2中 print(r.smembers("set3")) # 獲取集合3中所有的成員
sinter
交集 sinter(keys, *args) 獲取多一個name對應集合的交集 print(r.sinter("set1", "set2")) # 取2個集合的交集
sinterstore
交集–交集存在一個新的集合中 sinterstore(dest, keys, *args) 獲取多一個name對應集合的並集,再將其加入到dest對應的集合中 print(r.sinterstore("set3", "set1", "set2")) # 取2個集合的交集 print(r.smembers("set3"))
sunion
並集 sunion(keys, *args) 獲取多個name對應的集合的並集 print(r.sunion("set1", "set2")) # 取2個集合的並集1 並集–並集存在一個新的集合 sunionstore(dest,keys, *args) 獲取多一個name對應的集合的並集,並將結果保存到dest對應的集合中 print(r.sunionstore("set3", "set1", "set2")) # 取2個集合的並集 print(r.smembers("set3"))
sismember
判斷是否是集合的成員 類似in sismember(name, value) 檢查value是否是name對應的集合的成員,結果為True和False print(r.sismember("set1", 33)) # 33是集合的成員 print(r.sismember("set1", 23)) # 23不是集合的成員
smove
移動 smove(src, dst, value) 將某個成員從一個集合中移動到另外一個集合 r.smove("set1", "set2", 44) print(r.smembers("set1")) print(r.smembers("set2"))
spop
刪除–隨機刪除並且返回被刪除值 spop(name) 從集合移除一個成員,並將其返回,說明一下,集合是無序的,所有是隨機刪除的 print(r.spop("set2")) # 這個刪除的值是隨機刪除的,集合是無序的 print(r.smembers("set2")) 11.刪除–指定值刪除 srem(name, values) 在name對應的集合中刪除某些值 print(r.srem("set2", 11)) # 從集合中刪除指定值 11 print(r.smembers("set2"))
有序集合sort set操作
有序集合,在集合的基礎上,為每元素排序;
元素的排序需要根據另外一個值來進行比較,所以,對於有序集合,每一個元素有兩個值,即:值和分數,分數專門用來做排序。
zadd
新增 zadd(name, args, *kwargs) 在name對應的有序集合中添加元素 如: import redis import time pool = redis.ConnectionPool(host='127.0.0.1', port=6379, decode_responses=True) r = redis.Redis(connection_pool=pool) r.zadd("zset1", n1=11, n2=22) r.zadd("zset2", 'm1', 22, 'm2', 44) print(r.zcard("zset1")) # 集合長度 print(r.zcard("zset2")) # 集合長度 print(r.zrange("zset1", 0, -1)) # 獲取有序集合中所有元素 print(r.zrange("zset2", 0, -1, withscores=True)) # 獲取有序集合中所有元素和分數2
zcard
獲取有序集合元素個數 類似於len zcard(name) 獲取name對應的有序集合元素的數量 print(r.zcard("zset1")) # 集合長度1
zrange
獲取有序集合的所有元素 r.zrange( name, start, end, desc=False, withscores=False, score_cast_func=float) 按照索引范圍獲取name對應的有序集合的元素 參數: name,redis的name start,有序集合索引起始位置(非分數) end,有序集合索引結束位置(非分數) desc,排序規則,默認按照分數從小到大排序 withscores,是否獲取元素的分數,默認只獲取元素的值 score_cast_func,對分數進行數據轉換的函數 3-1 從大到小排序(同zrange,集合是從大到小排序的) zrevrange(name, start, end, withscores=False, score_cast_func=float) print(r.zrevrange("zset1", 0, -1)) # 只獲取元素,不顯示分數 print(r.zrevrange("zset1", 0, -1, withscores=True)) # 獲取有序集合中所有元素和分數,分數倒序 3-2 按照分數范圍獲取name對應的有序集合的元素 zrangebyscore(name, min, max, start=None, num=None, withscores=False, score_cast_func=float) for i in range(1, 30): element = 'n' + str(i) r.zadd("zset3", element, i) print(r.zrangebyscore("zset3", 15, 25)) # # 在分數是15-25之間,取出符合條件的元素 print(r.zrangebyscore("zset3", 12, 22, withscores=True)) # 在分數是12-22之間,取出符合條件的元素(帶分數) 3-3 按照分數范圍獲取有序集合的元素並排序(默認從大到小排序) zrevrangebyscore(name, max, min, start=None, num=None, withscores=False, score_cast_func=float) print(r.zrevrangebyscore("zset3", 22, 11, withscores=True)) # 在分數是22-11之間,取出符合條件的元素 按照分數倒序1 3-4 獲取所有元素–默認按照分數順序排序 zscan(name, cursor=0, match=None, count=None, score_cast_func=float) print(r.zscan("zset3"))1 3-5 獲取所有元素–迭代器 zscan_iter(name, match=None, count=None,score_cast_func=float) for i in r.zscan_iter("zset3"): # 遍歷迭代器 print(i)
zcount
zcount(name, min, max) 獲取name對應的有序集合中分數 在 [min,max] 之間的個數 print(r.zrange("zset3", 0, -1, withscores=True)) print(r.zcount("zset3", 11, 22))
zincrby
自增 zincrby(name, value, amount) 自增name對應的有序集合的 name 對應的分數 r.zincrby("zset3", "n2", amount=2) # 每次將n2的分數自增2 print(r.zrange("zset3", 0, -1, withscores=True))
zrank
獲取值的索引號 zrank(name, value) 獲取某個值在 name對應的有序集合中的索引(從 0 開始) 更多: zrevrank(name, value),從大到小排序 print(r.zrank("zset3", "n1")) # n1的索引號是0 這里按照分數順序(從小到大) print(r.zrank("zset3", "n6")) # n6的索引號是1 print(r.zrevrank("zset3", "n1")) # n1的索引號是29 這里安照分數倒序(從大到小)
zrem
刪除–指定值刪除 zrem(name, values) 刪除name對應的有序集合中值是values的成員 r.zrem("zset3", "n3") # 刪除有序集合中的元素n3 刪除單個 print(r.zrange("zset3", 0, -1))
zremrangebyrank
刪除–根據排行范圍刪除,按照索引號來刪除 zremrangebyrank(name, min, max) 根據排行范圍刪除 r.zremrangebyrank("zset3", 0, 1) # 刪除有序集合中的索引號是0, 1的元素 print(r.zrange("zset3", 0, -1)) zremrangebyscore(name, min, max) 根據分數范圍刪除 r.zremrangebyscore("zset3", 11, 22) # 刪除有序集合中的分數是11-22的元素 print(r.zrange("zset3", 0, -1))
zscore
獲取值對應的分數 zscore(name, value) 獲取name對應有序集合中 value 對應的分數 print(r.zscore("zset3", "n27")) # 獲取元素n27對應的分數271
其他常用操作
del
delete (*names) 根據刪除redis中的任意數據類型(string、hash、list、set、有序set) r.delete("gender") # 刪除key為gender的鍵值對1
exists
檢查名字是否存在 exists(name) 檢測redis的name是否存在,存在就是True,False 不存在 print(r.exists("zset1"))1
keys
模糊匹配 keys(pattern=’‘) 根據模型獲取redis的name 更多: KEYS 匹配數據庫中所有 key 。 KEYS h?llo 匹配 hello , hallo 和 hxllo 等。 KEYS h*llo 匹配 hllo 和 heeeeello 等。 KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo print(r.keys("foo*")) 1
expire
設置超時時間 expire(name ,time) 為某個redis的某個name設置超時時間 r.lpush("list5", 11, 22) r.expire("list5", time=3) print(r.lrange("list5", 0, -1)) time.sleep(3) print(r.lrange("list5", 0, -1))
rname
重命名 rename(src, dst) 對redis的name重命名 r.lpush("list5", 11, 22) r.rename("list5", "list5-1")
randomkey
隨機獲取name randomkey() 隨機獲取一個redis的name(不刪除) print(r.randomkey()) 1
type
獲取類型 type(name) 獲取name對應值的類型 print(r.type("set1")) print(r.type("hash2"))
scan
查看所有元素 scan(cursor=0, match=None, count=None) print(r.hscan("hash2")) print(r.sscan("set3")) print(r.zscan("zset2")) print(r.getrange("foo1", 0, -1)) print(r.lrange("list2", 0, -1)) print(r.smembers("set3")) print(r.zrange("zset3", 0, -1)) print(r.hgetall("hash1"))
scan_iter
查看所有元素–迭代器 scan_iter(match=None, count=None) for i in r.hscan_iter("hash1"): print(i) for i in r.sscan_iter("set3"): print(i) for i in r.zscan_iter("zset3"): print(i)8 other 方法 print(r.get('name')) # 查詢key為name的值 r.delete("gender") # 刪除key為gender的鍵值對 print(r.keys()) # 查詢所有的Key print(r.dbsize()) # 當前redis包含多少條數據 r.save() # 執行"檢查點"操作,將數據寫回磁盤。保存時阻塞 # r.flushdb() # 清空r中的所有數據
管道(pipeline)
redis-py默認在執行每次請求都會創建(連接池申請連接)和斷開(歸還連接池)一次連接操作,如果想要在一次請求中指定多個命令,則可以使用pipline實現一次請求指定多個命令,並且默認情況下一次pipline 是原子性操作。
#!/usr/bin/env python # -*- coding:utf-8 -*- 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.set('name', 'alex') pipe.set('role', 'sb') pipe.execute()
================================
管道的命令可以寫在一起,如: pipe.set('hello', 'redis').sadd('faz', 'baz').incr('num').execute() print(r.get("name")) print(r.get("role")) print(r.get("num"))