從零開始學Redis之金剛凡境

前言

文本已收錄至我的GitHub倉庫,歡迎Star:https://github.com/bin392328206/six-finger
種一棵樹最好的時間是十年前,其次是現在
我知道很多人不玩qq了,但是懷舊一下,歡迎加入六脈神劍Java菜鳥學習羣,羣聊號碼:549684836 鼓勵大家在技術的路上寫博客

絮叨

從今天開始把Redis好好的從零開始好好的學一下,雖然再工作上也有用Redis
但是對於我來說可能對Redis理解不深,剛好我們小組讓我分享點啥,我就選擇了redis
希望每個讀者看了我的redis系列都能有所收穫
Tips 金剛凡境 是我比較喜歡的一部動漫一樣《少年歌行》裏面武功的一種境界
讓我們再Redis的境界上 打怪升級吧

1 認識一下Redis

首先,肯定是去官網看看官方是怎麼介紹Redis的啦。https://redis.io/topics/introduction

Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message
broker. It supports data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps,
hyperloglogs, geospatial indexes with radius queries and streams. Redis has built-in replication, Lua scripting, LRU
eviction, transactions and different levels of on-disk persistence, and provides high availability via Redis
Sentinel and automatic partitioning with Redis Cluster. Learn more →

嗚嗚嗚、對於英文不好的我只能用谷歌看中文文檔了

Redis是一個開放源代碼(BSD許可)的內存中數據結構存儲,用作數據庫,緩存和消息代理。它支持數據結構,例如字符串,哈希,列表,集合,帶範圍查詢的排序集合,位圖,超日誌,帶有半徑查詢和流的地理空間索引。Redis具有內置的複製,Lua腳本LRU逐出事務和不同級別的磁盤持久性,並通過Redis SentinelRedis Cluster自動分區提供了高可用性。 瞭解更多→

一堆不認識的技術 不過我們應該能從上總結出一下幾點:

  • Redis 可以當數據庫使用
  • Redis 可以做緩存
  • Redis 可以做消息代理

1.1 爲什麼用Redis

從上面可以知道,Redis是基於內存,常用作於緩存的一種技術,並且Redis存儲的方式是以key-value的形式。
其實在開發過程中用的最多的還是把Redis當做緩存,因爲傳統的關係型數據庫已經不能適用所有的場景了,比如秒殺的庫存,首頁商品的高訪問量,都很容易把數據庫弄死,

瞭解一下什麼是緩存
緩存分爲本地緩存和分佈式緩存。以java爲例,使用自帶的map或者guava實現的是本地緩存,
使用redis或memcached之類的稱爲分佈式緩存,在多實例的情況下,各實例共用一份緩存數據,緩存具有一致性。

區別:

  • Java實現的Map是本地緩存,如果有多臺實例(機器)的話,每個實例都需要各自保存一份緩存,緩存不具有一致性
  • Redis實現的是分佈式緩存,如果有多臺實例(機器)的話,每個實例都共享一份緩存,緩存具有一致性。
  • Java實現的Map不是專業做緩存的,JVM內存太大容易掛掉的。一般用做於容器來存儲臨時數據,緩存的數據隨着JVM銷燬而結束。Map所存儲的數據結構,緩存過期機制等等是需要程序員自己手寫的。
  • Redis是專業做緩存的,可以用幾十個G內存來做緩存。Redis一般用作於緩存,可以將緩存數據保存在硬盤中,Redis重啓了後可以將其恢復。原生提供豐富的數據結構、緩存過期機制等等簡單好用的功能。

所以說專業的事還是交給專業的人來做吧

2 Redis的下載安裝

菜鳥教程

3 Redis的基本操作Api

在這裏我就不統一講了,雖然我們工作都是用封裝的,但是我覺得我們有必要花個1, 2小時跟着官方文檔敲一遍
重要哦,雖然博主已經在工作上用上了redis,但是博主自己寫博客也是花時間敲了一遍的大部分的命令 偷懶了沒有敲完)

下面是官方文檔(中文版的並且每一個操作都有例子和時間複雜度分析 超級贊):
Redis 命令

4 Redis的基本數據結構

這邊用基礎數據結構和高級數據結構來系統的講解一下(ps很多高級用法我也是現學的 嘻嘻)

4.1 String:

String 類型再工作當中我們用的是比較多的,它的內部是通過 **SDS(Simple Dynamic String)**來存儲的。
SDS 類似於 Java 中的 ArrayList,可以通過預分配冗餘空間的方式來減少內存的頻繁分配。當len小於IMB(1024*1024)時增加字符串分配空間大小爲原來的2倍,當len大於等於1M時每次分配 額外多分配1M的空間。

這是最簡單的類型,就是普通的 set 和 get,做簡單的 KV 緩存。但是很多時候我也會把對象或者map轉成JSON 然後存String類型
其實這樣給高手一看就知道不專業 所以呢 我們還是規範一點 合適的存儲類型選擇合適的Redis類型 慢慢養成好習慣

String的實際應用場景比較廣泛的有:

  • 緩存功能 比如說緩存商品類目 緩存商品詳情等 反正我們後端人員可以利用redis 配合其他關係形數據庫 大大提高系統的讀寫能力 而且還能降低 數據庫的壓力
  • 計數器 統計pv uv 等 後端通過定時任務定時的把數據刷到我們的數據庫 或者其他NoSql中去
  • 存儲用戶信息 存儲我們後臺系統的token等

4.2 Hash:

這個和我們Java中的Map很像呀 我們可以存一寫結構化的對象之類的 並且可以查詢和修改Hash裏面的某個值
就我來說自己用的也不多 這種結構

4.3 List:

List 有序列表 和Java中的LinkedList列表很像 它是一個無環雙向鏈表 可以從兩邊 插入 彈出 一個節點
List的應用場景還是很多的

  • 比如通過List 實現部分數據的分頁。這樣分頁的性能會很高
  • 比如我們做活動時候 存儲拉新人和被拉新人之間的關聯 或者點贊等
  • 消息隊列 Redis的鏈表結構,可以輕鬆實現阻塞隊列,可以使用左進右出的命令組成來完成隊列的設計。比如:數據的生產者可以通過Lpush命令從左邊插入數據,多個數據消費者,可以使用BRpop命令阻塞的“搶”列表尾部的數據

4.4 Set:

是無序集合,會自動去重的那種。和Java的Set差不多
Set的應用場景

  • 如果你需要對一些數據進行快速的全局去重,你當然也可以基於JVM內存裏的HashSet進行去重,如果你要對不同JVM的數據去重?得基於Redis進行全局的 Set 去重。
  • 還有什麼 交集 並集 等 操作 取相同或者不同的數據

4.5 Sorted Set:

Sorted set 是排序的 Set,我們稱爲:有序集合,去重但可以排序,寫進去的時候給一個分數,自動根據分數排序。

Sorted Set的應用場景

  • 我們一般用來做活動的時候 拉新人數的排行榜之類的

5 Redis的底層數據機構

Tips 其實我本來不想寫這個的 原因是自己其實也不是那麼懂的(學的雲裏霧裏的) 但是爲了給各位大佬看 我還是把別人的博客貼出來吧

8種底層實現的數據結構

6 Redis的幾種高級用法

6.1 Bitmap

在開發中,可能會遇到這種情況:需要統計用戶的某些信息,如活躍或不活躍,登錄或者不登錄;又如需要記錄用戶一年的打卡情況,打卡了是1, 沒有打卡是0,如果使用普通的 key/value存儲,則要記錄365條記錄,如果用戶量很大,需要的空間也會很大,所以 Redis 提供了 Bitmap 位圖這中數據結構,Bitmap 就是通過操作二進制位來進行記錄,即爲 0 和 1;如果要記錄 365 天的打卡情況,使用 Bitmap 表示的形式大概如下:0101000111000111…,這樣有什麼好處呢?當然就是節約內存了,365 天相當於 365 bit,又 1 字節 = 8 bit , 所以相當於使用 46 個字節即可。當然,由於公司業務關係,我還沒有遇到這種需求 ^ ^。

那麼具體是怎麼實現的呢 請看下面詳解

bitmap的命令

常用命令 作用
1、getbit key offset 用於獲取Redis中指定key對應的值,中對應offset的bit
2、setbit key key offset value 用於修改指定key對應的值,中對應offset的bit
3、 bitcount key [start end] 用於統計字符串被設置爲1的bit數
4、bitop and/or/xor/not destkey key [key …] 用於對多個key求邏輯與/邏輯或/邏輯異或/邏輯非

例1: 統計去年的活躍用戶數
我只要把用戶的id當做offset 只要他在一年中訪問過這個網站我就給這個offset 設置爲 1 最後通過**bitcount ** 來統計年的訪問量,同理 周 月 日 活躍都可以這樣統計

例2:統計三天內的活躍用戶數
我們把時間作爲key “191127:active” “191128:active” “191129:active” 用戶的ID就可以作爲offset,當用戶訪問過網站,就將對應offset的bit值設置爲“1”;

  • 統計三天的活躍用戶,通過bitop or 獲取三天周內訪問過的用戶數量
  • 連續三天訪問的用戶數量 bitop and

bitmap的優勢,以統計活躍用戶爲例 每個用戶id佔用空間爲1bit,消耗內存非常少,存儲1億用戶量只需要12.5M

6.2 HyperLogLog

HyperLogLog 數據結構是 Redis 的高級數據結構 供不精確的去重計數功能,比較適合用來做大規模數據的去重統計,例如統計 UV;

其實我們項目的總的uv pv也是我收集的 再沒有學習這個之前我是怎麼做的呢 pv 這個好說 設置一個key 用String類型的自增就好了 那uv 我是怎麼做的呢 我是直接把uid 存到set中去 最後定時任務凌晨把他刷到數據庫的 但是這個的缺點是如果訪問量很大就會佔用很多的空間 學完才知道自己的方式不是很好 哈哈

Redis 提供了 HyperLogLog 數據結構就是用來解決這種統計問題的。HyperLogLog 提供不精確的去重計數方案,雖然不精確但是也不是非常不精確,標準誤差是 0.81%,這樣的精確度已經可以滿足上面的 UV 統計需求了。

HyperLogLog 提供了兩個指令 pfadd 和 pfcount,根據字面意義很好理解,一個是增加計數,一個是獲取計數。pfadd 用法和 set 集合的 sadd 是一樣的,來一個用戶 ID,就將用戶 ID 塞進去就是。最後我們要把數據落地的時候用pfcount刷到數據庫就行了 哈哈 是不是很簡單 但是應該很少人用吧。

6.3 pub/sub:

功能是訂閱發佈功能,可以用作簡單的消息隊列。
這裏我其實就是提一嘴而已 再真實的項目中很少用到 畢竟再技術選型上有更好的全的消息中間間 很少人會用到 但是面試的時候又可能說會問到 這邊就在這提一下哈。

6.4 pipeline

Pipeline指的是管道技術,指的是客戶端允許將多個請求依次發給服務器,過程中而不需要等待請求的回覆,在最後再一併讀取結果即可。
redis客戶端執行一條命令分4個過程: 發送命令-〉命令排隊-〉命令執行-〉返回結果

這個過程稱爲Round trip time(簡稱RTT, 往返時間),redis的pipeline 可以讓多個命令同時發送 只需要最後接收返回
普通的請求模型是同步的,每次請求對應一次IO操作等待,而Pipeline 化之後所有的請求合併爲一次IO,除了時延可以降低之外,還能大幅度提升系統吞吐量。

6.5 redis事務

剛剛介紹了Redis Pipeline,Pipeline能幫我們組裝命令一次發送給Redis,但是Pipeline中的命令不具有原子性,所以如果我們需要自己組裝一個原子性的操作,使用Pipeline是無法實現的,慶幸的是Redis提供了事務和支持Lua腳本來實現原子性操作。

Redis提供了一個 multi 命令開啓事務,exec 命令提交事務,在它們之間的命令是在一個事務內的,能保證原子性。

192.168.1.4:0>multi
"OK"
192.168.1.4:0>set tran1 hello
"QUEUED"
192.168.1.4:0>set tran2 world
"QUEUED"
192.168.1.4:0>exec
 1)  "OK"
 2)  "OK"

通過上面的命令可以看到使用 multi 命令開啓事務之後,執行的Redis命令返回結果 QUEUED,表示命令並沒有執行,而是暫時保存在Redis事務中,直到執行 exec 命令後纔會執行上面的命令並且返回結果。

Redis的事務做的很簡單,沒有像關係型數據庫那樣把事務的隔離級別劃分的那麼細,Redis在事務沒提交之前不會執行事務中的命令,會等到事務提交的那一刻再執行事務中的所有命令。所以上面的案例中,如果在 exec 命令未執行之前另一個Redis客戶端調用 get tran1 命令返回值會是null,因爲事務沒有提交之前事務中的命令還沒有執行。

redis執行過程中發生了運行時錯誤,Redis不僅不會回滾事務,還會跳過這個運行時錯誤,繼續向下執行命令 他只是會返回告訴你哪個失敗了,

雖然事務能保證事務內的操作是原子性的,但是無法保證在事務開啓到事務提交之間事務中的key沒有被其他客戶端修改。

Redis提供了一個 watch 命令來幫我們解決上面描述的這個問題,在 multi 命令之前我們可以使用 watch 命令來”觀察”一個或多個key,在事務提交之前Redis會確保被”觀察”的key有沒有被修改過,沒有被修改過纔會執行事務中的命令,如果存在key被修改過,那麼整個事務中的命令都不會執行,有點類似於樂觀鎖的機制。下面是例子

@Test
public void testWatch() {
    JedisPool jedisPool = new JedisPool("192.168.1.4");
    // 設定 nowatch 的初始值爲 雪月劍仙李寒衣
    Jedis jedis = jedisPool.getResource();
    // 使用 watch 命令watch "watchtest"
    jedis.watch("watchtest");
    // 開啓事務
    jedis.set("watchtest", "雪月劍仙李寒衣");
    // 開啓事務
    Transaction multi = jedis.multi();
    // 另一個jedis客戶端對 watchtest進行append操作
    jedisPool.getResource().append("watchtest", "六脈神劍");
    // 事務內部對watchtest進行append操作
    multi.append("watchtest", " 孤劍仙洛青陽");
    // 提交事務
    multi.exec();
    // 打印watchtest對應的value
    System.out.println(jedis.get("watchtest"));
}

如上面的例子 他最後只會打印 雪月劍仙李寒衣 六脈神劍 最後的孤劍仙洛青陽並不會出現在控制檯上

不得不說Redis事務是一個好功能,能幫我們實現一些原子性的操作,但是實際正常開發中很少遇到使用Redis事務的場景,因爲Lua腳本同樣可以幫我們實現Redis事務相關功能,並且功能要強大很多。

結尾

redis的基礎入門篇就講到這了 對於redis的 持久化高可用哨兵Lua 。。。。。。等一大波技能 大家等下回一起打怪升級吧

因爲博主也是一個開發萌新 我也是一邊學一邊寫 我有個目標就是一週 二到三篇 希望能堅持個一年吧 哈哈

日常求贊

好了各位,以上就是這篇文章的全部內容了,能看到這裏的人呀,都是人才

創作不易,各位的支持和認可,就是我創作的最大動力,我們下篇文章見

六脈神劍 | 文 【原創】如果本篇博客有任何錯誤,請批評指教,不勝感激 !

發佈了47 篇原創文章 · 獲贊 9 · 訪問量 6236
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章