1. 程式人生 > >redis基礎二

redis基礎二

生產者消費者 復制 使用 unknown 定義 str 事務所 logs 關系

前面已經學習了redis的基本的命令行操作和數據類型,下面開始redis一些有趣的功能。

訂閱和發布機制

  • 定義:發布者相當於電臺,訂閱者相當於客戶端,客戶端發到頻道的消息,將會被推送到所有訂閱此頻道的客戶端;客戶端不需要主動去獲取消息,只需要訂閱頻道,這個頻道的內容就會被推送過來;

  • 作用:發布者和訂閱者的解耦合可以帶來更大的擴展性和更加動態的網絡拓撲;

相關命令

# 訂閱消息
subscribe 頻道1 頻道2   # 此時redis客戶端會一直處於監聽頻道的狀態,一有消息就處理;
# 取消訂閱
unsubcribe 頻道1 ...   # 如果不寫頻道名稱,則取消所有的訂閱;

# 推送消息
publish 頻道1 消息內容

重點說明

  • 發布訂閱機制一般使用在對同一個redis實例來說,實現類似於生產者消費者模式;

  • 在主從集群中,master發布的消息可以推送到slave中,但slave中的消息不能推送到master中;

訂閱發布機制的不足:

  1. 如果消息接收方不能及時處理推送的消息,消息會在緩存隊列中,會導致緩存占用的空間越來越大,最終導致redis崩潰;

  2. 發布的消息推送存在即時性,但網絡一般是不穩定的,對於客戶端來說,如果出現了斷網的現象,那麽接收的消息就會丟失,所以發布訂閱模式不能用在對數據完整性要求高的場合;

簡單的主從復制配置

  • 定義:一臺redis服務器可以作為一個master,在其下面可以有多個slave,每個slave又可以作為一個master,從而可以構建龐大的redis數據庫集群;

配置方法

方式一:修改配置文件

# 設置主服務器的配置,綁定固定的ip
sudo vi redis.conf
bind 服務器的ip

# 設置從服務器的ip
sudo vi redis.conf
bind 服務器的ip
slaveof 主服務器的ip 主服務器redis端口     # 註意,ip與端口之間使用空格分割

# 分別啟動主從redis,主服務器redis負責寫,也可以讀;從服務器只能讀,不能寫;

方式二:使用命令行的方式動態設置

# 從服務器連接主服務器
slaveof host port 
# 從服務器斷開主服務器
slaveof no one 

重要說明

  • 從服務器與主服務器進行初始連接時,從服務器會丟棄所有的舊數據,然後載入主服務器的數據;

  • redis不支持主主復制,也就是說不可以兩個redis相互設置對方為主服務器,雖然不會報錯,但性能方面,以及對客戶端的請求都可能出現問題;

redis復制的啟動過程

  • 當主服務器收到從服務器發送的復制請求的時候,主服務器執行的動作有:

等待從服務器的命令進入-->執行bgsave,創建快照文件;使用緩存區記錄bgsave命令執行後所有的寫命令-->快照文件創建完畢後,向服務器發送快照文件-->發送快照文件完畢後向從服務器發送緩存區的寫命令-->完畢後每收到一個寫命令就向從服務器發送

  • 從服務器相應的動作有:

連接主服務器,發送sync命令-->等待響應-->丟棄所有的舊數據,載入主服務器的快照文件-->完成快照文件的解釋,開始接受命令請求-->執行從主服務器發送的所有的寫命令;

註意:redis支持主從鏈,即從服務器還可以有從服務器,但是從服務器A復制從服務器B的過程和從服務器B復制主服務器是有區別的;從服務器B向從服務器A發送完畢快照文件後,會先斷開與從服務器A的連接,從服務器A需要重新連接並且請求同步;

redis的事務

  • redis有像關系型數據庫一樣的事務機制來保證多條命令作為原子操作;事務中的命令要麽全執行,要麽全不執行;

  • 事務的完整過程:開始事務-->命令進入緩存-->執行事務;

事務的基本使用

# 開啟一個事務
multi      # 提交命令後,redis會將後面的操作保存起來
# 提交事務
exec   # 提交命令後,redis會執行前面保存的所有的命令

# 取消事務
discard   # 如果書寫命令隊列的過程中需要取消事務時使用
  • redis事務中在寫命令隊列的時候,如果中間發生了語法錯誤,並且redis報了錯,那麽這個事務所有的命令都會取消執行;
> lpush list a b c 
(integer) 3
> multi
OK
> lpush list d
QUEUED
> lpuxh list f
(error) ERR unknown command ‘lpuxh‘
> lrange list 0 10
QUEUED
> exec
(error) EXECABORT Transaction discarded because of previous errors.
> lrange list 0 10
1) "c"
2) "b"
3) "a"

# 所有的命令都沒有執行
  • 但有一些錯誤redis在執行之前並不能感知,這時redis會執行所有的的命令,客戶端必須自己處理錯誤;
> lpush li blue red green
(integer) 3
> multi
OK
> get li
QUEUED
> lpush li white
QUEUED
> exec
1) (error) WRONGTYPE Operation against a key holding the wrong kind of value
2) (integer) 4
> lrange li 0 10
1) "white"
2) "green"
3) "red"
4) "blue"

# 所有的命令都執行了,只不過有的命令執行失敗

註意點

  • redis的開啟事務是將命令暫時保存在一個隊列裏,執行時依次操作;如果命令隊列有一條出現語法錯誤,整個事務創建會失敗;

  • redis沒有提供事務的回滾功能,客戶端必須自己處理失敗的命令;

事務鎖

# 基本命令
watch key key ..   # 監控鍵值

# 取消對所有鍵的監控
unwatch 
  • 由於redis的事務中的命令其實是緩存隊列,並且redis可以防止在事務的執行過程中有其他的命令插入,即具有隔離性;但是在多個客戶端進行並發操作時存在數據無法同步的問題;如客戶端A、B同時操作鍵key的值加一,預期結果為增加2,實際可能只有1.

  • 為了解決這個問題,redis引入了watch監控鍵;

> watch num 
OK
> set num 7
OK
> multi
OK
> set num 5
QUEUED
> exec
(nil)
> get num
"7"
  • 可以看到事務並沒有執行成功,wetch可以監控鍵,如果在監控後,鍵的值發生了改變,那麽redis後面與這個鍵相關的事務操作將會失敗,同時在exec執行後,鍵的監控會被取消;

註意:無論監控多少個鍵或事務中有沒有與該鍵相關的命令,在最近的一個執行了exec,無論事務執行有沒有成功,watch監控的所有鍵都將會取消,後面的事務不再受影響。

說明:使用watch監控實現並發修改鍵值,如果事務被取消,需要手動重新執行事務;

問題

  • 以上可知,redis實現的是類似樂觀鎖(即預期並發時沒有出現競爭修改同一個鍵值的狀況),這種情況在並發量低時影響不大,但是高並發時幾乎肯定出現競爭,並發修改鍵值程序重試的次數越來越多,資源被白白浪費,需要使用其他的方法實現悲觀鎖機制,這點後面會繼續研究;

redis基礎二