欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關(guān)于Redis你可能不了解的一些事

 更新時間:2019年04月03日 10:15:53   作者:JAVA油膩男  
這篇文章主要給大家介紹了關(guān)于Redis你可能不了解的一些事,對大家學(xué)習(xí)或者使用Redis具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧

引子

Redis 是一個高性能分布式的key-value數(shù)據(jù)庫。它支持多種數(shù)據(jù)結(jié)構(gòu),并可應(yīng)用于緩存、隊列等多種場景下。使用過Redis的小伙伴們可能對這些已經(jīng)非常熟知了,下面我想來談?wù)凴edis也許并不被每個人了解的那點事。

Redis持久化機制

剛看到標(biāo)題你可能會說,我知道,不就是RDB和AOF嘛。這些已經(jīng)是老生常談了。那么我們今天就深入談?wù)勥@兩種持久化方式的邏輯和原理。

RDB的原理

在Redis中RDB持久化的觸發(fā)分為兩種:自己手動觸發(fā)與Redis定時觸發(fā)。

針對RDB方式的持久化,手動觸發(fā)可以使用:

(1)save:會阻塞當(dāng)前Redis服務(wù)器,直到持久化完成,線上應(yīng)該禁止使用。
(2)bgsave:該觸發(fā)方式會fork一個子進(jìn)程,由子進(jìn)程負(fù)責(zé)持久化過程,因此阻塞只會發(fā)生在fork子進(jìn)程的時候。

而自動觸發(fā)的場景如下:

根據(jù)我們的 save m n 配置規(guī)則自動觸發(fā);
從節(jié)點全量復(fù)制時,主節(jié)點發(fā)送rdb文件給從節(jié)點完成復(fù)制操作,主節(jié)點會觸發(fā) bgsave;
執(zhí)行 debug reload 時處罰;
執(zhí)行 shutdown時,如果沒有開啟aof,也會觸發(fā)。

由于 save 基本不會被使用到,我們來看看 bgsave 這個命令是如何完成RDB的持久化的。


RDB文件保存過程

(1)redis調(diào)用fork,現(xiàn)在有了子進(jìn)程和父進(jìn)程。
(2)父進(jìn)程繼續(xù)處理client請求,子進(jìn)程負(fù)責(zé)將內(nèi)存內(nèi)容寫入到臨時文件。由于os的寫時復(fù)制機制(copy on write)父子進(jìn)程會共享相同的物理頁面,當(dāng)父進(jìn)程處理寫請求時os會為父進(jìn)程要修改的頁面創(chuàng)建副本,而不是寫共享的頁面。所以子進(jìn)程的地址空間內(nèi)的數(shù)據(jù)是fork時刻整個數(shù)據(jù)庫的一個快照。
(3)當(dāng)子進(jìn)程將快照寫入臨時文件完畢后,用臨時文件替換原來的快照文件,然后子進(jìn)程退出。

PS:fork 操作會阻塞,導(dǎo)致Redis讀寫性能下降。我們可以控制單個Redis實例的最大內(nèi)存,來盡可能降低Redis在fork時的時間消耗;或者控制自動觸發(fā)的頻率減少fork次數(shù)。

AOF的原理

AOF的整個流程大體來看可以分為兩步,一步是命令的實時寫入(如果是 appendfsync everysec 配置,會有1s損耗),第二步是對aof文件的重寫。

對于增量追加到文件這一步主要的流程是:

(1)命令寫入
(2)追加到aof_buf
(3)同步到aof磁盤

那么這里為什么要先寫入buf再同步到磁盤呢?如果實時寫入磁盤會帶來非常高的磁盤IO,影響整體性能。

AOF重寫

你可以會想,每一條寫命令都生成一條日志,那么AOF文件是不是會很大?答案是肯定的,AOF文件會越來越大,所以Redis又提供了一個功能,叫做AOF rewrite。其功能就是重新生成一份AOF文件,新的AOF文件中一條記錄的操作只會有一次,而不像一份老文件那樣,可能記錄了對同一個值的多次操作。

手動觸發(fā): bgrewriteaof

自動觸發(fā)就是根據(jù)配置規(guī)則來觸發(fā),當(dāng)然自動觸發(fā)的整體時間還跟Redis的定時任務(wù)頻率有關(guān)系。

下面來看看重寫的流程圖:

(1)redis調(diào)用fork ,現(xiàn)在有父子兩個進(jìn)程
(2)子進(jìn)程根據(jù)內(nèi)存中的數(shù)據(jù)庫快照,往臨時文件中寫入重建數(shù)據(jù)庫狀態(tài)的命令
(3)父進(jìn)程繼續(xù)處理client請求,除了把寫命令寫入到原來的aof文件中。同時把收到的寫命令緩存起來。這樣就能保證如果子進(jìn)程重寫失敗的話并不會出問題。
(4)當(dāng)子進(jìn)程把快照內(nèi)容寫到臨時文件中后,子進(jìn)程發(fā)信號通知父進(jìn)程。然后父進(jìn)程把緩存的寫命令也寫入到臨時文件。
(5)現(xiàn)在父進(jìn)程可以使用臨時文件替換老的aof文件,并重命名,后面收到的寫命令也開始往新的aof文件中追加。

PS:需要注意到是重寫aof文件的操作,并沒有讀取舊的aof文件,而是將整個內(nèi)存中的數(shù)據(jù)庫內(nèi)容用命令的方式重寫了一個新的aof文件,這點和快照有點類似。

Redis為什么這么快?

Redis采用的是基于內(nèi)存的單進(jìn)程單線程模型的 KV 數(shù)據(jù)庫,由C語言編寫,官方提供的數(shù)據(jù)是可以達(dá)到100000+的QPS(每秒內(nèi)查詢次數(shù))。這個數(shù)據(jù)不比采用單進(jìn)程多線程的同樣基于內(nèi)存的 KV 數(shù)據(jù)庫 Memcached 差!原因如下:

1、完全基于內(nèi)存,絕大部分請求是純粹的內(nèi)存操作,非??焖?;
2、數(shù)據(jù)結(jié)構(gòu)簡單,對數(shù)據(jù)操作也簡單,Redis中的數(shù)據(jù)結(jié)構(gòu)是專門進(jìn)行設(shè)計的;
3、采用單線程,避免了不必要的上下文切換和競爭條件,也不存在多進(jìn)程或者多線程導(dǎo)致的切換而消耗CPU,不用去考慮各種鎖的問題,不存在加鎖釋放鎖的操作,也不可能出現(xiàn)死鎖而導(dǎo)致的性能消耗;
4、使用多路I/O復(fù)用模型,非阻塞IO;
5、使用的底層模型不同,底層實現(xiàn)方式以及與客戶端之間通信的應(yīng)用協(xié)議不一樣,Redis直接構(gòu)建了自己的VM機制。

多路I/O復(fù)用模型

多路I/O復(fù)用模型是利用 select、poll、epoll 可以同時監(jiān)察多個流的 I/O 事件的能力,在空閑的時候,會把當(dāng)前線程阻塞掉,當(dāng)有一個或多個流有 I/O 事件時,就從阻塞態(tài)中喚醒,于是程序就會輪詢一遍所有的流(epoll 是只輪詢那些真正發(fā)出了事件的流),并且只依次順序的處理就緒的流,這種做法就避免了大量的無用操作。這里“多路”指的是多個網(wǎng)絡(luò)連接,“復(fù)用”指的是復(fù)用同一個線程。采用多路 I/O 復(fù)用技術(shù)可以讓單個線程高效的處理多個連接請求(盡量減少網(wǎng)絡(luò) IO 的時間消耗)。

Redis事務(wù)

Redis中的事務(wù)(transaction)是一組命令的集合。事務(wù)同命令一樣都是Redis最小的執(zhí)行單位,一個事務(wù)中的命令要么都執(zhí)行,要么都不執(zhí)行。Redis事務(wù)的實現(xiàn)需要 MULTI 和 EXEC 兩個命令,事務(wù)開始的時候先向Redis服務(wù)器發(fā)送 MULTI 命令,然后依次發(fā)送需要在本次事務(wù)中處理的命令,最后再發(fā)送 EXEC 命令表示事務(wù)命令結(jié)束。

舉個例子,使用redis-cli連接redis,然后在命令行工具中輸入如下命令:


從輸出中可以看到,當(dāng)輸入MULTI命令后,服務(wù)器返回OK表示事務(wù)開始成功,然后依次輸入需要在本次事務(wù)中執(zhí)行的所有命令,每次輸入一個命令服務(wù)器并不會馬上執(zhí)行,而是返回”QUEUED”,這表示命令已經(jīng)被服務(wù)器接受并且暫時保存起來,最后輸入EXEC命令后,本次事務(wù)中的所有命令才會被依次執(zhí)行,可以看到最后服務(wù)器一次性返回了三個OK,這里返回的結(jié)果與發(fā)送的命令是按順序,這說明這次事務(wù)中的命令全都執(zhí)行成功了。

再舉個例子,在命令行工具中輸入如下命令:


和前面的例子一樣,先輸入MULTI最后輸入EXEC表示中間的命令屬于一個事務(wù),不同的是中間輸入的命令有一個錯誤(set寫成了sett),這樣因為有一個錯誤的命令導(dǎo)致事務(wù)中的其他命令都不執(zhí)行了,可見事務(wù)中的所有命令是保持一致的。

如果客戶端在發(fā)送EXEC命令之前斷線了,則服務(wù)器會清空事務(wù)隊列,事務(wù)中的所有命令都不會被執(zhí)行。而一旦客戶端發(fā)送了EXEC命令之后,事務(wù)中的所有命令都會被執(zhí)行,即使此后客戶端斷線也沒關(guān)系,因為服務(wù)器已經(jīng)保存了事務(wù)中的所有命令。

除了保證事務(wù)中的所有命令要么全執(zhí)行要么全不執(zhí)行外,Redis的事務(wù)還能保證一個事務(wù)中的命令依次執(zhí)行而不會被其他命令插入。試想一個客戶端A需要執(zhí)行幾條命令,同時客戶端B發(fā)送了幾條命令,如果不使用事務(wù),則客戶端B的命令有可能會插入到客戶端A的幾條命令中,如果想避免這種情況發(fā)生,也可以使用事務(wù)。

Redis事務(wù)錯誤處理

如果一個事務(wù)中的某個命令執(zhí)行出錯,Redis會怎樣處理呢?要回答這個問題,首先要搞清楚是什么原因?qū)е旅顖?zhí)行出錯:

1.語法錯誤:就像上面的例子一樣,語法錯誤表示命令不存在或者參數(shù)錯誤,這種情況需要區(qū)分Redis的版本,Redis 2.6.5之前的版本會忽略錯誤的命令,執(zhí)行其他正確的命令,2.6.5之后的版本會忽略這個事務(wù)中的所有命令,都不執(zhí)行

2.運行錯誤 運行錯誤表示命令在執(zhí)行過程中出現(xiàn)錯誤,比如用GET命令獲取一個散列表類型的鍵值。這種錯誤在命令執(zhí)行之前Redis是無法發(fā)現(xiàn)的,所以在事務(wù)里這樣的命令會被Redis接受并執(zhí)行。如果食物里有一條命令執(zhí)行錯誤,其他命令依舊會執(zhí)行(包括出錯之后的命令)。


Redis中的事務(wù)并沒有關(guān)系型數(shù)據(jù)庫中的事務(wù)回滾(rollback)功能,因此使用者必須自己收拾剩下的爛攤子。不過由于Redis不支持事務(wù)回滾功能,這也使得Redis的事務(wù)簡潔快速。

WATCH、UNWATCH、DISCARD命令

從上面的例子我們可以看到,事務(wù)中的命令要全部執(zhí)行完之后才能獲取每個命令的結(jié)果,但是如果一個事務(wù)中的命令B依賴于他上一個命令A(yù)的結(jié)果的話該怎么辦呢?就比如說實現(xiàn)類似Java中的i++的功能,先要獲取當(dāng)前值,才能在當(dāng)前值的基礎(chǔ)上做加一操作。這種場合僅僅使用上面介紹的MULTI和EXEC是不能實現(xiàn)的,因為MULTI和EXEC中的命令是一起執(zhí)行的,并不能將其中一條命令的執(zhí)行結(jié)果作為另一條命令的執(zhí)行參數(shù),所以這個時候就需要引進(jìn)Redis事務(wù)家族中的另一成員:WATCH命令

換個角度思考上面說到的實現(xiàn)i++的方法,可以這樣實現(xiàn):

  • 監(jiān)控i的值,保證i的值不被修改
  • 獲取i的原值
  • 如果過程中i的值沒有被修改,則將當(dāng)前的i值+1,否則不執(zhí)行

WATCH命令可以監(jiān)控一個或多個鍵,一旦其中有一個鍵被修改(或刪除),之后的事務(wù)就不會執(zhí)行,監(jiān)控一直持續(xù)到EXEC命令(事務(wù)中的命令是在EXEC之后才執(zhí)行的,EXEC命令執(zhí)行完之后被監(jiān)控的鍵會自動被UNWATCH)。舉個例子:


上面的例子中,首先設(shè)置mykey的鍵值為1,然后使用WATCH命令監(jiān)控mykey,隨后更改mykey的值為2,然后進(jìn)入事務(wù),事務(wù)中設(shè)置mykey的值為3,然后執(zhí)行EXEC運行事務(wù)中的命令,最后使用get命令查看mykey的值,發(fā)現(xiàn)mykey的值還是2,也就是說事務(wù)中的命令根本沒有執(zhí)行(因為WATCH監(jiān)控mykey的過程中,mykey被修改了,所以隨后的事務(wù)便會被取消)。

UNWATCH命令可以在WATCH命令執(zhí)行之后、MULTI命令執(zhí)行之前取消對某個鍵的監(jiān)控。舉個例子:


上面的例子中,首先設(shè)置mykey的鍵值為1,然后使用WATCH命令監(jiān)控mykey,隨后更改mykey的值為2,然后取消對mykey的監(jiān)控,再進(jìn)入事務(wù),事務(wù)中設(shè)置mykey的值為3,然后執(zhí)行EXEC運行事務(wù)中的命令,最后使用get命令查看mykey的值,發(fā)現(xiàn)mykey的值還是3,也就是說事務(wù)中的命令運行成功。

DISCARD命令則可以在MULTI命令執(zhí)行之后,EXEC命令執(zhí)行之前取消WATCH命令并清空事務(wù)隊列,然后從事務(wù)狀態(tài)中退出。舉個例子:


上面的例子中,首先設(shè)置mykey的鍵值為1,然后使用WATCH命令監(jiān)控mykey,隨后更改mykey的值為2,然后進(jìn)入事務(wù),事務(wù)中設(shè)置mykey的值為3,然后執(zhí)行DISCARD命令,再執(zhí)行EXEC運行事務(wù)中的命令,發(fā)現(xiàn)報錯“ERR EXEC without MULTI”,說明DISCARD命令成功執(zhí)行——取消WATCH命令并清空事務(wù)隊列,然后從事務(wù)狀態(tài)中退出。

Redis分布式鎖

上面介紹的Redis的WATCH、MULTI和EXEC命令,只會在數(shù)據(jù)被其他客戶端搶先修改的情況下,通知執(zhí)行這些命令的客戶端,讓它撤銷對數(shù)據(jù)的修改操作,并不能阻止其他客戶端對數(shù)據(jù)進(jìn)行修改,所以只能稱之為樂觀鎖(optimistic locking)。

而這種樂觀鎖并不具備可擴展性——當(dāng)客戶端嘗試完成一個事務(wù)的時候,可能會因為事務(wù)執(zhí)行失敗而進(jìn)行反復(fù)的重試。保證數(shù)據(jù)準(zhǔn)確性非常重要,但是當(dāng)負(fù)載變大的時候,使用樂觀鎖的做法并不完美。這時就需要使用Redis實現(xiàn)分布式鎖。

分布式鎖:是控制分布式系統(tǒng)之間同步訪問共享資源的一種方式。在分布式系統(tǒng)中,常常需要協(xié)調(diào)他們的動作。如果不同的系統(tǒng)或是同一個系統(tǒng)的不同主機之間共享了一個或一組資源,那么訪問這些資源的時候,往往需要互斥來防止彼此干擾來保證一致性,在這種情況下,便需要使用到分布式鎖。

Redis命令介紹:

Redis實現(xiàn)分布式鎖主要用到命令是SETNX命令(SET if Not eXists)。

語法:SETNX key value

功能:當(dāng)且僅當(dāng) key 不存在,將 key 的值設(shè)為 value ,并返回1;若給定的 key 已經(jīng)存在,則 SETNX 不做任何動作,并返回0。

使用Redis構(gòu)建鎖:

思路:將“l(fā)ock:”+參數(shù)名設(shè)置為鎖的鍵,使用SETNX命令嘗試將一個隨機的uuid設(shè)置為鎖的值,并為鎖設(shè)置過期時間,使用SETNX設(shè)置鎖的值可以防止鎖被其他進(jìn)程獲取。如果嘗試獲取鎖的時候失敗,那么程序?qū)⒉粩嘀卦?,直到成功獲取鎖或者超過給定是時限為止。

代碼:

public String acquireLockWithTimeout(
 Jedis conn, String lockName, long acquireTimeout, long lockTimeout)
 {
 String identifier = UUID.randomUUID().toString(); //鎖的值
 String lockKey = "lock:" + lockName; //鎖的鍵
 int lockExpire = (int)(lockTimeout / 1000); //鎖的過期時間
 long end = System.currentTimeMillis() + acquireTimeout; //嘗試獲取鎖的時限
 while (System.currentTimeMillis() < end) { //判斷是否超過獲取鎖的時限
 if (conn.setnx(lockKey, identifier) == 1){ //判斷設(shè)置鎖的值是否成功
 conn.expire(lockKey, lockExpire); //設(shè)置鎖的過期時間
 return identifier; //返回鎖的值
 }
 if (conn.ttl(lockKey) == -1) { //判斷鎖是否超時
 conn.expire(lockKey, lockExpire);
 }
 try {
 Thread.sleep(1000); //等待1秒后重新嘗試設(shè)置鎖的值
 }catch(InterruptedException ie){
 Thread.currentThread().interrupt();
 }
 }
 // 獲取鎖失敗時返回null
 return null;
 }

鎖的釋放:

思路:使用WATCH命令監(jiān)視代表鎖的鍵,然后檢查鍵的值是否和加鎖時設(shè)置的值相同,并在確認(rèn)值沒有變化后刪除該鍵。

代碼:

public boolean releaseLock(Jedis conn, String lockName, String identifier) {
 String lockKey = "lock:" + lockName; //鎖的鍵
 while (true){
 conn.watch(lockKey); //監(jiān)視鎖的鍵
 if (identifier.equals(conn.get(lockKey))){ //判斷鎖的值是否和加鎖時設(shè)置的一致,即檢查進(jìn)程是否仍然持有鎖
 Transaction trans = conn.multi();
 trans.del(lockKey); //在Redis事務(wù)中釋放鎖
 List<Object> results = trans.exec();
 if (results == null){ 
 continue; //事務(wù)執(zhí)行失敗后重試(監(jiān)視的鍵被修改導(dǎo)致事務(wù)失敗,重新監(jiān)視并釋放鎖)
 }
 return true;
 }
 conn.unwatch(); //解除監(jiān)視
 break;
 }
 return false;
 }

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。

相關(guān)文章

  • Redis數(shù)據(jù)類型之散列類型hash命令學(xué)習(xí)

    Redis數(shù)據(jù)類型之散列類型hash命令學(xué)習(xí)

    這篇文章主要為大家介紹了Redis數(shù)據(jù)類型之散列類型hash命令學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • Redis生成分布式系統(tǒng)全局唯一ID的實現(xiàn)

    Redis生成分布式系統(tǒng)全局唯一ID的實現(xiàn)

    在互聯(lián)網(wǎng)系統(tǒng)中,并發(fā)越大的系統(tǒng),數(shù)據(jù)就越大,數(shù)據(jù)越大就越需要分布式,本文主要介紹了Redis生成分布式系統(tǒng)全局唯一ID的實現(xiàn),感興趣的可以了解一下
    2021-10-10
  • Redis如何使用樂觀鎖(CAS)保證數(shù)據(jù)一致性

    Redis如何使用樂觀鎖(CAS)保證數(shù)據(jù)一致性

    本文主要介紹了Redis如何使用樂觀鎖(CAS)保證數(shù)據(jù)一致性,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Redis中鍵的過期刪除策略深入講解

    Redis中鍵的過期刪除策略深入講解

    這篇文章主要給大家介紹了關(guān)于Redis中鍵的過期刪除策略的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-09-09
  • Redis批量刪除指定前綴的Key兩種方法

    Redis批量刪除指定前綴的Key兩種方法

    redis作為緩存服務(wù)器在項目中經(jīng)常使用,使用redis存儲數(shù)據(jù)時,我們經(jīng)常會將key分組,這篇文章主要給大家介紹了關(guān)于Redis批量刪除指定前綴的Key兩種方法,需要的朋友可以參考下
    2024-01-01
  • 分割超大Redis數(shù)據(jù)庫例子

    分割超大Redis數(shù)據(jù)庫例子

    這篇文章主要介紹了分割超大Redis數(shù)據(jù)庫例子,本文講解了分割的需求、分割的思路及分割實例,需要的朋友可以參考下
    2015-03-03
  • 如何使用Redis保存用戶會話Session詳解

    如何使用Redis保存用戶會話Session詳解

    這篇文章主要給大家介紹了關(guān)于如何使用Redis保存用戶會話Session的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-01-01
  • 圖文詳解Windows下使用Redis緩存工具的方法

    圖文詳解Windows下使用Redis緩存工具的方法

    這篇文章以圖文結(jié)合的方式詳解Windows下使用Redis緩存工具的方法,感興趣的小伙伴們可以參考一下
    2015-12-12
  • 使用Redis實現(xiàn)向量相似度搜索

    使用Redis實現(xiàn)向量相似度搜索

    在自然語言處理領(lǐng)域,有一個常見且重要的任務(wù)就是文本相似度搜索,所以本文為大家介紹一下如何利用Redis實現(xiàn)向量相似度搜索,解決文本、圖像和音頻之間的相似度匹配問題,需要的可以了解下
    2023-07-07
  • redis中跳表zset的具體使用

    redis中跳表zset的具體使用

    Redis跳表zset是一種結(jié)合了跳表和有序集合的高效數(shù)據(jù)結(jié)構(gòu),適用于實現(xiàn)排序和大規(guī)模數(shù)據(jù)的快速查詢,本文主要介紹了redis中跳表zset的具體使用,感興趣的可以了解一下
    2024-01-01

最新評論