redis簡介
Redis 是一個高性能的key-value數據庫。
- Redis可基於內存亦可持久化。
- Redis 支持存儲的value類型豐富,包括string(字符串)、list(鏈表)、set(集合)、zset(sorted set --有序集合)和hash(哈希類型)
- Redis性能極高,讀的速度可高達110000次/s,寫的速度可高達81000次/s 。
- Redis的所有操作都是原子性的,Redis還支持幾個操作合並后的原子性執行。
Redis 下載安裝
Redis官方並不支持Windows。 但是,微軟針對Win64自己開發了一個windows版的redis並共享到github上。
點擊https://github.com/MSOpenTech/redis/releases下載。
選擇你喜歡的安裝方式,這里我選擇壓縮版。
安裝/解壓縮后到redis目錄找到redis-server.exe和redis-cli.exe
雙擊redis-server.exe啟動redis服務
雙擊redis-cli.exe啟動客戶端,用來訪問redis服務。
測試一下,設置x的值為1並獲取x。
使用redis
redis使用key-value來存儲數據。
set & get
使用set命令設置值,並用get命令獲取值。
> set name "tenny"
OK
> get name
"tenny"
場景舉例
一般,我們存user是用id做key,user實體(或json格式化)做value。set 1 user1
但是,如果對user中某個字段經常要修改,可以采取另一種格式存儲user信息
- 使用mset命令批量設置屬性,假設id為1的用戶名為zhangsan,其余額為1688。
mset user:1:name zhangsan user:1:balance 1688
- 如果該用戶頻繁購物,余額經常變動,我們修改其余額很簡單,直接設置其余額值即可。
set user:1:banlance 1000
- 而如果是之前放入整個user實體作為value的情況,就需要不斷重復幾個步驟:
get 1
=> user1,反序列化user1,user.banlance = 1000
,序列化user1,set 1 user1
del
del刪除一個key
> del name
(integer) 1
> get name
(nil)
incr
incr遞增一個值,如果key不存在則創造它並初始化值為1
> set connections 10
OK
> incr connections
(integer) 11
> incr connections
(integer) 12
> del connections
(integer) 1
> incr connections //connections不存在,初始化為1
(integer) 1
場景舉例
分庫分表后,數據庫自己不生成id
- 由redis統一生成主鍵id。
incr orderId
- 如果並發請求大,可以批量生成id以提升性能,用完一批后再找redis要。
incrby orderId 1000
setnx
setnx(set-if-not-exists),如果key不存在才改變值。
> set name "panda"
OK
> get name
"panda"
> setnx name "tenny" //因為name存在所以不會改變值
(integer) 0
> get name
"panda"
> get age
(nil)
> setnx age 22 //age不存在,賦值為22
(integer) 1
> get age
"22"
場景舉例
利用該操作的原子性,實現分布式鎖。
expire & ttl
expire設置生存時間,ttl查看剩余時間。
> set name "tenny"
OK
> expire name 120 //設置name生存時間為120秒
(integer) 1
> ttl name
(integer) 80 //剩余80秒
> ttl name //2分鍾后
(integer) -2 //-2表示name這個key已經不存在了。
> get name
(nil)
> set name "tenny"
OK
> ttl name
(integer) -1 //默認-1表示永久存在
> expire name 120
(integer) 1
> ttl name
(integer) 118
> get name
"tenny"
> set name "panda" //設置值會重置存在時間
OK
> ttl name
(integer) -1
list操作:rpush & lpush & llen & lrange & lpop & rpop
- rpush在list末尾添加元素
> rpush color "blue" //[bule]
(integer) 1
> rpush color "red" //[bule, red]
(integer) 2
- lpush在list開頭添加元素
> lpush color "yellow" //[yellow, bule, red]
(integer) 3
- llen查看list長度
> llen color
(integer) 3
- lrange查看list內容,接受兩個參數,開始index和結尾index,如果結尾index為-1,表示直到list末尾。
> lrange color 0 2
1) "yellow"
2) "blue"
3) "red"
> lrange color 0 1
1) "yellow"
2) "blue"
> lrange color 1 2
1) "blue"
2) "red"
> lrange color 1 2
1) "blue"
2) "red"
- lpop移除list第一個元素並返回它
> lpop color
"yellow"
> llen color
(integer) 2
> lrange color 0 -1
1) "blue"
2) "red"
- rpop移除list最后一個元素並返回它
> rpop color
"red"
> llen color
(integer) 1
> lrange color 0 -1
1) "blue"
場景舉例
1 實現數據結構
- 棧FILO
lpush + lpop
- 隊列FIFO
lpush + rpop
- 阻塞隊列
lpush + brpop
。brpop其中的b表示block, 阻塞。可選參數timeout,默認0表示一直等待
2 公眾號消息列表
假設張三(userId=001)關注了兩個公眾號(V1和V2)
- V1發消息(消息ID=1001),往張三的消息隊列里放入消息1001。
lpush msg:001 1001
- V2發消息(消息ID=2001),往張三的消息隊列里放入消息2001。
lpush msg:001 2001
- 其他消息依次放入...
- 張三查看所關注的最新消息,從張三的消息隊列里取出前5條(5條只是舉例,隨便幾條都可以)。
lrange msg:001 0 4
- 實現消息翻頁功能,只需要往后再取5條即可,
lrange msg:001 5 9
set操作:sadd & srem & sismember & smembers & sunion
set類似list,但是元素沒有順序且只能出現一次。
- sadd添加一個元素到set
> sadd superpowers "flight" //["flight"]
(integer) 1
> sadd superpowers "x-ray vision" //["flight", "x-ray vision"]
(integer) 1
> sadd superpowers "reflexes" //["flight", "x-ray vision", "reflexes"]
(integer) 1
> SADD superpowers "flight" //重復添加無效
(integer) 0
- srem從set中移除一個元素
> srem superpowers "reflexes" //["flight", "x-ray vision"]
1
- sismember測試一個元素是否存在於set,存在返回1,不存在返回0
> sismember superpowers "flight"
(integer) 1
> sismember superpowers "reflexes"
(integer) 0
- smembers查看set所有元素
> sismember superpowers
1) "x-ray vision"
2) "flight"
- sunion聯合多個set並返回它們的合集
> sadd birdpowers "pecking"
(integer) 1
> sadd birdpowers "flight"
(integer) 1
> smembers birdpowers
1) "pecking"
2) "flight"
> smembers superpowers
1) "x-ray vision"
2) "flight"
> sunion superpowers birdpowers //無序的 sunion birdpowers superpowers結果一樣
1) "pecking"
2) "flight"
3) "x-ray vision"
場景舉例
1 抽獎活動
設抽獎集合名為luckdraw
- 將抽獎用戶放進set里。
sadd luckdraw 001
- 查看所有參與用戶。
smembers luckdraw
- 抽取3名中獎者。
srandmember luckdraw 3
- 有時,需同時將中獎者移除(如中了二等獎的用戶不能再參與一等獎)。
spop luckdraw 3
2 微博共同關注
設set1 set2 分別是兩個人a, b的關注集合
- 兩個人的共同關注,
sinter set1 set2
- a是否關注了某個人c,
sismember set1 c
- set1和set2的差集,即b可能認識的人,
sdiff set1 set2
sorted set操作:zadd zrange
有序集合(sorted set)類似集合,不過它每個元素有一個關聯值,通過這個關聯值對元素進行排序。
- zadd為一個有序集合添加元素
> zadd hackers 1940 "Alan Kay"
(integer) 1
> zadd hackers 1906 "Grace Hopper"
(integer) 1
> zadd hackers 1953 "Richard Stallman"
(integer) 1
> zadd hackers 1965 "Yukihiro Matsumoto"
(integer) 1
> zadd hackers 1916 "Claude Shannon"
(integer) 1
> zadd hackers 1969 "Linus Torvalds"
(integer) 1
> zadd hackers 1957 "Sophie Wilson"
(integer) 1
> zadd hackers 1912 "Alan Turing"
(integer) 1
- zrange類似於lrange,查看sorted set元素。接受兩個參數,開始index和結尾index,如果結尾index為-1,表示直到末尾
> zrange hackers 0 -1
1) "Grace Hopper"
2) "Alan Turing"
3) "Claude Shannon"
4) "Alan Kay"
5) "Richard Stallman"
6) "Sophie Wilson"
7) "Yukihiro Matsumoto"
8) "Linus Torvalds"
可以看到元素以年份遞增排序。
> ZRANGE hackers 2 4
1) "Claude Shannon"
2) "Alan Kay"
3) "Richard Stallman"
場景舉例
熱點新聞
假設20190918這天有 xx事件,yy事件,zz事件等熱點事件。
- 對於某一個事件,每被點擊(或搜索)一次熱度加1。
zincrby hotNews:20190918 1 xx事件
- 當日熱點前10,按分值倒序排序后取前10(翻頁的話往后再取10個即可)。
zrevrange hotNews:20190918 0 9 withscores
- 七日熱點榜單,求hotNews:20190912 hotNews:20190913 ... hotNews:20190918的並集,同樣的條目會合並加分(比如20190912有yy事件,20199013也有yy事件,最終集合里會將所有yy事件的分值求和)。
zunionstore hotNews:20190912-20190918 7 hotNews:20190912 hotNews:20190912 hotNews:20190913 ... hotNews:20190918
- 七日熱點前10,對上一步的並集按分值倒序排序后取前10。
zrevrange hotNews:20190912-20190918 0 9 withscores
hash操作:hset & hget & hgetall & hmset & hincrby & hdel
hash 是一個string類型的field和value的映射表,hash特別適合用於存儲對象。
- hset添加值
> hset user name "tenny"
(integer) 1
> hset user email "tenny@example.com"
(integer) 1
> hset user password "cutepanda"
(integer) 1
- hget獲取值
> hget user name
"tenny"
- hgetall獲取所有值
> hgetall user
1) "name"
2) "tenny"
3) "email"
4) "tenny@example.com"
5) "password"
6) "cutepanda"
- hmset一次性設置多個值
> hmset user1 name "tenny1" password "cutepanda1" email "tenny1@example.com"
OK
> hgetall user1
1) "name"
2) "tenny1"
3) "password"
4) "cutepanda1"
5) "email"
6) "tenny1@example.com"
可以使用hset繼續添加或修改
> hset user1 age 22
(integer) 1
> hgetall user1
1) "name"
2) "tenny1"
3) "password"
4) "cutepanda1"
5) "email"
6) "tenny1@example.com"
7) "age"
8) "22"
> hset user1 age 24 //已存在的值重復設定返回0,新值返回1
(integer) 0
> hgetall user1
1) "name"
2) "tenny1"
3) "password"
4) "cutepanda1"
5) "email"
6) "tenny1@example.com"
7) "age"
8) "24"
- hincrby增加數值型域的值
> hset user visits 10
(integer) 1
> hincrby user visits 1
(integer) 11
> hincrby user visits 1
(integer) 12
- hdel刪除指定的域
> hdel user visits
(integer) 1
> hdel user age //刪除不存在的域返回0
(integer) 0
> hdel user visits
(integer) 0
> hincrby user visits 20 //字段不存在則默認設置為0后再執行此增加操作
(integer) 20
場景舉例
1 分組儲存用戶
假設我們有存儲用戶信息:hmset user 1:name zhangsan 1:banlance 1688
當用戶很多(假設有10000個)時,一次性獲取全部用戶將耗時很久。這里便可以采取分組的策略
- 將10000個用戶分為10組,每組1000個用戶,則用戶所在組編號=userId/1000 + 1。即會有user01, user02, ... user10 共10個組,而不是原來的只有user一個大組
- 獲取用戶id是500的余額。先計算其所在分組編號 500/1000 + 1 = 01 ,然后執行
hmget user01 500:banlance
- 設置用戶id是1200的余額為8000。先計算其所在分組編號 1200/1000 + 1 = 02,然后執行
hmset user02 1200:balance 8000
2 購物車
假如以cart前綴+用戶id做key,商品id做field,商品數量做value。即cart:userId productId count
- 添加商品,往用戶1001的購物車里添加商品id為10088的商品。
hset cart:1001 10088 1
- 增加商品數量,用戶1001又添加了一個10088商品。
hincrby cart:1001 10088 1
- 刪除商品,用戶刪除了10088這個商品。
hdel cart:1001 10088
- 查看商品總數,統計用戶1001的購物車里所有商品的總數。
hlen cart:1001
- 獲取所有商品,獲取用戶1001的購物車里所有商品id及其count。
hgetall cart:1001
以上介紹了redis的一些簡單命令,可以訪問redis中文官方網站了解更多。