Redis初探

來源:互聯網
上載者:User

標籤:需求   一半   target   代碼   種類   cache   class   文本   後端   

一、什麼是Redis

Redis是一個開源的使用ANSI C語言編寫、遵守BSD協議、支援網路、可基於記憶體亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API。

1 Redis特點

  Redis本質上是一個Key-Value類型的記憶體資料庫,很像memcached,整個資料庫統統載入在記憶體當中進行操作,定期通過非同步作業把資料庫資料flush到硬碟上進行儲存。因為是純記憶體操作,Redis的效能非常出色,每秒可以處理超過 10萬次讀寫操作,是已知效能最快的Key-Value DB。而且Redis支援五大資料結構string,list,set,sorted set(zset),hash ,對單個Value大小的限制是1GB。

  雖然Redis是Nosql資料庫的一種,但他有著其他Nosql資料庫不具備的優點:Redis是支援事務(ACID)的,Redis的操作都是原子性的,而且支援復原,且Redis可以定期持久化到本地硬碟中。

Redis可用於緩衝,訊息,按key設定到期時間,到期後將會自動刪除

  與Memcached相比,Redis不僅能在記憶體中讀寫,同時還能把記憶體中的資料持久化到硬碟中,這樣就可以在一定程度上避免斷電後記憶體資料消失的問題。

  Redis的主要缺點是資料庫容量受到實體記憶體的限制,不能用作海量資料的高效能讀寫,因此Redis適合的情境主要局限在較小資料量的高效能操作和運算上。

2 Redis應用情境

  由於Redis有著速度快,支援資料結構多等特點,他在工業界的主要應用情境:緩衝;輕量級訊息佇列(用List來做FIFO雙向鏈表);處理資料到期(精確到毫秒):對存入的Key-Value設定expire時間,因此也可以被當作一個功能加強版的memcached來用。

 

二、Redis使用1 序列化

  序列化 (Serialization)是將對象的狀態資訊轉換為可以儲存或傳輸的形式的過程。在序列化期間,對象將其目前狀態寫入到臨時或持久性儲存區。以後,可以通過從儲存區中讀取或還原序列化對象的狀態,重新建立該對象。

網路傳輸中和本機存放區中,Java對象並不是平時在Java堆中的資料結構,不序列化的Java對象很難跨平台傳輸,安全性也無法保證。

序列化:把Java對象轉換為位元組序列的過程。

  還原序列化:把位元組序列恢複為Java對象的過程。

2 Protostuff序列化

  網路中傳輸的和硬碟中儲存的都是序列化後的資料(二進位流)所以要對存入的Redis資料進行序列化,從Redis取出的資料要做還原序列化。當兩個進程在進行遠程通訊時,彼此可以發送各種類型的資料。無論是何種類型的資料,都會以二進位序列的形式在網路上傳送。發送方需要把這個Java對象轉換為位元組序列,才能在網路上傳送;接收方則需要把位元組序列再恢複為Java對象。

  Jedis是首選的Java用戶端開發包,但與其他資料庫不同的是,Jedis和Redis內部並沒有實現序列化操作,需要程式員自己實現序列化操作。這裡比較初級的做法是在需要序列化的類實現java.io.Serializable介面。

  在眾多的序列化第三方庫中,Google開發的Protostuff是目前效率(時間、空間)最高的一種方式。

  Protostuff使用demo

    public Seckill getSeckill(long seckillId) {        //redis操作邏輯        try {            Jedis jedis = jedisPool.getResource();            try {                String key = "seckill:" + seckillId;                //並沒有實現內部序列化操作                // get-> byte[] -> 還原序列化 ->Object(Seckill)                // 採用自訂序列化                //protostuff : pojo.                byte[] bytes = jedis.get(key.getBytes());                //緩衝中擷取到bytes                if (bytes != null) {                    //Null 物件                    Seckill seckill = schema.newMessage();                    ProtostuffIOUtil.mergeFrom(bytes, seckill, schema);                    //seckill 被還原序列化                    return seckill;                }            } finally {                jedis.close();            }        } catch (Exception e) {            logger.error(e.getMessage(), e);        }        return null;    }    public String putSeckill(Seckill seckill) {        // set Object(Seckill) -> 序列化 -> byte[]        try {            Jedis jedis = jedisPool.getResource();            try {                String key = "seckill:" + seckill.getSeckillId();                byte[] bytes = ProtostuffIOUtil.toByteArray(seckill, schema,                        LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE));                //逾時緩衝                int timeout = 60 * 60;//1小時                String result = jedis.setex(key.getBytes(), timeout, bytes);                return result;            } finally {                jedis.close();            }        } catch (Exception e) {            logger.error(e.getMessage(), e);        }        return null;    }

 

三、Redis持久化

Redis相比其他記憶體型快取資料庫還有一個顯著性的優勢,就是支援持久化。Redis提供兩種持久化方式,一種是RDB持久化(原理是將Reids在記憶體中的資料庫記錄定時dump到磁碟上的RDB持久化),另外一種是AOF(append only file)持久化(原理是將Reids的動作記錄以追加的方式寫入檔案)。

RDB持久化是指在指定的時間間隔內將記憶體中的資料集快照寫入磁碟,實際操作過程是fork一個子進程,先將資料集寫入臨時檔案,寫入成功後,再替換之前的檔案,用二進位壓縮儲存。

 

AOF持久化以日誌的形式記錄伺服器所處理的每一個寫、刪除操作,查詢操作不會記錄,以文本的方式記錄,可以開啟檔案看到詳細的操作記錄。伺服器每次啟動都會讀取日誌記錄,在記憶體中形成記錄。

1 RDB持久化的優缺點

優點:

1). 一旦採用該方式,那麼你的整個Redis資料庫將只包含一個檔案,這對於檔案備份而言是非常完美的。比如,你可能打算每個小時歸檔一次最近24小時的資料,同時還要每天歸檔一次最近30天的資料。通過這樣的備份策略,一旦系統出現災難性故障,我們可以非常容易的進行恢複。

2). 對於災難恢複而言,RDB是非常不錯的選擇。因為我們可以非常輕鬆的將一個單獨的檔案壓縮後再轉移到其它儲存介質上。

3). 效能最大化。對於Redis的服務進程而言,在開始持久化時,它唯一需要做的只是fork出子進程,之後再由子進程完成這些持久化的工作,這樣就可以極大的避免服務進程執行IO操作了。

4). 相比於AOF機制,如果資料集很大,RDB的啟動效率會更高。

缺點:

1). 畢竟存在時間間隔,不能保證百分百的災備。如果你想保證資料的高可用性,即最大限度的避免資料丟失,那麼RDB將不是一個很好的選擇。因為系統一旦在定時持久化之前出現宕機現象,此前沒有來得及寫入磁碟的資料都將丟失。

2). 由於RDB是通過fork子進程來協助完成資料持久化工作的,因此,如果當資料集較大時,可能會導致整個伺服器停止服務幾百毫秒,甚至是1秒鐘。

2 AOF持久化的優缺點

優點:

1). 該機制可以帶來更高的資料安全性,即資料持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事實上,每秒同步也是非同步完成的,其效率也是非常高的,所差的是一旦系統出現宕機現象,那麼這一秒鐘之內修改的資料將會丟失。而每修改同步,我們可以將其視為同步持久化,即每次發生的資料變化都會被立即記錄到磁碟中。可以預見,這種方式在效率上是最低的。至於無同步,無需多言,我想大家都能正確的理解它。

2). 由於該機制對記錄檔的寫入操作採用的是append模式,因此在寫入過程中即使出現宕機現象,也不會破壞記錄檔中已經存在的內容。然而如果我們本次操作只是寫入了一半資料就出現了系統崩潰問題,不用擔心,在Redis下一次啟動之前,我們可以通過redis-check-aof工具來協助我們解決資料一致性的問題。

3). 如果日誌過大,Redis可以自動啟用rewrite機制。即Redis以append模式不斷的將修改資料寫入到老的磁碟檔案中,同時Redis還會建立一個新的檔案用於記錄此期間有哪些修改命令被執行。因此在進行rewrite切換時可以更好的保證資料安全性。

4). AOF包含一個格式清晰、易於理解的記錄檔用於記錄所有的修改操作。事實上,我們也可以通過該檔案完成資料的重建。

缺點:

1). 對於相同數量的資料集而言,AOF檔案通常要大於RDB檔案。RDB 在恢複大資料集時的速度比 AOF 的恢複速度要快。

2). 根據同步策略的不能,安全效能的提高,AOF在運行效率上往往會慢於RDB。總之,每秒同步策略的效率是比較高的,同步禁用策略的效率和RDB一樣高效。

二者選擇的標準,就是看系統是願意犧牲一些效能,換取更高的緩衝一致性(aof),還是願意寫操作頻繁的時候,不啟用備份來換取更高的效能,待手動運行save的時候,再做備份(rdb)

3 無持久化

當然,Redis也可以關閉持久化功能,僅把Redis當做緩衝來使用。

 

四、常見問題1 Redis作為緩衝時與DB的資料一致性維護

這個問題可深可淺,上升到Redis叢集後,解決方案會更複雜,這裡只先初步的分析一種簡單的情況。

我們在使用redis過程中,或者網上一些資料,通常會這樣做:先讀取緩衝,如果緩衝不存在,則讀取資料庫。虛擬碼如下

    Object stuObj = new Object();    public Stu getStuFromCache(String key){        Stu stu = (Stu) redis.get(key);        if(stu == null){            synchronized (stuObj) {                stu = (Stu) redis.get(key);                if(stu == null){                    Stu stuDb = db.query();                    redis.set(key, stuDb);                }            }        }        return stu;    }

上面加鎖是為了防止過多的查詢走到資料庫層

寫資料庫虛擬碼:

public void setStu(){    redis.del(key);    db.write(obj);}

不管是先寫庫,再刪除緩衝;還是先刪緩衝,再寫庫,都有可能出現資料不一致的情況
因為寫和讀是並發的,沒法保證順序,如果刪了緩衝,還沒有來得及寫庫,另一個線程就來讀取,發現緩衝為空白,則去資料庫中讀取資料寫入緩衝,此時緩衝中為髒資料。如果先寫了庫,再刪除緩衝前,寫庫的線程宕機了,沒有刪除掉緩衝,則也會出現資料不一致情況。

如果是redis叢集,或者主從模式,寫主讀從,由於redis複製存在一定的時間延遲,也有可能導致資料不一致。

這裡可以採用雙刪+逾時的方式來解決問題

在寫庫前後都進行redis.del(key)操作,並且設定合理的逾時時間。這樣最差的情況是在逾時時間記憶體在不一致,當然這種情況極其少見,可能的原因就是服務宕機。此種情況可以滿足絕大多數需求。 

當然這種策略要考慮redis和資料庫主從同步的耗時,所以在第二次刪除前最好休眠一定時間,比如500毫秒,這樣毫無疑問又增加了寫請求的耗時。

2 緩衝穿透什麼是緩衝穿透?一般的緩衝系統,都是按照key去緩衝查詢,如果不存在對應的value,就應該去後端系統尋找(比如DB)。如果key對應的value是一定不存在的,並且對該key並發請求量很大,就會對後端系統造成很大的壓力。這就叫做緩衝穿透。如何避免?

1. 對查詢結果為空白的情況也進行緩衝,緩衝時間設定短一點,或者該key對應的資料insert了之後清理緩衝。

2. 對一定不存在的key進行過濾。可以把所有的可能存在的key放到一個大的Bitmap中,查詢時通過該bitmap過濾。

3 緩衝雪崩什麼是緩衝雪崩?當快取服務器重啟或者大量緩衝集中在某一個時間段失效,這樣在失效的時候,也會給後端系統(比如DB)帶來很大壓力。如何避免?

1.在緩衝失效後,通過加鎖或者隊列來控制讀資料庫寫緩衝的線程數量。比如對某個key只允許一個線程查詢資料和寫緩衝,其他線程等待。

2.不同的key,設定不同的到期時間,讓緩衝失效的時間點盡量均勻。

3.做二級緩衝,A1為原始緩衝,A2為拷貝緩衝,A1失效時,可以訪問A2,A1緩衝失效時間設定為短期,A2設定為長期(此點為補充)

4 用Redis和任意語言實現一段惡意登入保護的代碼,限制1小時內每使用者Id最多隻能登入5次

  用隊列資料結構來實現,key為使用者,value為登入時間,每條記錄的到期時間設為1個小時,且維護隊列資料長度為5。當使用者發起新的登入請求且當前隊列的長度已經為5時,拒絕請求。

 

 

參考連結

https://www.cnblogs.com/fidelQuan/p/4543387.html

https://www.cnblogs.com/chenliangcl/p/7240350.html

53690312?winzoom=1

http://www.cnblogs.com/Survivalist/p/8119891.html

Redis初探

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.