數據庫學習之Redis高級特性

  • 數據持久化

    • 持久化定義

      將數據從掉電易失的內存放到永久存儲的設備上
      
    • 爲什麼需要持久化

      因爲所有的數據都在內存上,斷電後,數據就不見了,所以必須得持久化
      
    • 數據持久化分類 - RDB模式(默認開啓)

      • 默認模式

        1、保存真實的數據
        2、將服務器包含的所有數據庫數據以二進制文件的形式保存到硬盤裏面
        3、默認文件名 :/var/lib/redis/dump.rdb
        
    • 創建rdb文件的兩種方式

      • 方式1:服務器執行客戶端發送的SAVE或者BGSAVE命令

        127.0.0.1:6379> SAVE
        OK
        # 特點
        1、執行SAVE命令過程中,redis服務器將被阻塞,無法處理客戶端發送的命令請求,在SAVE命令執行完畢後,服務器纔會重新開始處理客戶端發送的命令請求
        2、如果RDB文件已經存在,那麼服務器將自動使用新的RDB文件代替舊的RDB文件
        # 工作中定時持久化保存一個文件
        
        127.0.0.1:6379> BGSAVE
        Background saving started
        # 執行過程如下
        1、客戶端 發送 BGSAVE 給服務器
        2、服務器馬上返回 Background saving started 給客戶端
        3、服務器 fork() 子進程做這件事情
        4、服務器繼續提供服務
        5、子進程創建完RDB文件後再告知Redis服務器
        
        # 配置文件相關操作
        /etc/redis/redis.conf
        dir /var/lib/redis   # 表示rdb文件存放路徑
        dbfilename dump.rdb  # 文件名
        
        # 兩個命令比較
        SAVE比BGSAVE快,因爲需要創建子進程,消耗額外的內存
        
        # 補充:可以通過查看日誌文件來查看redis都做了哪些操作
        # 日誌文件:配置文件中搜索 logfile
        logfile /var/log/redis/redis-server.log
        
      • 方式2:設置配置文件條件滿足時自動保存(使用最多)

        # 命令行示例
        redis>save 300 10
          表示如果距離上一次創建RDB文件已經過去了300秒,並且服務器的所有數據庫總共已經發生了不少於10次修改,那麼執行BGSAVE命令
        redis>save 60 10000
          表示'如果距離上一次創建rdb文件已經過去60秒,並且服務器所有數據庫總共已經發生了不少於10000次修改,那麼執行bgsave命令'
        
        # redis配置文件默認
        save 900 1
        save 300 10
        save 60 10000
          1、只要三個條件中的任意一個被滿足時,服務器就會自動執行BGSAVE
          2、每次創建RDB文件之後,服務器爲實現自動持久化而設置的時間計數器和次數計數器就會被清零,並重新開始計數,所以多個保存條件的效果不會疊加
        
    • 數據持久化分類- AOF(AppendOnlyFile,默認未開啓)

      • 特點

        1、存儲的是命令,而不是真實數據
        2、默認不開啓
        # 開啓方式(修改配置文件)
        1、/etc/redis/redis.conf
          appendonly yes # 把 no 改爲 yes
          appendfilename "appendonly.aof"
        2、重啓服務
          sudo /etc/init.d/redis-server restart
        
      • RDB缺點

        1、創建RDB文件需要將服務器所有的數據庫的數據都保存起來,這是一個非常消耗資源和時間的操作,所以服務器需要隔一段時間才創建一個新的RDB文件,也就是說,創建RDB文件不能執行的過於頻繁,否則會嚴重影響服務器的性能
        2、可能丟失數據
        
      • AOF持久化原理及優點

        # 原理
        1、每當有修改數據庫的命令被執行時,服務器就會將執行的命令寫入到AOF文件的末尾
        2、因爲AOF文件裏面存儲了服務器執行過的所有數據庫修改的命令,所以給定一個AOF文件,服務器只要重新執行一遍AOF文件裏面包含的所有命令,就可以達到還原數據庫的目的
        
        # 優點
        用戶可以根據自己的需要對AOF持久化進行調整,讓Redis在遭遇意外停機時不丟失任何數據,或者只丟失一秒鐘的數據,這比RDB持久化丟失的數據要少的多
        
      • 安全性問題考慮

        # 因爲
          雖然服務器執行一個修改數據庫的命令,就會把執行的命令寫入到AOF文件,但這並不意味着AOF文件持久化不會丟失任何數據,在目前常見的操作系統中,執行系統調用write函數,將一些內容寫入到某個文件裏面時,爲了提高效率,系統通常不會直接將內容寫入硬盤裏面,而是將內容放入一個內存緩存區(buffer)裏面,等到緩衝區被填滿時纔將存儲在緩衝區裏面的內容真正寫入到硬盤裏
        
        # 所以
          1、AOF持久化:當一條命令真正的被寫入到硬盤裏面時,這條命令纔不會因爲停機而意外丟失
          2、AOF持久化在遭遇停機時丟失命令的數量,取決於命令被寫入到硬盤的時間
          3、越早將命令寫入到硬盤,發生意外停機時丟失的數據就越少,反之亦然
        
      • 策略 - 配置文件

        # 打開配置文件:/etc/redis/redis.conf,找到相關策略如下
        1、alwarys
           服務器每寫入一條命令,就將緩衝區裏面的命令寫入到硬盤裏面,服務器就算意外停機,也不會丟失任何已經成功執行的命令數據
        2、everysec(# 默認)
           服務器每一秒將緩衝區裏面的命令寫入到硬盤裏面,這種模式下,服務器即使遭遇意外停機,最多隻丟失1秒的數據
        3、no
           服務器不主動將命令寫入硬盤,由操作系統決定何時將緩衝區裏面的命令寫入到硬盤裏面,丟失命令數量不確定
        
        # 運行速度比較
        always:速度慢
        everysec和no都很快,默認值爲everysec
        
      • AOF文件中是否會產生很多的冗餘命令?

        爲了讓AOF文件的大小控制在合理範圍,避免胡亂總長,redis提供了AOF重寫功能,通過這個功能,服務器可以產生一個新的AOF文件
          -- 新的AOF文件記錄的數據庫數據和原由的AOF文件記錄的數據庫數據完全一樣
          -- 新的AOF文件會使用盡可能少的命令來記錄數據庫數據,因此新的AOF文件的提及通常會小很多
          -- AOF重寫期間,服務器不會被阻塞,可以正常處理客戶端發送的命令請求
        
        • 示例:
      • AOF文件重寫方法觸發

        1、客戶端向服務器發送BGREWRITEAOF命令
           127.0.0.1:6379> BGREWRITEAOF
           Background append only file rewriting started
        
        2、修改配置文件讓服務器自動執行BGREWRITEAOF命令
          auto-aof-rewrite-percentage 100
          auto-aof-rewrite-min-size 64mb
          # 解釋
            1、只有當AOF文件的增量大於100%時才進行重寫,也就是大一倍的時候才觸發
                # 第一次重寫新增:64M
                # 第二次重寫新增:128M
                # 第三次重寫新增:256M(新增128M)
        
      • RDB和AOF持久化對比


  • Redis 主從複製

    • 定義

      1、一個Redis服務可以有多個該服務的複製品,這個Redis服務成爲master,其他複製品成爲slaves
      2、網絡正常,master會一直將自己的數據更新同步給slaves,保持主從同步
      3、只有master可以執行寫命令,slave只能執行讀命令
      
    • 作用

      分擔了讀的壓力(高併發)
      
    • 原理

      從服務器執行客戶端發送的讀命令,比如GET、LRANGE、SMEMMBERS、HGET、ZRANGE等等,客戶端可以連接slaves執行讀請求,來降低master的讀壓力
      
    • 兩種實現方式

      • 方式1:命令行實現1

        redis-server --slaveof <master-ip> <master-port>

        # 從服務端
        redis-server --port 6300 --slaveof 127.0.0.1 6379
        # 從客戶端
        redis-cli -p 6300
        127.0.0.1:6300> keys * 
        # 發現是複製了原6379端口的redis中數據
        127.0.0.1:6380> set mykey 123
        (error) READONLY You can't write against a read only slave.
        127.0.0.1:6380> 
        # 從服務器只能讀數據,不能寫數據
        
      • 方式1:命令行實現2

        # 服務端啓動
        redis-server --port 6380
        # 客戶端連接
        dayin@fafu:~$ redis-cli -p 6300
        127.0.0.1:6300> keys *
        1) "myset"
        2) "mylist"
        127.0.0.1:6300> set mykey 123
        OK
        # 切換爲從
        127.0.0.1:6300> slaveof 127.0.0.1 6379
        OK
        127.0.0.1:6300> set newkey 456
        (error) READONLY You can't write against a read only slave.
        127.0.0.1:6300> keys *
        1) "myset"
        2) "mylist"
        # 再切換爲主
        127.0.0.1:6300> slaveof no one
        OK
        127.0.0.1:6300> set name hello
        OK
        
      • 方式2:修改配置文件

        # 修改配置文件
        vi redis_6300.conf
        slaveof 127.0.0.1 6379
        port 6300
        # 啓動redis服務
        redis-server redis_6300.conf
        # 客戶端連接測試
        redis-cli -p 6300
        127.0.0.1:6300> hset user_001 username dayin
        (error) READONLY You can't write against a read only slave.
        
      • 問題總結

        1、一個Master可以有多個Slaves
        2、Slave下線,只是讀請求的處理性能下降
        3、Master下線,寫請求無法執行
        4、其中一臺Slave使用SLAVEOF no one命令成爲Master,其他Slaves執行SLAVEOF命令指向這個新的Master,從它這裏同步數據
        5、如果主服務器中有設置密碼,需要去配置文件中修改 masterauth xxx(redis服務器密碼)
        # 以上過程是手動的,能夠實現自動,這就需要Sentine哨兵,實現故障轉移Failover操作
        
      • 演示

        1、啓動端口6400redis,設置爲6379的slave
           redis-server --port 6400
           redis-cli -p 6400
           redis>slaveof 127.0.0.1 6379
        2、啓動端口6401redis,設置爲6379的slave
           redis-server --port 6401
           redis-cli -p 6401
           redis>slaveof 127.0.0.1 6379
        3、關閉6379redis
           sudo /etc/init.d/redis-server stop
        4、把6400redis設置爲master
           redis-cli -p 6401
           redis>slaveof no one
        5、把6401的redis設置爲6400redis的salve
           redis-cli -p 6401
           redis>slaveof 127.0.0.1 6400
        # 這是手動操作,效率低,而且需要時間,有沒有自動的???
        
    • 官方高可用方案Sentinel

      • Redis之哨兵 - sentinel

        1、Sentinel會不斷檢查Master和Slaves是否正常
        2、每一個Sentinel可以監控任意多個Master和該Master下的Slaves
        
      • 案例演示

        • 1、環境搭建

          # 共3臺redis的服務器,如果是不同機器端口號可以是一樣的
          1、啓動6379的redis服務器
             	sudo /etc/init.d/redis-server start
          2、啓動6380的redis服務器,設置爲6379的從
              redis-server --port 6380
              dayin@fafu:~$ redis-cli -p 6380
              127.0.0.1:6380> slaveof 127.0.0.1 6379
              OK
          3、啓動6381的redis服務器,設置爲6379的從
             	redis-server --port 6381
             	dayin@fafu:~$ redis-cli -p 6381
             	127.0.0.1:6381> slaveof 127.0.0.1 6379
          
        • 2、安裝並搭建sentinel哨兵

          # 1、安裝redis-sentinel
          sudo apt install redis-sentinel
          
          # 2、新建配置文件sentinel.conf
          port 27777
          Sentinel monitor dayin 127.0.0.1 6379 1
          
          # 3、啓動sentinel
          方式一: redis-sentinel sentinel.conf
          方式二: redis-server sentinel.conf --sentinel
          
          # 將master的redis服務終止,查看從是否會提升爲主
          sudo /etc/init.d/redis-server stop
          # 發現提升6381爲master,其他兩個爲從
          # 在6381上設置新值,6380查看
          127.0.0.1:6381> set name dayin
          OK
          
          # 啓動6379,觀察日誌,發現變爲了6381的從
          主從+哨兵基本就夠用了
          
        • sentinel.conf解釋

          # sentinel監聽端口,默認是26379,可以修改
          port 27777
          # 告訴sentinel去監聽地址爲ip:port的一個master,這裏的master-name可以自定義,quorum是一個數字,指明當有多少個sentinel認爲一個master失效時,master纔算真正失效
          sentinel monitor <master-name> <ip> <redis-port> <quorum>
          

  • Redis 分佈式鎖解決高併發問題

    • 案例引入

      1. 創建Django項目
      2. 配置Mysql
      3. 創建模型層,主要創建一個score字段,整型字段。
      4. 執行遷移文件
      5. 配置路由
      6. 編寫視圖函數
      7. 啓動多個服務端,這裏啓動 兩個8000和8001端口
      8. 編寫測試函數,模擬向這兩個服務器隨機發送30個請求
      9. 觀察數據庫中score字段發現並沒有增加30
      10.使用redis分佈式鎖解決問題 
      
    • 測試代碼:

      import requests
      from django.test import TestCase
      import random
      import threading
      
      
      # Create your tests here.
      def getRequest():
          url1 = 'http://127.0.0.1:8000/test_api'
          url2 = 'http://127.0.0.1:8001/test_api'
          url = random.choice([url1, url2])
          r = requests.get(url)
          print(r.text)
      
      
      t_list = []
      
      for i in range(30):
          t = threading.Thread(target=getRequest)
          t.start()
          t_list.append(t)
      
      for i in t_list:
          i.join()
      
    • 視圖函數

      # author:dayin
      # Date:2019/11/1 0001
      import redis
      from django.http import HttpResponse
      from user.models import UserProfile
      
      
      def test_api(request):
          # redis分佈式鎖
          pool = redis.ConnectionPool(
              host='127.0.0.1',
              port=6379,
              password='chendayin'
          )
          r = redis.Redis(connection_pool=pool)
      
          try:
              with r.lock(name='username', blocking_timeout=3) as lock:
                  u = UserProfile.objects.get(username='dayin')
                  u.score += 1
                  u.save()
                  return HttpResponse(u.score)
          except Exception as e:
              print(e)
      
      

end…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章