Redis 是徹底開源免費的,遵照BSD協議,是一個高性能的key-value數據庫。html
Redis 與其餘 key - value 緩存產品有如下三個特色:java
Redis與其餘key-value存儲有什麼不一樣?node
Redis有着更爲複雜的數據結構而且提供對他們的原子性操做,這是一個不一樣於其餘數據庫的進化路徑。Redis的數據類型都是基於基本數據結構的同時對程序員透明,無需進行額外的抽象。python
Redis運行在內存中可是能夠持久化到磁盤,因此在對不一樣數據集進行高速讀寫時須要權衡內存,由於數據量不能大於硬件內存。在內存數據庫方面的另外一個優勢是,相比在磁盤上相同的複雜的數據結構,在內存中操做起來很是簡單,這樣Redis能夠作不少內部複雜性很強的事情。同時,在磁盤格式方面他們是緊湊的以追加的方式產生的,由於他們並不須要進行隨機訪問。mysql
1. 整個Redis數據庫將只包含一個文件,一旦系統出現災難性故障,咱們能夠很是容易的進行恢復。程序員
2. 性能最大化,它僅須要fork出子進程,由子進程完成持久化工做,極大的避免服務進程執行IO操做了。redis
3. 相比於AOF機制,若是數據集很大,RDB的啓動效率會更高算法
1. RDB容易丟數據,由於系統一旦在定時持久化以前出現宕機現象,此前沒有來得及寫入磁盤的數據都將丟失spring
2. RDB經過fork子進程來完成持久化的若是當數據集較大時,可能會致使整個服務器中止服務幾百毫秒,甚至是1秒鐘。sql
AOF優缺點介紹(鏡像)
在 /etc/redis.conf 中配置使用RDP
# save 900 1 // 900內,有1條寫入,則產生快照 # save 300 1000 // 若是300秒內有1000次寫入,則產生快照 # save 60 10000 // 若是60秒內有10000次寫入,則產生快照
# stop-writes-on-bgsave-error yes // 後臺備份進程出錯時,主進程停不中止寫入? 主進程不中止 容易形成數據不一致 # rdbcompression yes // 導出的rdb文件是否壓縮 若是rdb的大小很大的話建議這麼作 # Rdbchecksum yes // 導入rbd恢復時數據時,要不要檢驗rdb的完整性 驗證版本是否是一致 # dbfilename dump.rdb //導出來的rdb文件名 # dir ./ //rdb的放置路徑
在 /etc/redis.conf 中配置使用AOF
# appendonly no // 是否打開aof日誌功能 aof跟 rdb都打開的狀況下 # appendfsync always // 每1個命令,都當即同步到aof. 安全,速度慢 # appendfsync everysec // 折衷方案,每秒寫1次 # appendfsync no // 寫入工做交給操做系統,由操做系統判斷緩衝區大小,統一寫入到aof. 同步頻率低,速度快, # no-appendfsync-on-rewrite yes: // 正在導出rdb快照的過程當中,要不要中止同步aof # auto-aof-rewrite-percentage 100 //aof文件大小比起上次重寫時的大小,增加率100%時,重寫 缺點 剛開始的時候重複重寫屢次 # auto-aof-rewrite-min-size 64mb //aof文件,至少超過64M時,重寫 測試使用: redis-benchmark -n 10000 表示 執行請求10000次,執行ls 發現出現 rdb 跟 aof文件。appendonly.aof dump.rdb
# 注: 在dump rdb過程當中,aof若是中止同步,會不會丟失? 答: 不會,全部的操做緩存在內存的隊列裏, dump完成後,統一操做.
# 注: aof重寫是指什麼? 答: aof重寫是指把內存中的數據,逆化成命令,寫入到.aof日誌裏,以解決aof日誌過大的問題.
# 問: 若是rdb文件,和aof文件都存在,優先用誰來恢復數據? 答: aof
# 問: 2種是否能夠同時用? 答: 能夠,並且推薦這麼作
# 問: 恢復時rdb和aof哪一個恢復的快 答: rdb快,由於其是數據的內存映射,直接載入到內存,而aof是命令,須要逐條執行
字符串(string)
list(列表)
hash(字典)
set(集合)
zset(有序集合)
Redis 對String操做
import redis r = redis.Redis(host='1.1.1.3', port=6379) #一、打印這個Redis緩存全部key以列表形式返回:[b'name222', b'foo'] print( r.keys() ) # keys * #二、清空redis r.flushall() #三、設置存在時間: ex=1指這個變量只會存在1秒,1秒後就不存在了 r.set('name', 'Alex') # ssetex name Alex r.set('name', 'Alex',ex=1) # ssetex name 1 Alex #四、獲取對應key的value print(r.get('name')) # get name #五、刪除指定的key r.delete('name') # del 'name' #六、避免覆蓋已有的值: nx=True指只有當字典中沒有name這個key纔會執行 r.set('name', 'Tom',nx=True) # setnx name alex #七、從新賦值: xx=True只有當字典中已經有key值name纔會執行 r.set('name', 'Fly',xx=True) # set name alex xx #八、psetex(name, time_ms, value) time_ms,過時時間(數字毫秒 或 timedelta對象) r.psetex('name',10,'Tom') # psetex name 10000 alex #十、mset 批量設置值; mget 批量獲取 r.mset(key1='value1', key2='value2') # mset k1 v1 k2 v2 k3 v3 print(r.mget({'key1', 'key2'})) # mget k1 k2 k3 #十一、getset(name, value) 設置新值並獲取原來的值 print(r.getset('age','100')) # getset name tom #十二、getrange(key, start, end) 下面例子就是獲取name值abcdef切片的0-2間的字符(b'abc') r.set('name','abcdef') print(r.getrange('name',0,2)) #1三、setbit(name, offset, value) #對name對應值的二進制表示的位進行操做 r.set('name','abcdef') r.setbit('name',6,1) #將a(1100001)的第二位值改爲1,就變成了c(1100011) print(r.get('name')) #最後打印結果:b'cbcdef' #1四、bitcount(key, start=None, end=None) 獲取name對應的值的二進制表示中 1 的個數 #1五、incr(self,name,amount=1) 自增 name對應的值,當name不存在時,則建立name=amount,不然自增 #1六、derc 自動減1:利用incr和derc能夠簡單統計用戶在線數量 #若是之前有count就在之前基礎加1,沒有就第一次就是1,之後每運行一次就自動加1 num = r.incr('count') #1七、num = r.decr('count') #每運行一次就自動減1 #每運行一次incr('count')num值加1每運行一次decr後num值減1 print(num) #1八、append(key, value) 在redis name對應的值後面追加內容 r.set('name','aaaa') r.append('name','bbbb') print(r.get('name')) #運行結果: b'aaaabbbb'
import redis r = redis.Redis(host='10.1.0.51', port=6379) r.setbit('n',10,1) #設置n的第十位是二進制的1 print(r.getbit('n',10)) #獲取n的第十位是1仍是0(id=10用戶是否在線) print(r.bitcount('n')) #統計那種共有多上個1(用戶在線數量)
Redis 對 Hash操做
import redis pool = redis.ConnectionPool(host='1.1.1.3', port=6379) r = redis.Redis(connection_pool=pool) #1 hset(name, key, value) name=字典名字,key=字典key,value=對應key的值 r.hset('info','name','tom') # hset info name tom r.hset('info','age','100') print(r.hgetall('info')) # hgetall info {b'name': b'tom', b'age': b'100'} print(r.hget('info','name')) # hget info name b'tom' print(r.hkeys('info')) #打印出」info」對應的字典中的全部key [b'name', b'age'] print(r.hvals('info')) #打印出」info」對應的字典中的全部value [b'tom', b'100'] #2 hmset(name, mapping) 在name對應的hash中批量設置鍵值對 r.hmset('info2', {'k1':'v1', 'k2': 'v2','k3':'v3'}) #一次性設置多個值 print(r.hgetall('info2')) #hgetall() 一次性打印出字典中全部內容 print(r.hget('info2','k1')) #打印出‘info2’對應字典中k1對應的value print(r.hlen('info2')) # 獲取name對應的hash中鍵值對的個數 print(r.hexists('info2','k1')) # 檢查name對應的hash是否存在當前傳入的key r.hdel('info2','k1') # 將name對應的hash中指定key的鍵值對刪除 print(r.hgetall('info2')) #3 hincrby(name, key, amount=1)自增name對應的hash中的指定key的值,不存在則建立key=amount r.hincrby('info2','k1',amount=10) #第一次賦值k1=10之後每執行一次值都會自動增長10 print(r.hget('info2','k1')) #4 hscan(name, cursor=0, match=None, count=None)對於數據大的數據很是有用,hscan能夠實現分片的獲取數據 # name,redis的name # cursor,遊標(基於遊標分批取獲取數據) # match,匹配指定key,默認None 表示全部的key # count,每次分片最少獲取個數,默認None表示採用Redis的默認分片個數 print(r.hscan('info2',cursor=0,match='k*')) #打印出全部key中以k開頭的 print(r.hscan('info2',cursor=0,match='*2*')) #打印出全部key中包含2的 #5 hscan_iter(name, match=None, count=None) # match,匹配指定key,默認None 表示全部的key # count,每次分片最少獲取個數,默認None表示採用Redis的默認分片個數 for item in r.hscan_iter('info2'): print(item)
Redis 對List操做
import redis pool = redis.ConnectionPool(host='10.1.0.51', port=6379) r = redis.Redis(connection_pool=pool) #1 lpush:反向存放 rpush正向存放數據 r.lpush('names','alex','tom','jack') # 從右向左放數據好比:3,2,1(反着放) print(r.lrange('names',0,-1)) # 結果:[b'jack', b'tom'] r.rpush('names','zhangsan','lisi') #從左向右放數據如:1,2,3(正着放) print(r.lrange('names',0,-1)) #結果:b'zhangsan', b'lisi'] #2.1 lpushx(name,value) 在name對應的list中添加元素,只有name已經存在時,值添加到列表最左邊 #2.2 rpushx(name, value) 表示從右向左操做 #3 llen(name) name對應的list元素的個數 print(r.llen('names')) #4 linsert(name, where, refvalue, value)) 在name對應的列表的某一個值前或後插入一個新值 # name,redis的name # where,BEFORE或AFTER # refvalue,標杆值,即:在它先後插入數據 # value,要插入的數據 r.rpush('name2','zhangsan','lisi') #先建立列表[zhangsan,lisi] print(r.lrange('name2',0,-1)) r.linsert('name2','before','zhangsan','wangwu') #在張三前插入值wangwu r.linsert('name2','after','zhangsan','zhaoliu') #在張三前插入值zhaoliu print(r.lrange('name2',0,-1)) #5 r.lset(name, index, value) 對name對應的list中的某一個索引位置從新賦值 r.rpush('name3','zhangsan','lisi') #先建立列表[zhangsan,lisi] r.lset('name3',0,'ZHANGSAN') #將索引爲0的位置值改爲'ZHANGSAN' print(r.lrange('name3',0,-1)) #最後結果:[b'ZHANGSAN', b'lisi'] #6 r.lrem(name, value, num) 在name對應的list中刪除指定的值 # name,redis的name # value,要刪除的值 # num, num=0,刪除列表中全部的指定值; # num=2,從前到後,刪除2個; # num=-2,從後向前,刪除2個 r.rpush('name4','zhangsan','zhangsan','zhangsan','lisi') r.lrem('name4','zhangsan',1) print(r.lrange('name4',0,-1)) #7 lpop(name) 在name對應的列表的左側獲取第一個元素並在列表中移除,返回值則是第一個元素 r.rpush('name5','zhangsan','lisi') r.rpop('name5') print(r.lrange('name5',0,-1)) #8 lindex(name, index) 在name對應的列表中根據索引獲取列表元素 r.rpush('name6','zhangsan','lisi') print(r.lindex('name6',1)) #9 lrange(name, start, end) 在name對應的列表分片獲取數據 r.rpush('num',0,1,2,3,4,5,6) print(r.lrange('num',1,3)) #10 ltrim(name, start, end) 在name對應的列表中移除沒有在start-end索引之間的值 r.rpush('num1',0,1,2,3,4,5,6) r.ltrim('num1',1,2) print(r.lrange('num1',0,-1)) #11 rpoplpush(src, dst) 從一個列表取出最右邊的元素,同時將其添加至另外一個列表的最左邊 r.rpush('num2',0,1,2,3) r.rpush('num3',100) r.rpoplpush('num2','num3') print(r.lrange('num3',0,-1)) #運行結果:[b'3', b'100'] #12 blpop(keys, timeout) 將多個列表排列,按照從左到右去pop對應列表的元素 #timeout,超時時間,當元素全部列表的元素獲取完以後,阻塞等待列表內有數據的時間(秒), 0 表示永遠阻塞 r.rpush('num4',0,1,2,3) r.blpop('num4',10) print(r.lrange('num4',0,-1))
redis對Set集合操做,Set集合就是不容許重複的列表
import redis r = redis.Redis(host='10.1.0.51', port=6379) #1 sadd(name,values) name對應的集合中添加元素 #2 scard(name) 獲取name對應的集合中元素個數 r.sadd('name0','alex','tom','jack') print(r.scard('name0')) #3 sdiff(keys, *args) 在第一個name對應的集合中且不在其餘name對應的集合的元素集合 r.sadd('num6',1,2,3,4) r.sadd('num7',3,4,5,6) #在num6中有且在num7中沒有的元素 print(r.sdiff('num6','num7')) #運行結果:{b'1', b'2'} #4 sdiffstore(dest, keys, *args) #獲取第一個name對應的集合中且不在其餘name對應的集合,再將其新加入到dest對應的集合中 # 將在num7中不在num8中的元素添加到num9 r.sadd('num7',1,2,3,4) r.sadd('num8',3,4,5,6) r.sdiffstore('num9','num7','num8') print(r.smembers('num9')) #運行結果: {b'1', b'2'} #5 sinter(keys, *args) 獲取多一個name對應集合的交集 r.sadd('num10',4,5,6,7,8) r.sadd('num11',1,2,3,4,5,6) print(r.sinter('num10','num11')) #運行結果: {b'4', b'6', b'5'} #6 sinterstore(dest, keys, *args) 獲取多一個name對應集合的並集,再講其加入到dest對應的集合中 r.sadd('num12',1,2,3,4) r.sadd('num13',3,4,5,6) r.sdiffstore('num14','num12','num13') print(r.smembers('num14')) #運行結果: {b'1', b'2'} #7 sismember(name, value) 檢查value是不是name對應的集合的成員 r.sadd('name22','tom','jack') print(r.sismember('name22','tom')) #8 smove(src, dst, value) 將某個成員從一個集合中移動到另一個集合 r.sadd('num15',1,2,3,4) r.sadd('num16',5,6) r.smove('num15','num16',1) print(r.smembers('num16')) #運行結果: {b'1', b'5', b'6'} #9 spop(name) 從集合的右側(尾部)移除一個成員,並將其返回 r.sadd('num17',4,5,6) print(r.spop('num17')) #10 srandmember(name, numbers) 從name對應的集合中隨機獲取 numbers 個元素 r.sadd('num18',4,5,6) print(r.srandmember('num18',2)) #11 srem(name, values) 在name對應的集合中刪除某些值 r.sadd('num19',4,5,6) r.srem('num19',4) print(r.smembers('num19')) #運行結果: {b'5', b'6'} #12 sunion(keys, *args) 獲取多一個name對應的集合的並集 r.sadd('num20',3,4,5,6) r.sadd('num21',5,6,7,8) print(r.sunion('num20','num21')) #運行結果: {b'4', b'5', b'7', b'6', b'8', b'3'} #13 sunionstore(dest,keys, *args) # 獲取多個name對應的集合的並集,並將結果保存到dest對應的集合中 r.sunionstore('num22','num20','num21') print(r.smembers('num22')) #運行結果: {b'5', b'7', b'3', b'8', b'6', b'4'} #14 sscan(name, cursor=0, match=None, count=None) # sscan_iter(name, match=None, count=None) #同字符串的操做,用於增量迭代分批獲取元素,避免內存消耗太大
import redis pool = redis.ConnectionPool(host='10.1.0.51', port=6379) r = redis.Redis(connection_pool=pool) #1 zadd(name, *args, **kwargs) 在name對應的有序集合中添加元素 r.zadd('zz', n1=11, n2=22,n3=15) print(r.zrange('zz',0,-1)) #[b'n1', b'n3', b'n2'] print(r.zrange('zz',0,-1,withscores=True)) #[(b'n1', 11.0), (b'n3', 15.0), (b'n2', 22.0)] #2 zcard(name) 獲取name對應的有序集合元素的數量 #3 zcount(name, min, max) 獲取name對應的有序集合中分數 在 [min,max] 之間的個數 r.zadd('name01', tom=11,jack=22,fly=15) print(r.zcount('name01',1,20)) #4 zincrby(name, value, amount) 自增name對應的有序集合的 name 對應的分數 #5 zrank(name, value) 獲取某個值在 name對應的有序集合中的排行(從 0 開始) r.zadd('name02', tom=11,jack=22,fly=15) print(r.zrank('name02','fly')) #6 zrem(name, values) 刪除name對應的有序集合中值是values的成員 r.zadd('name03', tom=11,jack=22,fly=15) r.zrem('name03','fly') print(r.zrange('name03',0,-1)) # [b'tom', b'jack'] #7 zremrangebyrank(name, min, max)根據排行範圍刪除 r.zadd('name04', tom=11,jack=22,fly=15) r.zremrangebyrank('name04',1,2) print(r.zrange('name04',0,-1)) # [b'tom'] #8 zremrangebyscore(name, min, max) 根據分數範圍刪除 r.zadd('name05', tom=11,jack=22,fly=15) r.zremrangebyscore('name05',1,20) print(r.zrange('name05',0,-1)) #9 zremrangebylex(name, min, max) 根據值返回刪除 #10 zscore(name, value) 獲取name對應有序集合中 value 對應的分數 #11 zinterstore(dest, keys, aggregate=None) #11測試過代碼報錯,未解決 #獲取兩個有序集合的交集,若是遇到相同值不一樣分數,則按照aggregate進行操做 # aggregate的值爲: SUM MIN MAX r.zadd('name09', tom=11,jack=22,fly=15) r.zadd('name10', tom=12,jack=23,fly=15) r.zinterstore('name11',2,'name09','name10') print(r.zrange('name11',0,-1))
# 127.0.0.1:6379> zadd name222 11 zhangsan 12 lisi (integer) 2 # 127.0.0.1:6379> zrange name222 0 -1 1) "zhangsan" 2) "lisi" # 127.0.0.1:6379> zadd name333 11 zhangsan 12 lisi (integer) 2 # 127.0.0.1:6379> zrange name333 0 -1 1) "zhangsan" 2) "lisi" # 127.0.0.1:6379> zinterstore name444 2 name222 name333 (integer) 2 # 127.0.0.1:6379> zrange name444 0 -1 withscores 1) "zhangsan" 2) "22" 3) "lisi" 4) "24"
redis其餘經常使用操做
import redis pool = redis.ConnectionPool(host='1.1.1.3', port=6379) r = redis.Redis(connection_pool=pool) #1 查看當前Redis全部key print(r.keys('*')) #2 delete(*names) 刪除Redis對應的key的值 r.delete('num16') #3 exists(name) 檢測redis的name是否存在 print(r.exists('name09')) #4 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(pattern='name*')) #打印出Redis中全部以name開通的key #5 expire(name ,time) 爲某個redis的某個name設置超時時間 r.expire('name09',1) # 1秒後就會刪除這個key值name09 #6 rename(src, dst) 對redis的name重命名爲 r.rename('num13','num13new')
# redis 127.0.0.1:6379> SET db_number 0 # 默認使用 0 號數據庫 # redis 127.0.0.1:6379> SELECT 1 # 使用 1 號數據庫 # redis 127.0.0.1:6379[1]> GET db_number # 已經切換到 1 號數據庫,注意 Redis 如今的命令提符多了個 [1] # redis 127.0.0.1:6379[1]> SET db_number 1 # 設置默認使用 1 號數據庫 # redis 127.0.0.1:6379[1]> GET db_number # 獲取當前默認使用的數據庫號 #1 move(name, db)) 將redis的某個值移動到指定的db下(對方庫中有就不移動) 127.0.0.1:6379> move name0 4 #2 type(name) 獲取name對應值的類型 127.0.0.1:6379[4]> type name0
redis的管道使用(經過管道向指定db傳送數據)
import redis,time pool = redis.ConnectionPool(host='10.1.0.51', port=6379,db=5) r = redis.Redis(connection_pool=pool) # pipe = r.pipeline(transaction=False) pipe = r.pipeline(transaction=True) pipe.set('name', 'alex') time.sleep(4) pipe.set('role', 'sb') pipe.execute() #只有執行這裏上面兩條纔會一塊兒執行,才能到db5中看到這兩個值 # 127.0.0.1:6379[5]> select 5 # OK # 127.0.0.1:6379[5]> keys * # 1) "name" # 2) "role"
import redis class RedisHelper: def __init__(self): self.__conn = redis.Redis(host='10.1.0.51') #鏈接Redis服務器 self.chan_sub = 'fm104.5' #發佈頻道'fm104.5' self.chan_pub = 'fm104.5' #接收頻道也是'fm104.5' #發消息 def public(self, msg): self.__conn.publish(self.chan_pub, msg) #直接調用Redis的chan_pub方法發消息 print('pub') return True #收消息 def subscribe(self): print('sub') pub = self.__conn.pubsub() #開始訂閱,僅僅至關於打開收音機 pub.subscribe(self.chan_sub) #調頻道 pub.parse_response() #準備接收 return pub #再調用一次pub.parse_response()纔會接收
from redisHelper import RedisHelper #這裏的RedisHelper()就是redisHelper中定義的類 obj = RedisHelper() #實例化一個對象RedisHelper redis_sub = obj.subscribe() while True: msg= redis_sub.parse_response() #若是Public發送有數據就打印,沒有就卡住 print(msg)
from redisHelper import RedisHelper obj = RedisHelper() obj.public('hello')
異步複製
# sentinel.conf 配置說明 sentinel monitor mymaster 127.0.0.1 6379 2 sentinel down-after-milliseconds mymaster 60000 sentinel failover-timeout mymaster 180000 sentinel parallel-syncs mymaster 1 '''一、sentinel monitor mymaster 127.0.0.1 6379 2''' #1)sentinel監控的master的名字叫作mymaster,地址爲127.0.0.1:6379 #2)當集羣中有2個sentinel認爲master死了時,才能真正認爲該master已經不可用了 '''二、sentinel down-after-milliseconds mymaster 60000''' #1)sentinel會向master發送心跳PING來確認master是否存活,若是master在60000毫秒內不迴應PONG #2)那麼這個sentinel會單方面地認爲這個master已經不可用了 '''三、sentinel failover-timeout mymaster 180000''' #1)若是sentinel A推薦sentinel B去執行failover,B會等待一段時間後,自行再次去對同一個master執行failover, #2)這個等待的時間是經過failover-timeout配置項去配置的。 #3)從這個規則能夠看出,sentinel集羣中的sentinel不會再同一時刻併發去failover同一個master, #4)第一個進行failover的sentinel若是失敗了,另一個將會在必定時間內進行從新進行failover,以此類推。 '''四、sentinel parallel-syncs mymaster 1''' #1)在發生failover主備切換時,這個選項指定了最多能夠有多少個slave同時對新的master進行同步 #2)若是這個數字越大,就意味着越多的slave由於replication而不可用,這個數字越小,完成failover所需的時間就越長。 #3)能夠經過將這個值設爲 1 來保證每次只有一個slave處於不能處理命令請求的狀態。
SLAVE OF NO ONE
命令,而後可以經過INFO
命令看到新master的配置信息。SLAVE OF NO ONE
`後,即便其它的slave還沒針對新master從新配置本身,failover也被認爲是成功了的。
# redis-6379.conf主要修改參數 port 6379 daemonize yes logfile "6379.log" dbfilename "dump-6379.rdb"
# ./redis-server redis-6379.conf
#方式1: # [root@localhost bin]# ./redis-cli -h 127.0.0.1 -p 6379 ping PONG # 方式2: # [root@localhost bin]# ./redis-cli -h 127.0.0.1 -p 6379 127.0.0.1:6379> keys * (empty list or set)
# 從節點1 redis-6380.conf 主要修改參數 port 6380 daemonize yes logfile "6380.log" dbfilename "dump-6380.rdb" slaveof 127.0.0.1 6379
# 從節點2 redis-6381.conf 主要修改參數 port 6381 daemonize yes logfile "6381.log" dbfilename "dump-6381.rdb" slaveof 127.0.0.1 6379
./redis-server redis-6380.conf
# ./redis-server redis-6381.conf
# [root@localhost bin]# ./redis-cli -p 6379 127.0.0.1:6379> info replication # Replication role:master #當前節點角色 connected_slaves:2 #從節點鏈接個數 slave0:ip=127.0.0.1,port=6380,state=online,offset=392,lag=1 #從節點鏈接信息 slave1:ip=127.0.0.1,port=6381,state=online,offset=392,lag=2 #從節點鏈接信息 master_replid:6bc06103642acba6430e01ec78ef18ada4736649 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:392 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:392
# port 26379 # sentinel monitor mymaster 127.0.0.1 6379 1
# ./redis-sentinel sentinel-26379.conf # 方法二, 使用redis-server命令加–sentinel參數: redis-server sentinel-26379.conf --sentinel
# [root@localhost bin]# redis-cli -h 127.0.0.1 -p 26379 info Sentinel # Sentinel sentinel_masters:1 sentinel_tilt:0 sentinel_running_scripts:0 sentinel_scripts_queue_length:0 sentinel_simulate_failure_flags:0 master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=1
# [root@localhost bin]# ./redis-cli shutdown
# 14549:X 24 Jul 15:44:44.568 # +vote-for-leader e31085285266ff86372eeeb4970c9a8de0471025 1 # 14549:X 24 Jul 15:44:44.604 # +sdown master mymaster 127.0.0.1 6379 # 14549:X 24 Jul 15:44:44.604 # +odown master mymaster 127.0.0.1 6379 #quorum 1/1 # 14549:X 24 Jul 15:44:44.604 # Next failover delay: I will not start a failover before Wed Jul 24 15:50:45 2019 # 14549:X 24 Jul 15:44:45.093 # +config-update-from sentinel e31085285266ff86372eeeb4970c9a8de0471025 127.0.0.1
26381 @ mymaster 127.0.0.1 6379 # 14549:X 24 Jul 15:44:45.093 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6381 # 14549:X 24 Jul 15:44:45.093 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6381 # 14549:X 24 Jul 15:44:45.093 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381 # 14549:X 24 Jul 15:45:15.127 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6381
127.0.0.1:6381> info replication # Replication role:slave ###6379節點正常是,6381爲從節點 master_host:127.0.0.1 master_port:6379 master_link_status:up master_last_io_seconds_ago:0 master_sync_in_progress:0 slave_repl_offset:166451 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:6bc06103642acba6430e01ec78ef18ada4736649 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:166451 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:253 repl_backlog_histlen:166199 127.0.0.1:6381> info replication # Replication role:master #執行shutdwon後成爲新的master節點 connected_slaves:1 slave0:ip=127.0.0.1,port=6380,state=online,offset=217098,lag=1 master_replid:e39de2323e3ab0ff0eff1347ad1c65e2bd3fd917 master_replid2:6bc06103642acba6430e01ec78ef18ada4736649 master_repl_offset:217098 second_repl_offset:172878 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:253 repl_backlog_histlen:216846
# Example sentinel.conf # 哨兵sentinel實例運行的端口 默認26379 port 26379 # 哨兵sentinel的工做目錄 dir /tmp # 哨兵sentinel監控的redis主節點的 ip port # master-name 能夠本身命名的主節點名字 只能由字母A-z、數字0-9 、這三個字符".-_"組成。 # quorum 配置多少個sentinel哨兵統一認爲master主節點失聯 那麼這時客觀上認爲主節點失聯了 # sentinel monitor <master-name> <ip> <redis-port> <quorum> sentinel monitor mymaster 127.0.0.1 6379 2 # 當在Redis實例中開啓了requirepass foobared 受權密碼 這樣全部鏈接Redis實例的客戶端都要提供密碼 # 設置哨兵sentinel 鏈接主從的密碼 注意必須爲主從設置同樣的驗證密碼 # sentinel auth-pass <master-name> <password> sentinel auth-pass mymaster MySUPER--secret-0123passw0rd # 指定多少毫秒以後 主節點沒有應答哨兵sentinel 此時 哨兵主觀上認爲主節點下線 默認30秒 # sentinel down-after-milliseconds <master-name> <milliseconds> sentinel down-after-milliseconds mymaster 30000 # 這個配置項指定了在發生failover主備切換時最多能夠有多少個slave同時對新的master進行 同步, 這個數字越小,完成failover所需的時間就越長, 可是若是這個數字越大,就意味着越 多的slave由於replication而不可用。 能夠經過將這個值設爲 1 來保證每次只有一個slave 處於不能處理命令請求的狀態。 # sentinel parallel-syncs <master-name> <numslaves> sentinel parallel-syncs mymaster 1 # 故障轉移的超時時間 failover-timeout 能夠用在如下這些方面: #1. 同一個sentinel對同一個master兩次failover之間的間隔時間。 #2. 當一個slave從一個錯誤的master那裏同步數據開始計算時間。直到slave被糾正爲向正確的master那裏同步數據時。 #3.當想要取消一個正在進行的failover所須要的時間。 #4.當進行failover時,配置全部slaves指向新的master所需的最大時間。不過,即便過了這個超時,slaves依然會被正確配置爲指向master,可是就不按parallel-syncs所配置的規則來了 # 默認三分鐘 # sentinel failover-timeout <master-name> <milliseconds> sentinel failover-timeout mymaster 180000 # SCRIPTS EXECUTION #配置當某一事件發生時所須要執行的腳本,能夠經過腳原本通知管理員,例如當系統運行不正常時發郵件通知相關人員。 #對於腳本的運行結果有如下規則: #若腳本執行後返回1,那麼該腳本稍後將會被再次執行,重複次數目前默認爲10 #若腳本執行後返回2,或者比2更高的一個返回值,腳本將不會重複執行。 #若是腳本在執行過程當中因爲收到系統中斷信號被終止了,則同返回值爲1時的行爲相同。 #一個腳本的最大執行時間爲60s,若是超過這個時間,腳本將會被一個SIGKILL信號終止,以後從新執行。 #通知型腳本:當sentinel有任何警告級別的事件發生時(好比說redis實例的主觀失效和客觀失效等等),將會去調用這個腳本, 這時這個腳本應該經過郵件,SMS等方式去通知系統管理員關於系統不正常運行的信息。調用該腳本時,將傳給腳本兩個參數, 一個是事件的類型, 一個是事件的描述。 若是sentinel.conf配置文件中配置了這個腳本路徑,那麼必須保證這個腳本存在於這個路徑,而且是可執行的,不然sentinel沒法正常啓動成功。 #通知腳本 # sentinel notification-script <master-name> <script-path> sentinel notification-script mymaster /var/redis/notify.sh # 客戶端從新配置主節點參數腳本 # 當一個master因爲failover而發生改變時,這個腳本將會被調用,通知相關的客戶端關於master地址已經發生改變的信息。 # 如下參數將會在調用腳本時傳給腳本: # <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port> # 目前<state>老是「failover」, # <role>是「leader」或者「observer」中的一個。 # 參數 from-ip, from-port, to-ip, to-port是用來和舊的master和新的master(即舊的slave)通訊的 # 這個腳本應該是通用的,能被屢次調用,不是針對性的。 # sentinel client-reconfig-script <master-name> <script-path> sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
添加:值到布隆過濾器
1)向布隆過濾器添加key,會使用 f、g、h hash函數對key算出一個整數索引,而後對長度取餘
2)每一個hash函數都會算出一個不一樣的位置,把算出的位置都設置成1就完成了布隆過濾器添加過程
查詢:布隆過濾器值
1)當查詢某個key時,先用hash函數算出一個整數索引,而後對長度取餘
2)當你有一個不爲1時確定不存在這個key,當所有都爲1時可能有這個key
3)這樣內存中的布隆過濾器過濾掉大量不存在的row請求,而後去再磁盤進行查詢,減小IO操做
刪除:不支持
1)目前咱們知道布隆過濾器能夠支持 add 和 isExist 操做
2)如何解決這個問題,答案是計數刪除,可是計數刪除須要存儲一個數值,而不是原先的 bit 位,會增大佔用的內存大小。
3)增長一個值就是將對應索引槽上存儲的值加一,刪除則是減一,判斷是否存在則是看值是否大於0。
# Multi 命令用於標記一個事務塊的開始事務塊內的多條命令會按照前後順序被放進一個隊列當中,最後由 EXEC 命令原子性( atomic )地執行 > multi(開始一個redis事物) incr books incr books > exec (執行事物) > discard (丟棄事物)
[root@redis ~]# redis-cli 127.0.0.1:6379> multi OK 127.0.0.1:6379> set test 123 QUEUED 127.0.0.1:6379> exec 1) OK 127.0.0.1:6379> get test "123" 127.0.0.1:6379> multi OK 127.0.0.1:6379> set test 456 QUEUED 127.0.0.1:6379> discard OK 127.0.0.1:6379> get test "123" 127.0.0.1:6379>
#定義ip host = 'localhost' #創建服務鏈接 r = redis.Redis(host=host) pipe = r.pipeline() #開啓事務 pipe.multi() #存儲子命令 pipe.set('key2', 4) #執行事務 pipe.execute() print(r.get('key2'))
watch key1 key2 ... : 監視一或多個key,若是在事務執行以前,被監視的key被其餘命令改動,則事務被打斷 ( 相似樂觀鎖 )
multi : 標記一個事務塊的開始( queued ) 事務塊內的多條命令會按照前後順序被放進一個隊列當中,最後由 EXEC 命令原子性( atomic )地執行
exec : 執行全部事務塊的命令 ( 一旦執行exec後,以前加的監控鎖都會被取消掉 )
discard : 取消事務,放棄事務塊中的全部命令
unwatch : 取消watch對全部key的監控
Redis事務的三個階段:
開始事務
Redis事務的開始是經過執行MULTI 命令來實現,它的做用是將執行該命令的客戶端從非事務狀態切換至事務狀態
命令入隊
當一個客戶端出於事務狀態時, 若是客戶端發送的命令是 EXEC(執行全部事務塊內的命令) 、DISCARD(取消事務,放棄執行事務塊內的全部命令。) 、 WATCH(監視任意數量的key ,提一下,在事務中執行這個命令會報錯:ERR WATCH inside MULTI is not allowed) 、 MULTI(標記一個事務塊的開始) 四個命令之外的其餘命令,那麼服務器並不當即執行這個命令,而是將這個命令放入一個事務隊列裏面, 而後向客戶端返回 QUEUED 回覆。
執行事務
當一個處於事務狀態的客戶端向服務器發送 EXEC 命令時, 這個 EXEC 命令將當即被服務器執行: 服務器會遍歷這個客戶端的事務隊列,執行隊列中保存的全部命令,最後將執行命令所得的結果所有返回給客戶端。(這裏須要說明的一點是,Redis在處理網絡請求的是單線程的,因此隊列中的命令在執行期間是不會被其餘客戶端命令插進來的。這一點對理解Redis事務很關鍵)
WATCH
用於事務開啓以前對任意數量的Key進行監視,若是這個被監視的key被改動(這裏提一下,這個改動,無論是刪除、添加、修改,或者A -> B -> A改回原值,都會被認爲發生了改動),那麼相應事務就被取消,不然事務正常執行。因此咱們能夠認爲 WATCH 是一個樂觀鎖。若是想讓key取消被監控,能夠用 UNWATCH 命令(這裏又要提一下,UNWATCH 若是在事務中執行,也是會被放到隊列裏的)。
# > setnx lock:codehole true # .... do something critical .... # > del lock:codehole
# > setnx lock:codehole true # > expire lock:codehole 5 # .... do something critical .... # > del lock:codehole
# > set lock:codehole true ex 5 nx # ''' do something ''' # > del lock:codehole
分佈式鎖舉例
分佈式鎖,是一種思想,它的實現方式有不少。好比,咱們將沙灘當作分佈式鎖的組件,那麼它看起來應該是這樣的:
加鎖
加鎖實際上就是在redis中,給Key鍵設置一個值,爲避免死鎖,並給定一個過時
在沙灘上踩一腳,留下本身的腳印,就對應了加鎖操做。其餘進程或者線程,看到沙灘上已經有腳印,證實鎖已被別人持有,則等待。
解鎖
解鎖的過程就是將Key鍵刪除。但也不能亂刪
把腳印從沙灘上抹去,就是解鎖的過程。
鎖超時
爲了不死鎖,咱們能夠設置一陣風,在單位時間後颳起,將腳印自動抹去。
對於分佈式鎖,注意的
能夠保證在分佈式部署的應用集羣中,同一個方法在同一時間只能被一臺機器上的一個線程執行這把鎖要是一把可重入鎖(避免死鎖)這把鎖最好是一把阻塞鎖有高可用的獲取鎖和釋放鎖功能獲取鎖和釋放鎖的性能要好
若是查詢數據庫也爲空,直接設置一個默認值存放到緩存,這樣第二次到緩衝中獲取就有值了,而不會繼續訪問數據庫。設置一個過時時間或者當有值的時候將緩存中的值替換掉便可。能夠給key設置一些格式規則,而後查詢以前先過濾掉不符合規則的Key。
public String getByKey(String keyA,String keyB) { String value = redisService.get(keyA); if (StringUtil.isEmpty(value)) { value = redisService.get(keyB); String newValue = getFromDbById(); redisService.set(keyA,newValue,31, TimeUnit.DAYS); redisService.set(keyB,newValue); } return value; }
public String getWithLock(String key, Jedis jedis, String lockKey, String uniqueId, long expireTime) { // 經過key獲取value String value = redisService.get(key); if (StringUtil.isEmpty(value)) { // 分佈式鎖,詳細能夠參考https://blog.csdn.net/fanrenxiang/article/details/79803037 //封裝的tryDistributedLock包括setnx和expire兩個功能,在低版本的redis中不支持 try { boolean locked = redisService.tryDistributedLock(jedis, lockKey, uniqueId, expireTime); if (locked) { value = userService.getById(key); redisService.set(key, value); redisService.del(lockKey); return value; } else { // 其它線程進來了沒獲取到鎖便等待50ms後重試 Thread.sleep(50); getWithLock(key, jedis, lockKey, uniqueId, expireTime); } } catch (Exception e) { log.error("getWithLock exception=" + e); return value; } finally { redisService.releaseDistributedLock(jedis, lockKey, uniqueId); } } return value; }
<dependencies> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>23.0</version> </dependency> </dependencies>
public class BloomFilterTest { private static final int capacity = 1000000; private static final int key = 999998; private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), capacity); static { for (int i = 0; i < capacity; i++) { bloomFilter.put(i); } } public static void main(String[] args) { /*返回計算機最精確的時間,單位微妙*/ long start = System.nanoTime(); if (bloomFilter.mightContain(key)) { System.out.println("成功過濾到" + key); } long end = System.nanoTime(); System.out.println("布隆過濾器消耗時間:" + (end - start)); int sum = 0; for (int i = capacity + 20000; i < capacity + 30000; i++) { if (bloomFilter.mightContain(i)) { sum = sum + 1; } } System.out.println("錯判率爲:" + sum); } } # 成功過濾到999998 # 布隆過濾器消耗時間:215518 # 錯判率爲:318
public static <T> BloomFilter<T> create(Funnel<T> funnel, int expectedInsertions /* n */) { return create(funnel, expectedInsertions, 0.03); // FYI, for 3%, we always get 5 hash functions }
# http://www.javashuo.com/article/p-cmxicfey-du.html:詳細操做
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd"> <!-- 鏈接池配置 --> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <!-- 最大鏈接數 --> <property name="maxTotal" value="30" /> <!-- 最大空閒鏈接數 --> <property name="maxIdle" value="10" /> <!-- 每次釋放鏈接的最大數目 --> <property name="numTestsPerEvictionRun" value="1024" /> <!-- 釋放鏈接的掃描間隔(毫秒) --> <property name="timeBetweenEvictionRunsMillis" value="30000" /> <!-- 鏈接最小空閒時間 --> <property name="minEvictableIdleTimeMillis" value="1800000" /> <!-- 鏈接空閒多久後釋放, 當空閒時間>該值 且 空閒鏈接>最大空閒鏈接數 時直接釋放 --> <property name="softMinEvictableIdleTimeMillis" value="10000" /> <!-- 獲取鏈接時的最大等待毫秒數,小於零:阻塞不肯定的時間,默認-1 --> <property name="maxWaitMillis" value="1500" /> <!-- 在獲取鏈接的時候檢查有效性, 默認false --> <property name="testOnBorrow" value="true" /> <!-- 在空閒時檢查有效性, 默認false --> <property name="testWhileIdle" value="true" /> <!-- 鏈接耗盡時是否阻塞, false報異常,ture阻塞直到超時, 默認true --> <property name="blockWhenExhausted" value="false" /> </bean> <!-- jedis客戶端單機版 --> <bean id="redisClient" class="redis.clients.jedis.JedisPool"> <constructor-arg name="host" value="192.168.146.131"></constructor-arg> <constructor-arg name="port" value="6379"></constructor-arg> <constructor-arg name="poolConfig" ref="jedisPoolConfig"></constructor-arg> </bean> <bean id="jedisClient" class="com.taotao.rest.dao.impl.JedisClientSingle"/> <!-- jedis集羣版配置 --> <!-- <bean id="redisClient" class="redis.clients.jedis.JedisCluster"> <constructor-arg name="nodes"> <set> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.25.153"></constructor-arg> <constructor-arg name="port" value="7001"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.25.153"></constructor-arg> <constructor-arg name="port" value="7002"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.25.153"></constructor-arg> <constructor-arg name="port" value="7003"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.25.153"></constructor-arg> <constructor-arg name="port" value="7004"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.25.153"></constructor-arg> <constructor-arg name="port" value="7005"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.25.153"></constructor-arg> <constructor-arg name="port" value="7006"></constructor-arg> </bean> </set> </constructor-arg> <constructor-arg name="poolConfig" ref="jedisPoolConfig"></constructor-arg> </bean> <bean id="jedisClientCluster" class="com.taotao.rest.dao.impl.JedisClientCluster"></bean> --> </beans>
package com.taotao.rest.dao.impl; import org.springframework.beans.factory.annotation.Autowired; import com.taotao.rest.dao.JedisClient; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; public class JedisClientSingle implements JedisClient{ @Autowired private JedisPool jedisPool; @Override public String get(String key) { Jedis jedis = jedisPool.getResource(); String string = jedis.get(key); jedis.close(); return string; } @Override public String set(String key, String value) { Jedis jedis = jedisPool.getResource(); String string = jedis.set(key, value); jedis.close(); return string; } @Override public String hget(String hkey, String key) { Jedis jedis = jedisPool.getResource(); String string = jedis.hget(hkey, key); jedis.close(); return string; } @Override public long hset(String hkey, String key, String value) { Jedis jedis = jedisPool.getResource(); Long result = jedis.hset(hkey, key, value); jedis.close(); return result; } @Override public long incr(String key) { Jedis jedis = jedisPool.getResource(); Long result = jedis.incr(key); jedis.close(); return result; } @Override public long expire(String key, int second) { Jedis jedis = jedisPool.getResource(); Long result = jedis.expire(key, second); jedis.close(); return result; } @Override public long ttl(String key) { Jedis jedis = jedisPool.getResource(); Long result = jedis.ttl(key); jedis.close(); return result; } @Override public long del(String key) { Jedis jedis = jedisPool.getResource(); Long result = jedis.del(key); jedis.close(); return result; } @Override public long hdel(String hkey, String key) { Jedis jedis = jedisPool.getResource(); Long result = jedis.hdel(hkey, key); jedis.close(); return result; } }
package com.taotao.rest.dao.impl; import org.springframework.beans.factory.annotation.Autowired; import com.taotao.rest.dao.JedisClient; import redis.clients.jedis.JedisCluster; public class JedisClientCluster implements JedisClient { @Autowired private JedisCluster jedisCluster; @Override public String get(String key) { return jedisCluster.get(key); } @Override public String set(String key, String value) { return jedisCluster.set(key, value); } @Override public String hget(String hkey, String key) { return jedisCluster.hget(hkey, key); } @Override public long hset(String hkey, String key, String value) { return jedisCluster.hset(hkey, key, value); } @Override public long incr(String key) { return jedisCluster.incr(key); } @Override public long expire(String key, int second) { return jedisCluster.expire(key, second); } @Override public long ttl(String key) { return jedisCluster.ttl(key); } @Override public long del(String key) { return jedisCluster.del(key); } @Override public long hdel(String hkey, String key) { return jedisCluster.hdel(hkey, key); } }
package com.taotao.rest.service.impl; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.taotao.commonEntity.JsonUtils; import com.taotao.commonEntity.TaotaoResult; import com.taotao.mapper.TbContentMapper; import com.taotao.pojo.TbContent; import com.taotao.pojo.TbContentExample; import com.taotao.pojo.TbContentExample.Criteria; import com.taotao.rest.dao.JedisClient; import com.taotao.rest.service.ContentService; import redis.clients.jedis.Jedis; //首頁大廣告位的獲取服務層信息 @Service public class ContentServiceImpl implements ContentService { @Value("${CONTENTCATEGORYID}") private String CONTENTCATEGORYID; @Autowired private TbContentMapper contentMapper; @Autowired private JedisClient jedisClient; @Override public List<TbContent> getContentList(Long categoryId) { /*通常第一次訪問的時候先從數據庫讀取數據,而後將數據寫入到緩存,再次訪問同一內容的時候就從緩存中讀取,若是緩存中沒有則從數據庫中讀取 因此咱們添加緩存邏輯的時候,從數據庫中將內容讀取出來以後,先set入緩存,而後再從緩存中添加讀取行爲,若是緩存爲空則從數據庫中進行讀取 */ //從緩存中獲取值 String getData = jedisClient.hget(CONTENTCATEGORYID, categoryId+""); if (!StringUtils.isBlank(getData)) { List<TbContent> resultList= JsonUtils.jsonToList(getData, TbContent.class); return resultList; } TbContentExample example=new TbContentExample(); Criteria criteria = example.createCriteria(); criteria.andCategoryIdEqualTo(categoryId); List<TbContent> list = contentMapper.selectByExample(example); //向緩存中放入值 String jsonData = JsonUtils.objectToJson(list); jedisClient.hset(CONTENTCATEGORYID, categoryId+"",jsonData); return list; } }
終於結束了,但願能幫助你們,多多支持,關注不迷路哦!!!