原文出處:https://juejin.im/post/5dc4c5ef5188252af967afd7
redis 是什么
Redis是開源的 內存中的數據結構存儲系統,它可以用作 數據庫、 緩存和 消息中間件。 它支持多種類型的數據結構,如 字符串strings, 散列hashes, 列表lists, 集合sets, 有序集合sorted sets與范圍查詢, bitmaps, hyperloglogs 和 地理空間(geospatial) 索引半徑查詢。
Redis還內置了 復制(replication),LUA腳本(Lua ing), LRU驅動事件(LRU eviction),事務(transactions) 和不同級別的 磁盤持久化(persistence), 并通過 Redis哨兵(Sentinel)和自動 分區(Cluster)提供高可用性(high availability)。
嗯,沒錯!這就是 redis中文官方網站上面的介紹,簡潔明了。
NoSQL 是什么
我們知道 redis是一種非關系型數據庫 NoSQL。而為什么出現 NoSQL?NoSQL又是什么呢?
單機數據庫的年代
在一個網站訪問量不大的時候,我們使用一個數據庫就足以應對流量請求。
緩存 + 拆分
隨著訪問量的上升,一個數據庫已經不能滿足我們的需求了。為了更高的性能,我們在中間加上了一個緩存層并且將數據庫做了集群、結構優化和讀寫分離。
而這里的緩存就是 NoSQL,當然做緩存也只是 NoSQL的一種功能,就像 Redis并不僅僅有緩存這一種功能。比如它還能實現 簡單的消息隊列, 解決Session共享, 計數器, 排行榜, 好友關系處理等等功能,可見 Redis是一個非常強大工具,讓我們來學習它吧!
Redis 通用命令
首先我們拋開數據類型來講關于 Redis的通用命令。
操作 key 和 value
Redis是一種 keyvalue存儲的緩存數據庫,所有的數據都有一個自己唯一的key。
這里為了方便演示,我使用了字符串相關的設置命令
- keys [pattern] 獲取符合要求的所有key。 時間復雜度為 O(n) ,一般在生產環境中不使用,因為 Redis 是 單線程 的, 執行耗時的任務會阻塞其他任務 。 一般會使用 scan 命令替代(非阻塞)。
- dbsize 獲取當前存儲數據個數。
- exists key 判斷是否存在該key
- del key 刪除指定數據
- type key 獲取指定key的數據類型
- rename key newkey 重命名
過期時間
Redis中很多數據都是用來作為 緩存數據的,而作為緩存就需要有過期時間,在 Redis中提供了很強大的 過期時間設置功能。
- expire key seconds 為某個 key 設置過期時間。
- ttl key 查看某個 key 的剩余時間,返回正數代表剩余的時間,-1代表永久,-2代表已過期或不存在。
Redis 的五種基本數據類型
在上面我說到了很多 Redis作為緩存能實現的其他功能,比如計數器,排行榜,好友關系等,這些實現的依據就是靠著 Redis的數據結構。在整個 Redis中一共有五種基本的數據結構(還有些高級數據結構以后會講),他們分別是 字符串strings, 散列hashes, 列表lists, 集合sets, 有序集合sorted sets。
字符串 string
在絕大部分編程語言中都有 String字符串類型,對于作為數據庫的 Redis也是必不可少的。
- set key value 設置值
- get key 獲取某個key的值
- mset key1 value1 key2 value2 批量設置并且是 原子 的,可以用來減少網絡時間消耗
- mget key1 key2 批量獲取并且是 原子 的,可以用來減少網絡時間消耗
- incr key 自增指定key的值
- decr key 自減指定key的值
- incrby key value 自增指定數值
- decrby key value 自減指定數值
- incrbyfloat key floatvalue 增加指定浮點數 前面幾個操作就可以用來實現 計數器 的功能。
- setnx key value 如果不存在該key則可以設置成功,否則會失敗, 加上過期時間限制,則是redis實現分布式鎖的一種方式 (后面會提到) 。
- set key value xx 與前面相反,如果存在則設置成功,否則失敗(相當于更新操作)
- getset key newvalue 設置新值并返回舊值
- Append key value 為原本內容追加內容
- strlen key 獲取字符串長度
- getrange key start end 獲取指定范圍的內容
- setrange key index value 設置指定范圍的內容
- setex key seconds value 設置值且設置過期時間
- set key value ex seconds nx 為不存在的key設置值且設置過期時間, 分布式鎖的實現方式 。
hash
其實我們可以理解 hash為 小型Redis, Redis在底層實現上和 JAVA中的 HashMap差不多,都是使用 數組 + 鏈表的二維結構實現的。
不同的是,在 Redis中字典的值 只能是字符串,而且他們 rehash的方式不一樣,在 Redis中使用的是 漸進式rehash。
在 rehash 的時候會保留新舊兩個 hash 字典,在數據遷移的時候會將舊字典中的內容一點一點遷移到新字典中,查詢的同時會查詢兩個 hash 字典,等數據全部遷移完成才會將新字典代替就字典。
下面我們來看一下關于 hash的基本操作。
- hset key field value 設置字典中某個key的值
- hsetnx key field value 設置字典中某個key的值(不存在的)
- hmset key field1 value1 field2 value2 ... 批量設置
- hget key field 獲取字典中某個key的值
- hmget key field1 field2 批量獲取
- hgetall key 獲取全部
- hdel key field 刪除某個key
- hexists key field 判斷是否存在
- hlen key 獲取指定key對應的字典中的存儲個數
- hvals key 返回所有的value
- hkeys key 返回所有的key
- hincrby key field increValue 增加某個value的值(也可以增加負數)
- hincrbyfloat key field floatValue 增加某個value的值(浮點數)
list
Redis中的列表相當于 Java中的 LinkedList(雙向鏈表) ,也就是底層是通過 鏈表來實現的,所以對于 list來說 插入刪除操作很快,但 索引定位非常慢。
Redis提供了許多對于 list的操作,如 出, 入等操作,你可以充分利用它們來實現一個 棧或者 隊列。
下面我們來看一下關于 list的基本操作。
- lpush key item1 item2 item3... 從左入棧
- rpush key item1 item2 item3... 從右入棧
- lpop key 從左出棧
- rpop key 從右出棧
- lindex key index 獲取指定索引的元素 O(n)謹慎使用
- lrange key start end 獲取指定范圍的元素 O(n)謹慎使用
- linsert key before|after item newitem 在指定元素的前面或者后面添加新元素
- lrem key count value 刪除指定個數值為value的元素
- count = 0 :刪除所有值為value的元素
- count > 0 :從左到右刪除 count 個值為 value 的元素
- count < 0 :從右到做刪除 |count| 個值為 value 的元素
- ltrim key start end 保留指定范圍的元素
- lset key index newValue 更新某個索引的值
- blpop key timeout 沒有則阻塞(timeout指定阻塞時間 為0代表永久)
- brpop key timeout 沒有則阻塞(timeout指定阻塞時間 為0代表永久) 這兩個可以用來實現 消費者生產者
總結來說我們可以使用 左入又出或者右入左出 來實現隊列,左入左出或者右入右出 來實現棧。
- lpush + lpop = Stack
- rpush + rpop = Stack **
- lpush + rpop = Queue
- rpush + lpop = Queue
- lpush/rpush + ltrim = Capped List (定長列表)
- lpush + brpop = Message Queue (消息隊列)
- rpush + blpop = Message Queue (消息隊列)
set
Redis中的 set相當于 Java中的 HashSet(無序集合),其中里面的元素 不可以重復,我們可以利用它實現一些去重的功能。 我們還有對幾個集合進行 取交集, 取并集等操作,這些操作就可以獲取不同用戶之間的共同好友,共同愛好等等。
下面我們就來看一下關于 set的一些基本操作。
- sadd key value 添加元素
- sdel key value 刪除某個元素
- sismember key value 判斷是否是集合中的元素
- srandmember key count 隨機獲取指定個數的元素(不會影響集合結構)
- spop key count 從集合中隨機彈出元素(會破壞結合結構)
- smembers key 獲取集合所有元素 O(n)復雜度
- scard key 獲取集合個數
- sinter set1 set2 ... 獲取所有集合中的交集
- sdiff set1 set2 ... 獲取所有集合中的差集
- sunion set1 set2 ... 獲取所有集合中的并集
zset
Redis中的 zset是一個 有序集合,通過它可以實現很多有意思的功能,比如學生成績排行榜,視頻播放量排行榜等等。
zset中是使用 跳表來實現的,我們知道只有數組這種連續的空間才能使用 二分查找進行快速的定位,而鏈表是不可以的。 跳表幫助鏈表查找的時候節省了很多時間(使用跳的方式來遍歷索引來進行有序插入),如果不了解跳表的同學可以補習一下。
下面我們來看一下關于 zset的一些基本操作。
- zadd key score element 添加,score用于排序,value需要唯一,由于使用的跳表,時間復雜度為 O(logn)。
- zrem key element 刪除某元素 O(1)時間復雜度
- zscore key element 獲取某個元素的分數
- zincrby key incrScore element 增加某個元素的分數
- zrange key start end [withscores] 獲取指定索引范圍的元素 加上 withscores 則返回分數 O(logn + m)時間復雜度
- zrangebyscore key minScore maxScore [withscores] 獲取指定分數范圍的元素 加上 withscores 則返回分數, O(logn + m)時間復雜度
- zcard key 獲取有序集合長度
Redis 中的事務和管道
管道 Pipeline
在某些場景下我們在 一次操作中可能需要執行多個命令,而如果我們只是一個命令一個命令去執行則會浪費很多網絡消耗時間,如果將命令一次性傳輸到 Redis中去再執行,則會減少很多開銷時間。但是需要注意的是 pipeline中的命令并不是原子性執行的,也就是說管道中的命令到達 Redis服務器的時候可能會被其他的命令穿插。
事務
關系型數據庫具有 ACID特性,Redis能保證A(原子性)和I(隔離性),D(持久性)看是否有配置 RDB或者 AOF持久化操作,但無法保證一致性,因為 Redis 事務不支持回滾 。
我們可以簡單理解為 Redis中的事務只是比 Pipeline多了個原子性操作,也就是不會被其他命令給分割,如上圖。
- multi 事務開始的標志
- exec 事務執行
- discard 清除在這個事務中放入隊列的所有命令,即解除整個事務。
- watch key 在事務開始前監控某個元素,如果在提交事務的時候發現這個元素的值被其他客戶端更改了則事務會運行失敗。
- unwatch key 解除監控
(試了好幾次,圖片傳上來還是不清晰,需要的朋友就加個人微信:fangzhen0219,我私發吧)
好了,這就是這篇文章全部的內容了,對于 Redis你還有多少遺忘的或者沒學習到的呢?歡迎留言區評論