1. Redis基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)與應(yīng)用場(chǎng)景

概要
1)Redis的常用指令及業(yè)務(wù)應(yīng)用場(chǎng)景
2)使用Java客戶端操作Redis

簡(jiǎn)介:redis是一個(gè)開(kāi)源的使用C語(yǔ)言編寫说贝、支持網(wǎng)絡(luò)榆综、可基于內(nèi)存也可以持久化的日志型Key-Value數(shù)據(jù)庫(kù)糠爬,并提供多種語(yǔ)言的API楣号。
本質(zhì):客戶端-服務(wù)端應(yīng)用程序
特點(diǎn):使用簡(jiǎn)單叼耙、性能強(qiáng)大腕窥、應(yīng)用場(chǎng)景豐富

1.1 通用指令

指令 說(shuō)明
del key key存在時(shí),將key刪除
dump key 序列化給定key筛婉,并返回被序列化的值
exists key 檢查給定key是否存在
expire key seconds 為給定key設(shè)置過(guò)期時(shí)間簇爆,以秒計(jì)
ttl key 以秒為單位炸宵,返回給定key的剩余生存時(shí)間(TTL-time to live)
type key 返回key所存儲(chǔ)的值得類型
dbsize 返回對(duì)應(yīng)DB的大小
help @對(duì)應(yīng)的數(shù)據(jù)類型 查看對(duì)應(yīng)的數(shù)據(jù)類型的所有指令
help cmd 查看對(duì)應(yīng)的指令的用法

示例
連接到服務(wù)端:

[docker@docker-node1 redis-all]$ ./redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379>

簡(jiǎn)單指令演示:

127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> set name vander
OK
127.0.0.1:6379[1]> exists name
(integer) 1
127.0.0.1:6379[1]> ttl name
(integer) -1
127.0.0.1:6379[1]> type name
string
127.0.0.1:6379[1]> set name vander ex 10
OK
127.0.0.1:6379[1]> ttl name
(integer) 5
127.0.0.1:6379[1]> get name
"vander"
127.0.0.1:6379[1]> ttl name
(integer) -2
127.0.0.1:6379[1]> get name
(nil)
127.0.0.1:6379[1]> set name1 jason
OK
127.0.0.1:6379[1]> keys *
1) "name1"
127.0.0.1:6379[1]>

1.2 五種基本數(shù)據(jù)結(jié)構(gòu)

Redis的數(shù)據(jù)結(jié)構(gòu)的命令可以從官方文檔查找:https://redis.io/commands

redis官網(wǎng)
Redis的5種基本數(shù)據(jù)結(jié)構(gòu).png

1.2.1 數(shù)據(jù)結(jié)構(gòu)-String

簡(jiǎn)介:string數(shù)據(jù)結(jié)構(gòu)是簡(jiǎn)單的key-value類型,value不僅是string腊徙,還可以是數(shù)字
應(yīng)用場(chǎng)景:微博數(shù)酷含、粉絲數(shù)等常規(guī)計(jì)數(shù)、分布式鎖(Redis執(zhí)行Lua腳本保證原子性)哨毁、Session共享枫甲、全局序列號(hào)

字符串常用操作

指令 說(shuō)明
set key value 設(shè)置指定key的值
get key 獲取指定key的值
mset key value [<key value> ...] 批量存儲(chǔ)多個(gè)鍵值對(duì)
mget key [key ...] 獲取所有給定key的值

原子加減

指令 說(shuō)明
incr key 將key中存儲(chǔ)的數(shù)字值增1
decr key 將key中存儲(chǔ)的數(shù)字值減1
incrby key increment 將key存儲(chǔ)的數(shù)字值加上increment
decrby key decrement 將key存儲(chǔ)的數(shù)字值減去decrement

設(shè)置Key-Value的有效時(shí)間:

127.0.0.1:6379[1]> set name vander ex 10
OK
127.0.0.1:6379[1]> get name
"vander"
127.0.0.1:6379[1]> ttl name
(integer) 5
127.0.0.1:6379[1]> get name
"vander"
127.0.0.1:6379[1]> ttl name
(integer) -2
127.0.0.1:6379[1]> get name
(nil)

設(shè)置對(duì)Value進(jìn)行遞增或者遞減

127.0.0.1:6379[1]> set counter 10
OK
127.0.0.1:6379[1]> incr counter
(integer) 11
127.0.0.1:6379[1]> decr counter
(integer) 10
127.0.0.1:6379[1]>

應(yīng)用場(chǎng)景

## 單值緩存或者是使用對(duì)象序列化器存儲(chǔ)<key,Object>
127.0.0.1:6379> set name jason
OK
127.0.0.1:6379> get name
"jason"
127.0.0.1:6379>
## 分布式鎖的應(yīng)用
# 假設(shè)是第一個(gè)線程執(zhí)行這個(gè)指令
127.0.0.1:6379> setnx product:iPhone true 
(integer) 1
# 另一個(gè)線程執(zhí)行此指令就失敗了
127.0.0.1:6379> setnx product:iPhone true 
(integer) 0
# 第一個(gè)線程獲取設(shè)置成功后表示獲取到了鎖,等用完之后把鎖釋放掉
127.0.0.1:6379> del product:iPhone 
(integer) 1
## 計(jì)數(shù)器場(chǎng)景
# 例如記錄文章被讀取的次數(shù)
127.0.0.1:6379> incr article:read_count:001
(integer) 1
127.0.0.1:6379> incr article:read_count:001
(integer) 2
127.0.0.1:6379> incr article:read_count:001
(integer) 3
127.0.0.1:6379> get article:read_count:001
"3"
## Web集群的Session共享
Spring Session + Redis 實(shí)現(xiàn)Session共享
## 分布式系統(tǒng)全局序列號(hào)
#為了保證性能每個(gè)客戶端批量獲取一批序列號(hào)扼褪,用完之后再到Redis里重新取一批
incrby orderid 1000

1.2.2 數(shù)據(jù)結(jié)構(gòu)-List

簡(jiǎn)介:即鏈表

數(shù)據(jù)結(jié)構(gòu)-list.png

可實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu):
Stack(棧) = LPUSH+LPOP = FILO
Queue(隊(duì)列)= LPUSH+RPOP = FIFO
Blocking Queue(阻塞隊(duì)列)= LPUSH + BRPOP

使用場(chǎng)景:微博的關(guān)注列表想幻,粉絲列表

指令 說(shuō)明
lpush key value1 value2 ... 將一個(gè)或多個(gè)值插入到列表頭部
rpush key value1 value2 ... 在列表中添加一個(gè)或多個(gè)值
lpop key 移除并獲取列表的第一個(gè)元素,返回值為移除的元素
rpop key 移除并獲取列表的最后一個(gè)元素话浇,返回值為移除的元素
lrange 獲取所有給定key的值

實(shí)現(xiàn)棧(后進(jìn)先出)的功能:

127.0.0.1:6379[1]> rpush teachers Miss.Xu Miss.Yu
(integer) 2
127.0.0.1:6379[1]> rpop teachers
"Miss.Yu"
127.0.0.1:6379[1]> rpop teachers
"Miss.Xu"
127.0.0.1:6379[1]> rpush teachers Miss.Xu
(integer) 1
127.0.0.1:6379[1]> rpush teachers Miss.Yu
(integer) 2
127.0.0.1:6379[1]> rpop teachers
"Miss.Yu"
127.0.0.1:6379[1]> rpop teachers
"Miss.Xu"
127.0.0.1:6379[1]>

實(shí)現(xiàn)隊(duì)列(先進(jìn)先出)功能:

127.0.0.1:6379[1]> lpush students stu1 stu2
(integer) 2
127.0.0.1:6379[1]> rpop students
"stu1"
127.0.0.1:6379[1]> rpop students
"stu2"
127.0.0.1:6379[1]>

場(chǎng)景模擬

指令 說(shuō)明
lpush key value1 value2 ... 將一個(gè)或多個(gè)值插入到列表頭部
rpush key value1 value2 ... 在列表中添加一個(gè)或多個(gè)值
lpop key 移除并獲取列表的第一個(gè)元素脏毯,返回值為移除的元素
rpop key 移除并獲取列表的最后一個(gè)元素,返回值為移除的元素
lrange 獲取所有給定key的值
blpop key [key ...] timeout 從key列表頭部彈出一個(gè)元素幔崖,若列表沒(méi)有元素食店,阻塞等待timeout秒,如果timeout=0赏寇,則會(huì)一直阻塞等待
brpop key [key ...] timeout 從key列表尾部彈出一個(gè)元素吉嫩,若列表沒(méi)有元素,阻塞等待timeout秒蹋订,如果timeout=0率挣,則會(huì)一直阻塞等待
##微博或微信公眾號(hào)消息,如Vander關(guān)注了"財(cái)經(jīng)旗艦"露戒、“大眾點(diǎn)評(píng)”兩個(gè)公眾號(hào)
#001為用戶Vander的UserID
#模擬財(cái)經(jīng)旗艦發(fā)送了一個(gè)消息椒功,消息編號(hào)為1001
127.0.0.1:6379> lpush msg:001 msg_1001
(integer) 1
#模擬大眾點(diǎn)評(píng)發(fā)送了一個(gè)消息,消息編號(hào)為1005
127.0.0.1:6379> lpush msg:001 msg_1005
(integer) 2
#查看待閱列表
127.0.0.1:6379> lrange msg:001 0 2
1) "msg_1005"
2) "msg_1001"

1.2.3 數(shù)據(jù)結(jié)構(gòu)-Set

簡(jiǎn)介:即集合智什,存儲(chǔ)一堆不重復(fù)值的組合
使用場(chǎng)景:抽獎(jiǎng)动漾、微信微博點(diǎn)贊、收藏荠锭、標(biāo)簽

Set常用操作

指令 說(shuō)明
sadd key member [member ...] 將一個(gè)或多個(gè)值插入到列表頭部
srem key member [member ...] 從集合key中刪除元素
spop key [count] 移除并獲取集合中隨機(jī)獲取count個(gè)元素旱眯,返回值為移除的元素
srandmember key [count] 獲取集合中隨機(jī)獲取count個(gè)元素,不會(huì)移除元素
sismembers key member 判斷member元素是否存在集合key中
smembers key 返回集合中的所有成員
sunion 返回給定集合的并集

往集合中添加刪除元素:

127.0.0.1:6379[1]> sadd students a_stu b_stu
(integer) 2
127.0.0.1:6379[1]> spop students
"b_stu"
127.0.0.1:6379[1]> sadd students c_stu
(integer) 1
127.0.0.1:6379[1]> spop students
"c_stu"
127.0.0.1:6379[1]> sadd students 0_stus
(integer) 1
127.0.0.1:6379[1]> spop students
"a_stu"
127.0.0.1:6379[1]> sadd students a_stu b_stu
(integer) 2
127.0.0.1:6379[1]> smembers students
1) "b_stu"
2) "0_stus"
3) "a_stu"
127.0.0.1:6379[1]> spop students 3
1) "b_stu"
2) "0_stus"
3) "a_stu"

將三個(gè)集合取并集:

127.0.0.1:6379[1]> sadd stuGroup1 stu_a stu_b stu_c
(integer) 3
127.0.0.1:6379[1]> sadd stuGroup2 stu_k stu_a stu_b
(integer) 3
127.0.0.1:6379[1]> sadd stuGroup3 stu_x stu_c stu_b
(integer) 3
127.0.0.1:6379[1]> sunion stuGroup1 stuGroup2 stuGroup3
1) "stu_x"
2) "stu_c"
3) "stu_k"
4) "stu_a"
5) "stu_b"
127.0.0.1:6379[1]>

場(chǎng)景模擬

##模擬抽獎(jiǎng)
# 添加抽獎(jiǎng)用戶
127.0.0.1:6379> sadd draw_user Vander Jason Panda Susan
(integer) 4
# 查看所有參與抽獎(jiǎng)用戶
127.0.0.1:6379> smembers draw_user
1) "Susan"
2) "Jason"
3) "Panda"
4) "Vander"
# 抽取一個(gè)用戶放回的(不放回的用spop draw_user 1)
127.0.0.1:6379> srandmember draw_user 1
1) "Susan"
127.0.0.1:6379> srandmember draw_user 1
1) "Panda"
127.0.0.1:6379> srandmember draw_user 1
1) "Jason"

##模擬點(diǎn)贊取消點(diǎn)贊证九、收藏删豺、標(biāo)簽
# 點(diǎn)贊,001-消息ID愧怜、1001-用戶ID呀页,說(shuō)明1001用戶對(duì)001消息點(diǎn)贊
127.0.0.1:6379> sadd like:001 1001
(integer) 1
# 1001用戶對(duì)002消息點(diǎn)贊
127.0.0.1:6379> sadd like:002 1001
(integer) 1
# 1002用戶對(duì)001消息點(diǎn)贊
127.0.0.1:6379> sadd like:001 1002
(integer) 1
# 檢查1002用戶是否對(duì)001消息點(diǎn)贊
127.0.0.1:6379> sismember like:001 1002
(integer) 1
# 獲取對(duì)001消息點(diǎn)贊的用戶列表
127.0.0.1:6379> smembers like:001
1) "1001"
2) "1002"
# 獲取對(duì)001消息點(diǎn)贊的用戶數(shù)
127.0.0.1:6379> scard like:001
(integer) 2
# 1001用戶取消對(duì)001消息點(diǎn)贊
127.0.0.1:6379> srem like:001 1001
(integer) 1

Set集合操作

指令 說(shuō)明
sinter key [key ...] 交集運(yùn)算
sinterstore dest key [key...] 交集運(yùn)算并把結(jié)果存到新的集合dest中
sunion key [key ...] 并集運(yùn)算
sunionstore dest key [key ...] 并集運(yùn)算并把結(jié)果存到新的集合dest中
sdiff key [key ...] 差集運(yùn)算
sdiffstore dest key [key ...] 差集運(yùn)算并把結(jié)果存到新的集合dest中
set-集合運(yùn)算.png

集合運(yùn)算應(yīng)用場(chǎng)景:共同關(guān)注、共同喜好拥坛、電商商品篩選

假設(shè)上圖中的集合Set1是A同學(xué)的關(guān)注列表蓬蝶,Set2是B同學(xué)的關(guān)注列表尘分,Set3是C同學(xué)的關(guān)注列表

A同學(xué)和B同學(xué)的共用關(guān)注:Set1∩Set2 -> sinter set1 set2

A同學(xué)關(guān)注的人關(guān)注了他:即A同學(xué)關(guān)注了B同學(xué),A同學(xué)關(guān)注了C同學(xué)丸氛,B培愁、C同學(xué)都關(guān)注了d
SISMEMBER Set2 d(即A同學(xué)關(guān)注的B同學(xué)關(guān)注了d)
SISMEMBER Set3 d(即A同學(xué)關(guān)注的C同學(xué)關(guān)注了d)

A可能認(rèn)識(shí)的人:SDIFF Set2 Set1 (即從B同學(xué)的關(guān)注列表里列出A同學(xué)沒(méi)有的)

場(chǎng)景模擬

# 添加華為p40手機(jī)
127.0.0.1:6379> sadd brand:huawei p40
(integer) 1
# 添加小米11Pro手機(jī)
127.0.0.1:6379> sadd brand:xiaomi "xiaomi 11 pro"
(integer) 1
# 添加iPhone 12 Pro手機(jī)
127.0.0.1:6379> sadd brand:apple "iPhone 12 Pro"
(integer) 1
# 添加安卓系統(tǒng)的手機(jī)
127.0.0.1:6379> sadd os:android p40 "xiaomi 11 pro"
(integer) 2
# 添加內(nèi)存8G的手機(jī)
127.0.0.1:6379> sadd ram:8G p40 "xiaomi 11 pro" "iPhone 12 Pro"
(integer) 3
# 獲取安卓系統(tǒng)且內(nèi)存為8G的手機(jī)
127.0.0.1:6379> sinter os:android ram:8G
1) "p40"
2) "xiaomi 11 pro"

1.2.4 數(shù)據(jù)結(jié)構(gòu)-Sorted Set(ZSet)

簡(jiǎn)介:即有序集合,存儲(chǔ)一堆不重復(fù)值的組合缓窜,與Set類似定续,區(qū)別在于Set不是自動(dòng)有序,Sorted Set則可以通過(guò)用戶額外提供一個(gè)優(yōu)先級(jí)(score)來(lái)為成員排序雹洗,并且是插入有序(會(huì)自動(dòng)進(jìn)行排序)

數(shù)據(jù)結(jié)構(gòu)-zset.png

使用場(chǎng)景:排行榜香罐、按用戶投票和進(jìn)行時(shí)間排序

ZSet常用操作

指令 說(shuō)明
zadd key <score member> [<score member> ...] 往有序集合key中加入帶分值的元素
zrem key member [member ...] 從有序集合key中刪除元素
zscore key member 返回有序集合key中元素member的分值
zincrby key incr member 為有序集合key中元素member的分支加上incr
zcard key 返回有序集合key中的元素個(gè)數(shù)
zrange key start stop [withscores] 正序獲取有序集合Key卧波,從start下標(biāo)到stop下標(biāo)的元素
zrevrange key start stop [withscores] 倒序獲取有序集合Key时肿,從start下標(biāo)到stop下標(biāo)的元素

往有序集中添加元素:

127.0.0.1:6379[1]> del stuGroup1
(integer) 1
127.0.0.1:6379[1]> zadd stuGroup1 0 "stu_a" 1 "stu_b" 2 "stu_c"
(integer) 3
127.0.0.1:6379[1]> del stuGroup1
(integer) 1
127.0.0.1:6379[1]> zadd stuGroup1 0 stu_a 1 stu_b 2 stu_c
(integer) 3
127.0.0.1:6379[1]> zrange stuGroup1 0 -1 withscores
1) "stu_a"
2) "0"
3) "stu_b"
4) "1"
5) "stu_c"
6) "2"

移除并顯示有序集的元素:

127.0.0.1:6379[1]> zrem stuGroup1 stu_b
(integer) 1
127.0.0.1:6379[1]> zrange stuGroup1 0 -1 withscores
1) "stu_a"
2) "0"
3) "stu_c"
4) "2"
127.0.0.1:6379[1]> zcard stuGroup1
(integer) 2

場(chǎng)景模擬

##使用ZSet實(shí)現(xiàn)排行榜,假設(shè)當(dāng)前百度熱榜是趙麗穎結(jié)婚港粱、鄭爽棄養(yǎng)螃成、喬妹離婚
# 使用此方式登錄,否則顯示亂碼
./redis-cli --raw -h 127.0.0.1 -p 6379
# 模擬點(diǎn)擊新聞
127.0.0.1:6379> zincrby HotNews:20210201 1 鄭爽棄養(yǎng)
"1"
127.0.0.1:6379> zincrby HotNews:20210201 1 鄭爽棄養(yǎng)
"2"
127.0.0.1:6379> zincrby HotNews:20210201 1 鄭爽棄養(yǎng)
"3"
127.0.0.1:6379> zincrby HotNews:20210201 1 鄭爽棄養(yǎng)
"4"
127.0.0.1:6379> zincrby HotNews:20210201 1 趙麗穎結(jié)婚
"1"
127.0.0.1:6379> zincrby HotNews:20210201 1 趙麗穎結(jié)婚
"2"
127.0.0.1:6379> zincrby HotNews:20210201 1 喬妹離婚
"1"
127.0.0.1:6379> zincrby HotNews:20210201 1 小米11發(fā)布
"1"
127.0.0.1:6379> zincrby HotNews:20210201 1 蘋果發(fā)布會(huì)
"1"
# 展示當(dāng)日點(diǎn)擊量排行前3的新聞查坪,并將點(diǎn)擊量輸出
127.0.0.1:6379> zrevrange HotNews:20210201 0 2 withscores
鄭爽棄養(yǎng)
4
趙麗穎結(jié)婚
2
蘋果發(fā)布會(huì)
1

#新增其它兩天的點(diǎn)擊量
127.0.0.1:6379> zincrby HotNews:20210202 1 小米11發(fā)布
1
127.0.0.1:6379> zincrby HotNews:20210202 1 喬妹離婚
1
127.0.0.1:6379> zincrby HotNews:20210203 1 鄭爽棄養(yǎng)
1
127.0.0.1:6379> zincrby HotNews:20210203 1 小米11發(fā)布
1
# 3天內(nèi)的榜單計(jì)算
127.0.0.1:6379> zunionstore HotNews:20210201-20210203 3 HotNews:20210201 HotNews:20210202 HotNews:20210203
5
127.0.0.1:6379> zrange HotNews:20210201-20210203 0 -1 withscores
蘋果發(fā)布會(huì)
1
喬妹離婚
2
趙麗穎結(jié)婚
2
小米11發(fā)布
3
鄭爽棄養(yǎng)
5
# 展示3天排行前3名點(diǎn)擊量的事件
127.0.0.1:6379> zrevrange HotNews:20210201-20210203 0 2
鄭爽棄養(yǎng)
小米11發(fā)布
趙麗穎結(jié)婚

ZSet集合操作

指令 說(shuō)明
zunionstore dest numkeys key [key ...] 并集運(yùn)算寸宏,并存儲(chǔ)結(jié)果到dest
zinterstore dest numkeys key [key ...] 交集運(yùn)算,并存儲(chǔ)結(jié)果到dest

1.2.5 數(shù)據(jù)結(jié)構(gòu)-Hash

簡(jiǎn)介:Hash是一個(gè)string類型的field和value的映射表
使用場(chǎng)景:存儲(chǔ)部分變更數(shù)據(jù)偿曙,如用戶信息

指令 說(shuō)明
hset key field value 將哈希表中key的字段field的值設(shè)置為value
hget key field 獲取存儲(chǔ)在哈希表中指定field的值
hmset key field value [field value ...] 批量獲取哈希表key中對(duì)應(yīng)多個(gè)field的鍵值
hmget key field [field ...] 批量獲取哈希表key中多個(gè)field鍵值
hsetnx key field value 存儲(chǔ)一個(gè)不存在與hash表key的鍵值對(duì)
hdel key field [field ...] 刪除hash表key中的field簡(jiǎn)直
hlen key 返回hash表key中field的數(shù)量
hget all 獲取在哈希表中指定key的所有字段和值
hincrby key field increment 為hash表key中filed鍵的值上增量increment

往Hash表中插入元素:

127.0.0.1:6379[1]> hset Miss.Xu age 25
(integer) 1
127.0.0.1:6379[1]> hset Miss.Xu height 170
(integer) 1
127.0.0.1:6379[1]> hget Miss.Xu age
"25"
127.0.0.1:6379[1]> hgetall Miss.Xu
1) "age"
2) "25"
3) "height"
4) "170"
127.0.0.1:6379[1]>

場(chǎng)景模擬

##對(duì)象緩存
127.0.0.1:6379> hmset user:2010130110 name Vander age 18
OK
127.0.0.1:6379> hmget user:2010130110 name age
1) "Vander"
2) "18"
##電商購(gòu)物車氮凝,key-用戶ID field-商品ID value-商品數(shù)量
# 添加商品
127.0.0.1:6379> hset cart:2010130110 1001 1
(integer) 1
127.0.0.1:6379> hset cart:2010130110 1002 1
(integer) 1
127.0.0.1:6379> hset cart:2010130110 1003 2
(integer) 1
127.0.0.1:6379> hincrby cart:2010130110 1001 3
(integer) 4
# 獲取商品種類的個(gè)數(shù)
127.0.0.1:6379> hlen cart:2010130110
(integer) 3
# 刪除商品
127.0.0.1:6379> hdel cart:2010130110 1002
(integer) 1
# 獲取購(gòu)物車的所有商品
127.0.0.1:6379> hgetall cart:2010130110
1) "1001"
2) "4"
3) "1003"
4) "2"

Hash VS string

優(yōu)勢(shì):
1)同類數(shù)據(jù)歸類整合存儲(chǔ),方便數(shù)據(jù)管理
2)相比string操作消耗內(nèi)存與CPU更小
3)相比string存儲(chǔ)更省空間

劣勢(shì):

1)過(guò)期功能不能使用在field上望忆,只能用在key上
2)Redis集群架構(gòu)下不適合大規(guī)模使用

1.3 Java代碼實(shí)操

下面我們通過(guò)Spring演示以上的數(shù)據(jù)罩阵,細(xì)節(jié)請(qǐng)查看Spring官方文檔:

https://docs.spring.io/spring-data/redis/docs/2.1.8.RELEASE/reference/html/

官方文檔中有這么一句話:
Spring Redis requires Redis 2.6 or above and Spring Data Redis integrates with Lettuce and Jedis, two popular open-source Java libraries for Redis(說(shuō)明Spring的Redis客戶端有Lettuce和Jedis兩種)

1.3.1 Example :使用Jedis連接單實(shí)例的Redis服務(wù)端

@Profile(“jedis”)—@ActiveProfiles(“jedis”)成對(duì)使用,ActiveProfiles會(huì)選擇對(duì)應(yīng)的Profile進(jìn)行注入
測(cè)試類:JedisTests.java——使用Jedis操作set启摄、hash稿壁、zset、list(模擬隊(duì)列)

package szu.vander.test.standalone;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import redis.clients.jedis.Jedis;

import java.util.HashMap;
import java.util.List;
import java.util.Set;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
@ActiveProfiles("single") // 設(shè)置profile
@Slf4j
public class JedisTests {

    @Autowired
    private Jedis jedis;

    // ------------------------ jedis 工具直連演示
    // jedis和redis命令名稱匹配度最高歉备,最為簡(jiǎn)潔傅是,學(xué)習(xí)難度最低

    // 列表~ 集合數(shù)據(jù)存儲(chǔ)~ java.util.List,java.util.Stack
    // 生產(chǎn)者消費(fèi)者(簡(jiǎn)單MQ)
    @Test
    public void list() {

        // 插入數(shù)據(jù)1 --- 2 --- 3
        jedis.rpush("queue_1", "1");
        jedis.rpush("queue_1", "2", "3");

        List<String> strings = jedis.lrange("queue_1", 0, -1);
        for (String string : strings) {
            log.info(String.format("往隊(duì)列queue_1中寫入:%s", string));
        }

        // 消費(fèi)者線程簡(jiǎn)例
        while (true) {
            String item = jedis.lpop("queue_1");
            if (item == null) break;
            log.info(String.format("從隊(duì)列queue_1中取出:%s", item));
        }

        jedis.close();
    }

    // 類似:在redis里面存儲(chǔ)一個(gè)hashmap
    // 推薦的方式蕾羊,無(wú)特殊需求是喧笔,一般的緩存都用這個(gè)
    @Test
    public void hashTest() {
        String key = "2010130110";
        jedis.hset(key, "name", "Vander");
        jedis.hset(key, "age", "18");

        jedis.hget(key, "name");
        log.info(String.format("獲取Key=%s的所有相關(guān)屬性:%s", key, jedis.hgetAll(key).toString()));
        jedis.close();
    }

    // 用set實(shí)現(xiàn)(交集 并集)
    // 交集示例: 共同關(guān)注的好友
    // 并集示例:
    @Test
    public void setTest() {
        // 取出兩個(gè)人共同關(guān)注的好友
        // 每個(gè)人維護(hù)一個(gè)set
        jedis.sadd("userA", "userC", "userD", "userE");
        jedis.sadd("userB", "userC", "userE", "userF");
        // 取出共同關(guān)注
        Set<String> intersection = jedis.sinter("userA", "userB");
        log.info(String.format("獲取userA{%s}和userB{%s}的交集:%s", jedis.smembers("userA"), jedis.smembers("userB"), intersection));

        // 取出共同人群
        Set<String> unionSet = jedis.sunion("userA", "userB");
        log.info(String.format("獲取userA{%s}和userB{%s}的并集:%s", jedis.smembers("userA"), jedis.smembers("userB"), unionSet));

        jedis.close();
    }

    // 游戲排行榜
    @Test
    public void zsetTest() {
        String ranksKeyName = "exam_rank";
        jedis.zadd(ranksKeyName, 100.0, "stu1");
        jedis.zadd(ranksKeyName, 82.0, "stu2");
        jedis.zadd(ranksKeyName, 90, "stu3");
        jedis.zadd(ranksKeyName, 96, "stu4");
        jedis.zadd(ranksKeyName, 89, "stu5");
        jedis.zadd(ranksKeyName, 29, "stu6");

        Set<String> stringSet = jedis.zrevrange(ranksKeyName, 0, 2);
        System.out.println("返回前三名:");
        for (String s : stringSet) {
            System.out.println(s);
        }

        long zcount = jedis.zcount(ranksKeyName, 85, 100);
        System.out.println("超過(guò)85分的數(shù)量 " + zcount);

        jedis.close();
    }
}

JedisConfig

package szu.jason.redis.standalone.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import redis.clients.jedis.Jedis;
import szu.jason.redis.standalone.config.common.RedisStandaloneProperties;

/**
 * @author : Vander
 * @date :   2021/3/7
 * @description : 注入連接單節(jié)點(diǎn)的Jedis客戶端
 */
@Profile("jedis")
@Slf4j
@Configuration
public class JedisConfig {

    @Autowired
    private RedisStandaloneProperties properties;

    @Bean
    public Jedis jedis() {
        log.info("注入Jedis!");
        Jedis jedis = new Jedis(properties.getHostname(), properties.getPort());
        jedis.select(properties.getDb());
        return jedis;
    }

}

User

package szu.jason.redis.standalone.model;

import com.sun.xml.internal.ws.developer.Serialization;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @author : Vander
 * @date :   2021/3/7
 * @description : 此對(duì)象需要實(shí)現(xiàn)序列化接口龟再,否則JDK序列化器無(wú)法將其序列化
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {

    private String userId;

    private String username;

}

AppConfig

package szu.jason.redis.standalone.config.common;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import szu.jason.redis.standalone.model.User;

import java.util.HashMap;
import java.util.Map;

/**
 * @author : Vander
 * @date :   2021/3/7
 * @description :
 */
@ComponentScan("szu.jason.redis.standalone")
@Configuration
public class AppConfig {
    @Bean
    public Map<String, User> userRepository() {
        Map<String, User> userRepository = new HashMap<>();
        userRepository.put("2010130110", new User("2010130110", "Jason"));
        userRepository.put("2013130128", new User("2013130128", "Panda"));
        return userRepository;
    }
}

PropertiesConfig

package szu.jason.redis.standalone.config.common;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * @author : Vander
 * @date :   2021/3/7
 * @description :
 */
@Configuration
@EnableConfigurationProperties(RedisStandaloneProperties.class)
public class PropertiesConfig {
}

RedisStandaloneProperties

package szu.jason.redis.standalone.config.common;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * @author : Vander
 * @date :   2021/3/7
 * @description : 根據(jù)前綴注入屬性值
 */
@Getter
@Setter
@ConfigurationProperties(prefix = "standalone.redis")
public class RedisStandaloneProperties {

    private String hostname;

    private int port;

    private int db;

}

application-redis.properties——配置redis服務(wù)

standalone.redis.hostname=192.168.118.8
standalone.redis.port=6379
standalone.redis.db=1

Pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cache-redis-demo</artifactId>
        <groupId>szu.jason</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>redis-standalone</artifactId>

    <dependencies>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
            <version>5.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
    </dependencies>

</project>

1.3.2 Example :使用Lettuce連接單實(shí)例的Redis服務(wù)端

測(cè)試類:使用Lettuce客戶端操作Hash類型的數(shù)據(jù)

package test.redis.standalone;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import szu.jason.redis.standalone.config.common.AppConfig;
import szu.jason.redis.standalone.model.User;
import szu.jason.redis.standalone.service.LettuceExampleService;


@ActiveProfiles("lettuce")
@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@PropertySource("classpath:application-redis.properties")
@ImportAutoConfiguration(classes= AppConfig.class)
public class LettuceTests {

    @Autowired
    private LettuceExampleService exampleService;

    @Test
    public void testGet() throws Exception {
        User user = exampleService.findUser("2010130110");
        System.out.println(user);
    }

}

LettuceConfig

package szu.jason.redis.standalone.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import szu.jason.redis.standalone.config.common.RedisStandaloneProperties;

@Profile("lettuce")
@Slf4j
@EnableCaching
@Configuration
public class LettuceConfig {


    @Autowired
    private RedisStandaloneProperties properties;

    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        log.info(String.format("注入LettuceConnectionFactory书闸,當(dāng)前Host:%s:%s database:%s!",
                properties.getHostname(), properties.getPort(), properties.getDb()));
        RedisStandaloneConfiguration redisStandaloneConfiguration =
                new RedisStandaloneConfiguration(properties.getHostname(), properties.getPort());
        redisStandaloneConfiguration.setDatabase(properties.getDb());
        return new LettuceConnectionFactory(redisStandaloneConfiguration);
    }

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        log.info("注入RedisTemplate吸申!");
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 可以配置對(duì)象的轉(zhuǎn)換規(guī)則梗劫,比如使用json格式對(duì)object進(jìn)行存儲(chǔ)享甸。
        // Object --> 序列化 --> 二進(jìn)制流 --> redis-server存儲(chǔ)
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
        return redisTemplate;
    }

    // 配置Spring Cache注解功能
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        log.info("注入CacheManager!");
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
        RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
        return cacheManager;
    }

}

LettuceExampleService

package szu.jason.redis.standalone.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import szu.jason.redis.standalone.model.User;

import java.util.Map;


@Profile("lettuce")
@Slf4j
@Service
public class LettuceExampleService {
    // 參數(shù)可以是任何對(duì)象梳侨,默認(rèn)由JDK序列化
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private Map<String, User> userRepository;

    /**
     * 對(duì)象緩存功能
     */
    public User findUser(String userId) throws Exception {
        User user;
        // 1蛉威、 判定緩存中是否存在
        user = (User) redisTemplate.opsForValue().get(userId);
        if (user != null) {
            log.info("從緩存中讀取到值:" + user);
            return user;
        }

        // TODO 2、不存在則讀取數(shù)據(jù)庫(kù)或者其他地方的值
        user = userRepository.get(userId);
        log.info("從數(shù)據(jù)庫(kù)中讀取到值:" + user);

        // 3走哺、 同步存儲(chǔ)value到緩存蚯嫌。
        redisTemplate.opsForValue().set(userId, user);
        return user;
    }

}

AppConfig、PropertiesConfig丙躏、RedisStandaloneProperties择示、User、application-redis.properties晒旅、pom均與上個(gè)example一致

1.3.3 Example :使用Spring Cache實(shí)現(xiàn)自動(dòng)移除緩存栅盲、添加緩存、更新緩存

測(cè)試類:模擬查找废恋、更新谈秫、刪除數(shù)據(jù)庫(kù)記錄的操作,Spring Cache會(huì)自動(dòng)更新緩存

package test.redis.standalone;

import lombok.extern.slf4j.Slf4j;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import szu.jason.redis.standalone.config.common.AppConfig;
import szu.jason.redis.standalone.model.User;
import szu.jason.redis.standalone.service.SpringCacheService;

@ActiveProfiles("lettuce")
@Slf4j
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RunWith(SpringJUnit4ClassRunner.class)
@PropertySource("classpath:application-redis.properties")
@ImportAutoConfiguration(classes= AppConfig.class)
public class SpringCacheTests {

    private final static String USER_ID = "2010130110";

    @Autowired
    private SpringCacheService springCacheService;

    @Test
    public void test0FindUserById() {
        User user = springCacheService.findUserById(USER_ID);
        System.out.println("\n");
        log.info("查找到userId為{}的用戶:{}", USER_ID, user.toString());
        System.out.println("\n");
    }

    @Test
    public void test1UpdateUser() {
        springCacheService.updateUser(new User(USER_ID, "Panda"));
        User user = springCacheService.findUserById(USER_ID);
        System.out.println("\n");
        log.info("userId為{}的用戶:{}", USER_ID, user.toString());
        System.out.println("\n");
    }

    @Test
    public void test2DeleteUserById() {
        User user = springCacheService.deleteUserById(USER_ID);
        System.out.println("\n");
        log.info("刪除userId為{}的用戶:{}", USER_ID, user.toString());
        System.out.println("\n");
    }
}

SpringCacheService

package szu.jason.redis.standalone.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import szu.jason.redis.standalone.model.User;

import java.util.Map;

@Profile("lettuce")
@Slf4j
@Service
public class SpringCacheService {

    @Autowired
    private Map<String, User> userRepository;

    /**
     * value的作用:寫入Redis時(shí)會(huì)自動(dòng)添加緩存前綴
     * key:支持SpEL表達(dá)式
     * @param userId
     * @return
     */
    @Cacheable(cacheManager = "cacheManager", value = "user", key = "#userId")
    public User findUserById(String userId) {
        // 讀取數(shù)據(jù)庫(kù)
        User user = userRepository.get(userId);
        System.out.println("\n");
        log.info("從數(shù)據(jù)庫(kù)中讀取到數(shù)據(jù):{}", user);
        System.out.println("\n");
        return user;
    }

    @CacheEvict(cacheManager = "cacheManager", value = "user", key = "#userId")
    public User deleteUserById(String userId) {
        User user = userRepository.remove(userId);
        System.out.println("\n");
        log.info(String.format("用戶從數(shù)據(jù)庫(kù)刪除成功鱼鼓,請(qǐng)檢查緩存ID:%s是否已經(jīng)清除拟烫!", user.getUserId()));
        System.out.println("\n");
        return user;
    }

    // 如果數(shù)據(jù)庫(kù)更新成功,更新redis緩存
    @CachePut(cacheManager = "cacheManager", value = "user", key = "#user.userId", condition = "#result ne null")
    public User updateUser(User user) {
        // 更新數(shù)據(jù)庫(kù)
        userRepository.put(user.getUserId(), user);
        System.out.println("\n");
        log.info("數(shù)據(jù)庫(kù)進(jìn)行了更新迄本,檢查緩存是否一致");
        System.out.println("\n");
        return user; // 返回最新內(nèi)容硕淑,代表更新成功
    }

}

AppConfig、PropertiesConfig嘉赎、RedisStandaloneProperties置媳、User、application-redis.properties曹阔、pom均與上個(gè)example一致

運(yùn)行結(jié)果:
test0FindUserById:觀察到對(duì)應(yīng)的User被加載到了緩存

test1UpdateUser:觀察到對(duì)應(yīng)的User的名稱被改為了Panda

test2DeleteUserById:觀察到對(duì)應(yīng)的Key已經(jīng)被刪除了

附錄:建議安裝教程

下載Redis半开,解壓后編譯

$ wget http://download.redis.io/releases/redis-5.0.4.tar.gz
$ tar xzf redis-5.0.4.tar.gz
$ cd redis-5.0.4
$ make

運(yùn)行Redis服務(wù)端

$ src/redis-server
You can interact with Redis using the built-in client:

運(yùn)行Redis客戶端

$ src/redis-cli
redis> set foo bar
OK
redis> get foo
"bar"
···
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市赃份,隨后出現(xiàn)的幾起案子寂拆,更是在濱河造成了極大的恐慌,老刑警劉巖抓韩,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纠永,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡谒拴,警方通過(guò)查閱死者的電腦和手機(jī)尝江,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)英上,“玉大人炭序,你說(shuō)我怎么就攤上這事啤覆。” “怎么了惭聂?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵窗声,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我辜纲,道長(zhǎng)笨觅,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任耕腾,我火速辦了婚禮见剩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扫俺。我一直安慰自己苍苞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布牵舵。 她就那樣靜靜地躺著柒啤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪畸颅。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天方援,我揣著相機(jī)與錄音没炒,去河邊找鬼。 笑死犯戏,一個(gè)胖子當(dāng)著我的面吹牛送火,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播先匪,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼种吸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了呀非?” 一聲冷哼從身側(cè)響起坚俗,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎岸裙,沒(méi)想到半個(gè)月后猖败,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡降允,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年恩闻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片剧董。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡幢尚,死狀恐怖破停,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情尉剩,我是刑警寧澤辱挥,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站边涕,受9級(jí)特大地震影響晤碘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜功蜓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一园爷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧式撼,春花似錦童社、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至美浦,卻和暖如春弦赖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背浦辨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工蹬竖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人流酬。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓币厕,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親芽腾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子旦装,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容