Redis 單線程高性能,它所有的數據都在內存中,所有的運算都是內存級別的運算,而且單線程避免了多線程的切換性能損耗問題。redis利用epoll來實現IO多路復用,將連接信息和事件放到隊列中,依次放到文件事件分派器,事件分派器將事件分發給事件處理器。
1.Redis數據結構及簡單操作指令、應用場景
String、list、set、hash、zset(有序set)
總體來說redis都是通過Key-Value的形式來存儲數據的。只是不用數據類型Value的形式不同。
String:最簡單數據結構,比如我們將一個對象轉成json串來存儲
set key value 存放數據
get key 獲取數據
exists key 查看數據是否存在,存在返回1否則0
del key 刪除數據 返回操作成功的條數
mset key1 value1 key2 value2 key3 value3...存放多組數據
mget key1 ke2y key3... 獲取多個key的數據,返回一個集合,類似Map的values方法
expire key second 設置key 過期時間,單位秒
expire key second 設置key 過期時間,單位秒
setex key second value設置key 過期時間,單位秒(等價於先set,再expire)
setnx key value 如果key不存在就set 返回1.如果存在返回0(可以基於此實現分布式鎖)
應用場景:
1.普通的單值存儲key-value
2.分布式鎖 setnx key value
3.計數器 incr key 原子加,比如統計文章的閱讀量
4.分布式seesion
List:並不是java里面的list,redis的list更像一個鏈表或者說隊列/棧的結構。這就意味着它的刪除插入快,但是通過索引定位就比較慢了。當列表彈出了最后一個元素之后,該數據結構自動被刪除,內存被回收。
Redis 的列表結構可以來做異步隊列使用。將需要延后處理的任務塞進 Redis 的列表,另一個線程從這個列表中輪詢數據進行處理。list的索引有正向索引和反向所以,正向索引以0開始,反向索引以-1結束。
rpush key value1 value2 value3... 插入list數據
llen key 查看長度
lpop key 按加入順序獲取(先進先出,類似隊列)
rpop key 后進先出,有點類似棧
lrange key start stop 查看此索引訪問內的數據,可以使用正向索引或者反向索引,這個操作不會彈出數據。
blpop key timeout 從左邊彈出一個元素如果沒有則阻塞等待timeout秒,如果timeout=0表示一直阻塞等待
brpop key timeout 同上從右邊彈出一個元素
列表取數據,使用pop取完后整個列表都被回收了,就是說只能取一次數據。
應用場景
1.常用數據結構模擬:
棧:lpush + lpop 后進先出
隊列:lpush + rpop 先進先出
阻塞隊列:lpush + brpop
2.訂閱消息推送:A用戶關注B公眾號,B發布文章的時候就往一個關於A的key里面寫入文章關鍵信息,lpush B:A:msg 文章ID,每次A進入訂閱信息的時候:lrange B:A:msg 0 4 就可以看到最新的5條消息
3.
Hash:類似java的hashMap,和字符串相比,我們存儲數據的時候可以只存儲對象的部分屬性,而字符串則需要完整將整個對象轉換。當然hash存儲結構的消耗肯定是高於字符串的,缺點只能對rediskey進行一些屬性設置操作,不能操作設置里面hashkey,比如設置過期時間這些,只能是針對rediskey。
hset redisKey hashKey1 value1
hset redisKey hashKey2 value2 插入數據
hgetall redisKey 獲取數據,key value間隔出現
hlen redisKey 查看hash長度
hget redisKey hashKey 獲取hashKey 對應的value
hmset redisKey hashKey1 value1 hashKey2 value2 hashKey2 value3 批量插入值
應用場景
1.購物車實現:
添加商品:hset cart:用戶ID 商品ID 數量
增加數量:hincrby cart:用戶ID 商品ID 數量
商品總數:hlen cart:用戶ID
刪除商品:hdel cart:用戶ID 商品ID
獲取購物車所有商品:hgetall cart:用戶ID
Set : 類似HashSet無序,
sadd key value
sadd key value1 value2 批量添加
smembers key 查看所有
sismember key value 查詢某個值是否存在,存在返回1
scard key 查看大小
spop key 獲取一個元素 最后一個數據取完之后,該結構會被清理,無法再次獲取數據
srandmember key count 從集合中選出幾個元素,此操作不會刪除數據
sinter key... 交集運算,求出多個集合的交集
sinterstore newkey key... 將交集結果存入newkey中
sunion key... 求並集
sunionstore newkey key... 將並集結果存入新key中
sdiff key... 求差集
sdiffstore newkey key... 將差集結果存入新key中
應用場景
1.隨機抽獎活動:將參與者放入set中,抽獎時如果可以重復參與一二三等獎,就用srandmember key count ,如果不能重復參與就世界pop出去。
2.紅包瓜分,參考隨機瓜分百萬紅包,這里面其實應該用set,文章中用的list,因為是在代碼層面做了一次隨機性。
3.微博的關注模型:可以使用set的交,並,差操作來實現。
Zset:帶一個分值大小的set集合,基於大小排序的集合。
zadd key score value 往有序集合key中加入帶分值score的元素
zrem key value 從有序集合key中刪除元素
zscore key value 返回有序集合key中元素value的分值
zincrby key increment value 為有序集合key中元素value的分值加上increment
zcard key 返回有序集合key中元素個數
zrange key start stop 正序獲取有序集合key從start下標到stop下標的元素(從小到大)
zrevrange key start stop 倒序獲取有序集合key從start下標到stop下標的元素(從大到小)
zunionstore newkey numkeys key... 並集計算將多個key求並集 如果value一樣的則會將score相加 得到一個新的zet,numkeys是后面集合數量
zinterstore newkey numkeys key... 交集計算,同樣會將其score相加
應用場景:
1.排行榜,投票排名等需要排序的場景
2.一周排行數據匯總,zunionstore或者zinterstore
原子計數操作
如果value是整數的話還可以實現自增操作(也可以用於實現分布式鎖,該增長有限,最大到long max,超過該值會直接報錯)
incr key 自增 如果key不存在默認從0自增1
incrby key step 設置增加步長step
2.redis持久化
雖然說redis都是內存級別的操作,其實也是有持久化的。
一種是基於RDB快照,
Redis 將內存數據庫快照保存在名字為 dump.rdb 的二進制文件中。
可以對 Redis 進行設置, 讓它在N 秒內數據集至少有 M 個改動這一條件被滿足時, 自動保存一次數據集。
也可以執行sava或者bgsave指令手動生成快照。save是同步的會阻塞其他指令執行,bgsave是另起線程去生成快照,不會阻塞客戶端指令。
另一種是AOF(append-only file)
快照並不可靠,上次快照之后,還未到達下一次快照條件時,這時候服務出現了問題,那么這期間的數據是無法保存到快照版本中的。這個時候就需要AOF了,它將每一條指令都記錄進文件,當redis重啟的時候,重新執行這個文件里面指令,就可以恢復所有的數據到內存中了。
可以通過配置appendonly yes 來開啟AOF,默認是關閉的
AOF也有三種同步數據的策略,
每次有操作都去刷新文件,很慢,但安全
每秒同步刷新一次:可能會丟失一秒內的數據
從不同步同步刷新:讓操作系統在需要的時候刷新數據,不安全
默認的是每秒刷一次
這樣一來可能AOF中有很多沒用的記錄,比如多次set 其實只有最后一次的數據才是有效的,前面全是無效的,保存下來,既占用磁盤,執行的時候也耗費時間,這個時候就有AOF重寫。AOF重寫是啥呢,其實就是整理我們的執行指令,前面那些本來會被覆蓋的指令就不需要再記錄了,只保留最后一次修改的指令就可以了,這樣一來文件就可以小很多,既省了磁盤,后續恢復數據執行起來也快。
有兩個參數:
混合持久化:
RDB快照數據恢復速度快,但是可能會有大量數據丟失,所以通常恢復數據還是用的AOF日志重放,但是AOF相對來說速度會很慢,尤其是在數據量大的時候。因此在4.0的時候帶來了混合持久化,也就是AOF在刷新的時候,先記錄上次的快照版本,然后記錄上次快照版本到現在的增量操作,然后合並成一個文件,覆蓋原來的appendonly.aof文件。Redis重啟的時候,先加載RDB快照的內容,在重放AOF日志中增量操作的內容就可以了。
開啟混合持久化:aof-use-rdb-preamble yes
混合持久化中appendonly.aof內容格式,一部分是RDB文件內容格式,另外的才是AOF文件的內容格式。
3.緩存淘汰策略:
當 Redis 內存超出物理內存限制時,內存的數據會開始和磁盤產生頻繁的交換 。會讓 Redis 的性能急劇下降,對於訪問量比較頻繁的 Redis 來說,這樣存取效率基本上等於不可用。
在生產環境中我們是不允許 Redis 出現交換行為的,為了限制最大使用內存,Redis 提供了配置參數 maxmemory 來限制內存超出期望大小。
當實際內存超出 maxmemory 時,Redis 提供了幾種可選策略 (maxmemory-policy) 來讓用戶自己決定該如何騰出新的空間以繼續提供讀寫服務。
maxmemory <bytes> # MAXMEMORY POLICY: how Redis will select what to remove when maxmemory # is reached. You can select among five behaviors: # # volatile-lru -> Evict using approximated LRU among the keys with an expire set. # allkeys-lru -> Evict any key using approximated LRU. # volatile-lfu -> Evict using approximated LFU among the keys with an expire set. # allkeys-lfu -> Evict any key using approximated LFU. # volatile-random -> Remove a random key among the ones with an expire set. # allkeys-random -> Remove a random key, any key. # volatile-ttl -> Remove the key with the nearest expire time (minor TTL) # noeviction -> Don't evict anything, just return an error on write operations. # # LRU means Least Recently Used # LFU means Least Frequently Used # # Both LRU, LFU and volatile-ttl are implemented using approximated # randomized algorithms. # # Note: with any of the above policies, Redis will return an error on write # operations, when there are no suitable keys for eviction. # # At the date of writing these commands are: set setnx setex append # incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd # sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby # zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby # getset mset msetnx exec sort # # The default is: # # maxmemory-policy noeviction
noeviction 不會繼續處理寫請求 (del,read請求可以繼續進行)。這樣可以保證不會丟失數據,但是會讓線上的寫相關的業務不能持續進行。這是默認的淘汰策略。
volatile-lru 嘗試淘汰設置了過期時間的 key,最少使用的 key 優先被淘汰。沒有設置過期時間的 key 不會被淘汰,這樣可以保證需要持久化的數據不會突然丟失。
volatile-ttl 跟上面一樣,除了淘汰的策略不是 LRU,而是 key 的剩余壽命 ttl 的值,ttl 越小越優先被淘汰。
volatile-random 跟上面一樣,不過淘汰的 key 是過期 key 集合中隨機的 key。
allkeys-lru 區別於 volatile-lru,這個策略要淘汰的 key 對象是全體的 key 集合,而不只是過期的 key 集合。這意味着沒有設置過期時間的 key 也會被淘汰。
allkeys-random 跟上面一樣,不過淘汰的策略是隨機的 key。
volatile-xxx 策略只會針對帶過期時間的 key 進行淘汰,allkeys-xxx 策略會對所有的 key 進行淘汰。如果你只是拿 Redis 做緩存,那應該使用 allkeys-xxx,客戶端寫緩存時不必攜帶過期時間。如果你還想同時使用 Redis 的持久化功能,那就使用 volatile-xxx 策略,這樣可以保留沒有設置過期時間的 key,它們是永久的 key 不會被 LRU 算法淘汰。