淺談工做中celery與Redis遇到的一些問題

celery的內存泄漏?  

  總結:   celery執行完任務不釋放內存與原worker一直沒有被銷燬有關,所以CELERYD_MAX_TASKS_PER_CHILD能夠適當配置小點,而任務併發數與CELERYD_CONCURRENCY配置項有關,   python

  每增長一個worker必然增長內存消耗,同時也影響到一個worker什麼時候被銷燬,由於celery是均勻調度任務至每一個worker,所以也不宜配置過大,適當配置。  程序員

     CELERYD_MAX_TASKS_PER_CHILD redis

      CELERYD_CONCURRENCY = 20  # 併發worker數 算法

      CELERYD_FORCE_EXECV = True    # 很是重要,有些狀況下能夠防止死鎖  sql

      CELERYD_MAX_TASKS_PER_CHILD = 100    # 每一個worker最多執行萬100個任務就會被銷燬,可防止內存泄露  數據庫

      CELERYD_TASK_TIME_LIMIT = 60    # 單個任務的運行時間不超過此值,不然會被SIGKILL 信號殺死   django

    任務發出後,通過一段時間還未收到acknowledge , 就將任務從新交給其餘worker執行 編程

      CELERY_DISABLE_RATE_LIMITS = True json

 

 在Django中使用celery內存泄漏問題?   

    在django下使用celery做爲異步任務系統,十分方便。flask

    同時celery也提供定時任務機制,celery beat。使用celery beat 能夠爲咱們提供 cron,schedule 形式的定時任務。

    在django下使用celery beat的過程當中,發現了 celery beat進程 佔用內存很是大,並且一直不釋放。

    懷疑其有內存佔用不釋放的可能。

    由於以前使用django的時候,就知道在django中開啓DEBUG模式,會爲每次的SQL查詢 緩存結果。   celery beat 做爲 定時任務的timer和heartbeat程序,是長期運行的,而我使用了MYSQL做爲存儲定時任務的backend。

    由於每次heartbeat和timer產生的sql查詢在開啓了DEBUG模式下的django環境中,都會緩存查詢結果集。所以 celery beat佔用的 內存會一直不釋放。在個人線上環境中 達到10G內存佔用!

       解決: 關掉django的DEBUG模式,在setting中,設置DEBUG=False 便可。   關閉DEBUG模式後的celery beat程序 的內存佔用大概 一直維持在150M左右。

       

 

數據庫鏈接是單利嗎? 有必要實現多例嗎?

     單例數據庫鏈接在鏈接池中只有一個實例,對系統資源的開銷比較少,甚至能夠長時間保持鏈接不回收,以節省建立鏈接和回收鏈接的時間。  

   但這樣的鏈接在多用戶併發時不能提供足夠的效率,形象的來說就是你們要排隊。  初級程序員的做法是每一個用戶需求過來都打開一次鏈接,用完回收掉。  

   進階的作法是創建一個鏈接池,鏈接池裏面給定一些已打開的鏈接,用程序控制這些鏈接的分配與調度  

      數據庫連接用單例模式的緣由:

        單例只保留一個對象,能夠減小系統資源開銷。

   提升建立速度,每次都獲取已經存在的對象所以提升建立速度全局共享對象。

   單例在系統中只存在一個對象實例,所以任何地方使用此對象都是一個對象避免多實例建立使用時產生的邏輯錯誤。

   例模式是一種經常使用的軟件設計模式,它的核心結構只包含一個被稱爲單例的特殊類。它的目的是保證一個類僅有一個實例,並提供一個訪問它的全局訪問點,該實例被全部程序模塊共享。

   單例模式有3種實現方式:懶漢式、餓漢式和雙重鎖的形式。

 

  單例模式優勢 只容許建立一個對象,所以節省內存,加快對象訪問速度,所以對象須要被公用的場合適合使用,如多個模塊使用同一個數據源鏈接對象等等  由於類控制了實例化過程,因此類能夠靈活更改實例化過程   

  單例模式會阻止其餘對象實例化其本身的單例對象的副本,從而確保全部對象都訪問惟一實例。  單例的缺點  就是不適用於變化的對象,若是同一類型的對象老是要在不一樣的用例場景發生變化,

  單例就會引發數據的錯誤,不能保存彼此的狀態。 用單例模式,就是在適用其優勢的狀態下使用。    

    好比:   要進一個房間(數據庫),就爲這個房間開了一扇門(數據庫類),通常狀況下是一我的開一扇門,   無論你進出(數據庫操做)這個房間多少次,門就這一扇(單例),

    固然一我的也能夠開不少扇門(非單例),   但你知道一個房間能開的門的數量是有限的,所以你不使用單例的話,一是性能慢一些,二是走別人的門,讓別人無門可進。。。

 

 

數據類型的堆棧存儲?  

   堆棧是一個後進先出的數據結構,其工做方式就像一堆汽車排隊進去一個死衚衕裏面,最早進去的必定是最後出來。  

   隊列是一種先進先出的數據類型,它的跟蹤原理相似於在超市收銀處排隊,隊列裏的的第一我的首先接受服務,新的元素經過入隊的方式添加到隊列的末尾,而出隊就是將隊列的頭元素刪除。  

   棧:是一種容器,可存入數據元素、訪問元素、刪除元素  

   特色:只能從頂部插入(入棧)數據和刪除(出棧)數據  

   原理:LIFO(Last In First Out)後進先出  棧可使用順序表實現也可以使用鏈表實現 使用python列表實現代碼:  class Stack(object):   """   棧   使用python列表實現   """

      def __init__(self):    self.items = list()

      def is_empty(self):    """判空"""    return self.items == []

      def size(self):    """獲取棧元素個數"""    return len(self.items)

      def push(self, item):    """入棧"""    self.items.append(item)

      def pop(self):    """出棧"""    self.items.pop()

      def peek(self):    """獲取棧頂元素"""    if self.is_empty():     raise IndexError("stack is empty")    return self.items[-1]      

  

 

flask的jwt?  

  一篇文章需求分析:(Flask + flask-jwt 實現基於Json Web Token的用戶認證受權)

   jwt是flask的一個第三方庫:flask-jwt-------->能夠實現基於Json Web Token的用戶認證受權

   使用 JWT 讓你的 RESTful API 更安全  什麼是 JWT ?   

    JWT 及時 JSON Web Token,它是基於 RFC 7519 所定義的一種在各個系統中傳遞緊湊和自包含的 JSON 數據形式。   

    緊湊(Compact) :因爲傳送的數據小,JWT 能夠經過GET、POST 和 放在 HTTP 的 header 中,同時也是由於小也能傳送的更快。   

    自包含(self-contained) : Payload 中可以包含用戶的信息,避免數據庫的查詢。  JSON Web Token 由三部分組成使用 .

    分割開:   Header   Payload   Signature     一個 JWT 形式上相似於下面的樣子:   xxxxx.yyyy.zzzz     

    Header 通常由兩個部分組成:   alg   typ     alg 是是所使用的 hash 算法例如 HMAC SHA256 或 RSA,typ 是 Token 的類型天然就是 JWT。   {     "alg": "HS256",     "typ": "JWT"   }         

  JSON Web Token 的工做流程: 

     在用戶使用證書或者帳號密碼登入的時候一個 JSON Web Token 將會返回,同時能夠把這個 JWT 存儲在local storage、或者 cookie 中,用來替代傳統的在服務器端建立一個 session 返回一個 cookie。  

     當用戶想要使用受保護的路由時候,應該要在請求得時候帶上 JWT ,通常的是在 header 的 Authorization 使用 Bearer 的形式,一個包含的 JWT 的請求頭的 Authorization 以下:

         Authorization: Bearer <token>  

       這是一中無狀態的認證機制,用戶的狀態歷來不會存在服務端,在訪問受保護的路由時候回校驗 HTTP header 中 Authorization 的 JWT,

      同時 JWT 是會帶上一些必要的信息,不須要屢次的查詢數據庫。

      這種無狀態的操做能夠充分的使用數據的 APIs,甚至是在下游服務上使用,這些 APIs 和哪服務器沒有關係,

      所以,因爲沒有 cookie 的存在,因此在不存在跨域(CORS, Cross-Origin Resource Sharing)的問題。

 

 

gil鎖的侷限性和打破方式?

    侷限性:   

      在Cpython解釋器中,同一個進程下開啓的多線程,同一時刻只能有一個線程執行,沒法利用多核優點   

      GIL存在緣由   CPython在執行多線程的時候並非線程安全的,因此爲了程序的穩定性,加一把全局解釋鎖,可以確保任什麼時候候都只有一個Python線程執行。   

      GIL的弊端

           GIL對計算密集型的程序會產生影響。由於計算密集型的程序,須要佔用系統資源。GIL的存在,至關於始終在進行單線程運算,這樣天然就慢了。

        IO密集型影響不大的緣由在於,IO,input/output,這兩個詞就代表程序的瓶頸在於輸入所耗費的時間,線程大部分時間在等待,因此它們是多個一塊兒等(多線程)仍是單個等(單線程)無所謂的。  

         這就比如,你在公交站等公交時,大家排隊等公交(單線程)仍是沿着馬路一字排開等(多線程)是無所謂的。公交車(即input,即輸入的資源)沒來,哪一種方式都是瞎折騰。

    解決方案(打破方式)   

      multiprocessing   

      multiprocessing是一個多進程模塊,開多個進程,每一個進程都帶一個GIL,就至關於多線程來用了。   

      multiprocessing的弊端   多線程與多進程一個不一樣點在於:

           多線程是共享內存的,即這些線程共用一個內存地址。好處在於便於線程間數據通訊和數據同步。   

        多進程,各個進程地址之間是獨立的內存地址。這樣不存內存地址之間通訊就麻煩了。  

        綜上所述,若是是IO密集型且對數據通訊有需求,使用python 的threading模塊也是能夠的。              

    解決方法:   1. 使用多進程執行,此將要面臨解決共享數據的問題,多用queue或pipe解決;   2. 使用Python多線程load C的module執行。

          from ctypes import *   form threading import Thread

          #加載動態庫   lib = cdll.LoadLibrary("./libdeadloop.so")

          #建立一個子線程,讓其執行c語言編寫的函數,此函數是一個死循環   t = Thread(target=lib.DeadLoop)   t.start()

          while True:    pass

 網友博客理解:   

  IL是限制同一個進程中只有一個線程進入Python解釋器。。。。。   

  而線程鎖是因爲在線程進行數據操做時保證數據操做的安全性(同一個進程中線程之間能夠共用信息,若是同時對數據進行操做,則會出現公共數據錯誤)  

  其實線程鎖徹底能夠替代GIL,可是Python的後續功能模塊都是加在GIL基礎上的,因此沒法更改或去掉GIL,這就是Python語言最大的bug…只能用多進程或協程改善,或者直接用其餘語言寫這部分   

  追問         

  GIL本質就是一把互斥鎖,既然是互斥鎖,全部互斥鎖的本質都同樣,都是將併發運行變成串行,以此來控制同一時間內共享數據只能被一個任務所修改,進而保證數據安全。   

  保護不一樣的數據的安全,就應該加不一樣的鎖。    

  每執行一個python程序,就是開啓一個進程,在一個python的進程內,不只有其主線程或者由該主線程開啓的其餘線程,還有解釋器開啓的垃圾回收等解釋器級別的線程,   全部的線程都運行在這一個進程內,

    因此:    

    一、全部數據都是共享的,這其中,代碼做爲一種數據也是被全部線程共享的(test.py的全部代碼以及Cpython解釋器的全部代碼)    

    二、全部線程的任務,都須要將任務的代碼當作參數傳給解釋器的代碼去執行,即全部的線程要想運行本身的任務,首先須要解決的是可以訪問到解釋器的代碼。       

  在python的原始解釋器CPython中存在着GIL(Global Interpreter Lock,全局解釋器鎖),所以在解釋執行python代碼時,會產生互斥鎖來限制線程對共享資源的訪問,

  直到解釋器遇到I/O操做或者操做次數達到必定數目時纔會釋放GIL。   因此,雖然CPython的線程庫直接封裝了系統的原生線程,但CPython總體做爲一個進程,同一時間只會有一個得到GIL的線程在跑,

  其餘線程則處於等待狀態。   這就形成了即便在多核CPU中,多線程也只是作着分時切換而已。   不過muiltprocessing的出現,已經可讓多進程的python代碼編寫簡化到了相似多線程的程度了

 

我對 GIL的理解:  

  解決多線程之間數據完整性和狀態同步的最簡單方法天然就是加鎖。   

  GIL鎖開始運做主線程作操做主線程完成操做GIL鎖釋放資源   因此多線程共同操做共享資源的時候,有一個線程競得了資源,它就被GIL鎖保護起來,其餘線程只能是在那裏等着,

  可是這個時候,線程的休眠喚醒,所有會消耗CPU資源,因此嘞,就會慢。   Python語言和GIL解釋器鎖沒有關係,它是在實現Python解析器(CPython)時所引入的一個概念,

  一樣一段代碼能夠經過CPython,PyPy,Psyco等不一樣的Python執行環境來執行,然而由於CPython是大部分環境下默認的Python執行環境。因此在不少人的概念裏CPython就是Python,

  也就想固然的把GIL歸結爲Python語言的缺陷,全部GIL並非python的特性,僅僅是由於歷史緣由在Cpython解釋器中難以移除。

     GIL保證同一時刻只有一個線程執行代碼,每一個線程在執行過程當中都要先獲取GIL

 

    線程釋放GIL鎖的狀況:

     在IO操做等可能會引發阻塞的system call以前,能夠暫時釋放GIL,但在執行完畢後,必須從新獲取GIL Python 3.x使用計時器(執行時間達到閾值後,當前線程釋放GIL)或Python 2.x,tickets計數達到100

     Python使用多進程是能夠利用多核的CPU資源的。   多線程爬取比單線程性能有提高,由於遇到IO阻塞會自動釋放GIL鎖

     GIL只對計算密集型的程序有做用,對IO密集型的程序並無影響,由於遇到IO阻塞會自動釋放GIL鎖   當須要執行計算密集型的程序時,

     能夠選擇:1.換解釋器,2.擴展C語言,3.換多進程等方案

 

    GIL(Global Interpreter Lock):全局解釋器鎖,python解釋器在執行python字節碼的時候會鎖住解釋器,致使其它的線程不能使用解釋器,從而多線程狀況下CPU上不去。  

    lupa是一個python調用lua的第三方庫(https://pypi.python.org/pypi/lupa),lua_code是一段純CPU計算的lua代碼片斷,以後開啓了3個線程,能夠發現CPU利用率達到了300%  以前在寫一些python程序的時候,

    若是是cpu密集的經常會使用多進程的方式,可是這樣會有一些缺點:   

      1. 進程間共享數據特別麻煩,雖然multiprocessing庫提供了不少進程間共享數據的方法,可是這些方法最後本身會成爲瓶頸

         2. 編程複雜度比較高

         3. 主進程和子進程必然須要通訊,進程間數據隔離,因此數據須要內存拷貝,成本高  相應的python代碼:

          #python lupa load

          import lupa   lua = lupa.LuaRuntime()

          LIBS = [    "./scripts/foo.lua",   ]  

          llibs = {}

         def get_file_name(filename):    

          import os    

          (_, tmp) = os.path.split(filename)    

          (f_name, ext) = os.path.splittext(tmp)     

          return f_name       

         def load_libs():   

          global LIBS, llibs    for lib_p in LIBS:     f = open(lib_p, 'r')     

          code_str = f.readlines()     filename = get_file_name(lib_p)     

          llibs[filename] = lua.execute('\n'.join(code_str))        

        if __name__ == '__main__':   

           load_libs()    

          print llibs['foo'].sayhi()    

          print llibs['foo'].callback(100, 200, 300, 400)     

          --foo.lualibfoo = {}

             function libfoo.sayhi()    

          return "hi from lupa"   end  function libfoo.callback(a, b, c, d)    return a * b + c - d   endreturn libfoo  

  這樣作有幾點好處:

     1. python寫框架,lua寫回調,每次調用一遍load_libs就至關於一次熱更新

     2. lua代碼自己特別簡單,能夠交給策劃配置,與熱更新結合效果更好

     3. python多線程結合lua使用能夠突破python GIL的限制,後面補充一個demo    

        線程安全:   線程安全就是多線程訪問時,採用了加鎖機制,當一個線程訪問該類的某個數據時,進行保護,其餘線程不能進行訪問直到該線程讀取完,其餘線程纔可以使用。   不會出現數據不一致或者數據污染。

        線程不安全:就是不提供數據訪問保護,有可能出現多個線程前後更改數據形成所獲得的數據是髒數據。

 

      

celery在某一時刻忽然執行2回? 爲何? 否 怎麼解決?  

    Celery是一個用Python開發的異步的分佈式任務調度模塊    

  網友遇到的工做問題以及解決方案:   

    使用 Celery Once 來防止 Celery 重複執行同一個任務   在使用 Celery 的時候發現有的時候 Celery 會將同一個任務執行兩遍,我遇到的狀況是相同的任務在不一樣的 worker 中被分別執行,而且時間只相差幾毫秒。

    這問題我一直覺得是本身哪裏處理的邏輯有問題,後來發現其餘人 也有相似的問題,而後基本上出問題的都是使用 Redis 做爲 Broker 的,而我這邊一方面不想將 Redis 替換掉,就只能在 task 執行的時候加分佈式鎖了。

      不過在 Celery 的 issue 中搜索了一下,有人使用 Redis 實現了分佈式鎖,而後也有人使用了 Celery Once。 大體看了一下 Celery Once ,發現很是符合如今的狀況,就用了下。

    Celery Once 也是利用 Redis 加鎖來實現, Celery Once 在 Task 類基礎上實現了 QueueOnce 類,該類提供了任務去重的功能,

    因此在使用時,咱們本身實現的方法須要將 QueueOnce 設置爲 base   @task(base=QueueOnce, once={'graceful': True})   

    後面的 once 參數表示,在遇到重複方法時的處理方式,默認 graceful 爲 False,那樣 Celery 會拋出 AlreadyQueued 異常,手動設置爲 True,則靜默處理。

       另外若是要手動設置任務的 key,能夠指定 keys 參數

       @celery.task(base=QueueOnce, once={'keys': ['a']})   def slow_add(a, b):    sleep(30)    return a + b

 

 

celery 任務忽然不執行是爲何?  

  問題:   

    別人推送不少消息給我,用 tornado 接收而後傳到 celery 裏面處理   celery 進程剛啓動仍是沒問題,運行一天半天 忽然裏面的任務都不處理了   從新啓動下 就能把以前接收到的推送 一個個繼續處理。。。   

    看不出是什麼問題。。   打算修改下配置的處理任務的超時時間,看看能不能解決這個問題。  

  目前解決方案:   

    在服務器加一個定時刷新,開啓時間長,處理大量消息,出現任務超時,端口占用,任務沒有放掉,致使後期任務沒法執行,須要服務器定時任務重啓  

  問題:   

    最近在寫一個分佈式微博爬蟲,主要就是使用celery作的分佈式任務調度。celery確實比較好用,可是也遇到一些問題,我遇到的問題主要集中在定時任務和任務路由這兩個部分。   

    本文不會講解celery的基本使用,若是須要看celery入門教程的話,請點擊這裏跳轉。   celery worker -A app_name -l info必須推薦在項目的根目錄運行並且,這裏的app_name必須是項目中的Celery實例的完整引用路徑*。

    若是不在項目根目錄運行,那麼相關的調用也得切換到app同級目錄下,這一點能夠經過命令行進行佐證      

    celery的定時任務會有必定時間的延遲。好比,我規定模擬登錄新浪微博任務每隔10個小時執行一次,那麼定時任務第一次執行就會在開啓定時任務以後的10個小時後纔會執行。   

    而我抓取微博須要立刻執行,須要帶上cookie,因此不能等那1個小時。這個沒有一個比較好的解決方法,可使用celery的crontab()來代替schdule作定時,它會在啓動的時候就執行。   

  解決方案

    我採用的方法是第一次手動執行該任務,而後再經過schedule執行。      celery的定時任務可能會讓任務重複。定時器必定只能在一個節點啓動,不然會形成任務重複。   

    另外,若是當前worker節點都中止了,而beat在以後才中止,那麼下一次啓動worker的時候,它還會執行上一次未完成的任務,可能會有重複。   

    因爲抓取用戶和抓取用戶關注、粉絲的任務耗時和工做量不一樣,因此須要使用任務路由,將任務按比重合理分配到各個分佈式節點上,這就須要使用到celery提供的task queue。   

    若是單獨使用task queue還好,可是和定時任務一塊兒使用,就可能出現問題。我遇到的問題就是定時任務壓根就不執行!開始個人配置大概就是這樣    

      app.conf.update(     

        CELERY_TIMEZONE='Asia/Shanghai',     

        CELERY_ENABLE_UTC=True,     

        CELERY_ACCEPT_CONTENT=['json'],     

        CELERY_TASK_SERIALIZER='json',      

        CELERY_RESULT_SERIALIZER='json',     

        CELERYBEAT_SCHEDULE={      

             'user_task': {       'task': 'tasks.user.excute_user_task',       'schedule': timedelta(minutes=3),      },     

             'login_task': {       'task': 'tasks.login.excute_login_task',       'schedule': timedelta(hours=10),      },    

             },     

        CELERY_QUEUES=(     

             Queue('login_queue', exchange=Exchange('login', type='direct'), routing_key='for_login'),     

             Queue('user_crawler', exchange=Exchange('user_info', type='direct'), routing_key='for_user_info'),      

             Queue('fans_followers', exchange=Exchange('fans_followers', type='direct'), routing_key='for_fans_followers')     )    )

 

    結果過了一天發現定時任務並無執行,後來把task加上了一個option字段,指定了任務隊列,就能夠了,

    好比     'user_task': {      'task': 'tasks.user.excute_user_task',      'schedule': timedelta(minutes=3),      'options': {'queue': 'fans_followers', 'routing_key': 'for_fans_follwers'}     },

      部分分佈式節點一直出現Received task,可是卻不執行其中的任務的狀況。這種狀況下重啓worker節點通常就能夠恢復。   

    可是最好查查緣由。經過查看flower的失敗任務信息,才發現是插入數據的時候有的異常未被處理。這一點嚴格說來並非celery的bug,不過也很使人費解。   

    因此推薦在使用celery的時候配合使用flower作監控。

 

 

 

Redis是否保證事務的一致性?  

    網友遇到問題:   

      」redis設計之初是簡單高效,因此說在事務操做時命令是不會出錯的,出錯的可能性就是程序的問題「   那這樣的意思就是把鍋拋給程序咯?    

      若是程序能保證百分之百不出錯那麼關係型數據庫還要啥事務呢?

      redis事務報錯時仍會執行全部命令,這樣怎麼保證一致性呢?    

      或者說白了redis根本就不支持事務只是冠以事務的名號而已。以上純屬我的看法   又專業人士能夠解釋你們討論。  

    網友理解:   

      一、Redis事務主要用於不間斷執行多條命令,便是存在引起錯誤的命令。Redis先執行命令,命令執行成功後纔會記錄日誌,因此出現錯誤時沒法回滾。     

        支持完整的acid會讓Redis變得複雜也可能致使性能較低。 此外,使用lua腳本也能夠保證Redis不間斷執行多條命令。   

      二、找到網上這個解釋比較到位:      單個 Redis 命令的執行是原子性的,但 Redis 沒有在事務上增長任何維持原子性的機制,因此 Redis 事務的執行並非原子性的。     

        事務能夠理解爲一個打包的批量執行腳本,但批量指令並不是原子化的操做,中間某條指令的失敗不會致使前面已作指令的回滾,也不會形成後續的指令不作。    

 

 

 redis事務

      一、基本概念

        1)什麼是redis的事務?

          簡單理解,能夠認爲redis事務是一些列redis命令的集合,而且有以下兩個特色:

          a)事務是一個單獨的隔離操做:事務中的全部命令都會序列化、按順序地執行。事務在執行的過程當中,不會被其餘客戶端發送來的命令請求所打斷。

          b)事務是一個原子操做:事務中的命令要麼所有被執行,要麼所有都不執行。

  

        2)事務的性質ACID

          通常來講,事務有四個性質稱爲ACID,分別是原子性,一致性,隔離性和持久性。

          a)原子性atomicity:redis事務保證事務中的命令要麼所有執行要不所有不執行。有些文章認爲redis事務對於執行錯誤不回滾違背了原子性,是偏頗的。

          b)一致性consistency:redis事務能夠保證命令失敗的狀況下得以回滾,數據能恢復到沒有執行以前的樣子,是保證一致性的,除非redis進程意外終結。

          c)隔離性Isolation:redis事務是嚴格遵照隔離性的,緣由是redis是單進程單線程模式,能夠保證命令執行過程當中不會被其餘客戶端命令打斷。

          d)持久性Durability:redis事務是不保證持久性的,這是由於redis持久化策略中無論是RDB仍是AOF都是異步執行的,不保證持久性是出於對性能的考慮。

 

        3)redis事務的錯誤

          使用事務時可能會趕上如下兩種錯誤:

            a)入隊錯誤:事務在執行 EXEC 以前,入隊的命令可能會出錯。好比說,命令可能會產生語法錯誤(參數數量錯誤,參數名錯誤,等等),

              或者其餘更嚴重的錯誤,好比內存不足(若是服務器使用 maxmemory 設置了最大內存限制的話)。

            b)執行錯誤:命令可能在 EXEC 調用以後失敗。舉個例子,事務中的命令可能處理了錯誤類型的鍵,好比將列表命令用在了字符串鍵上面,諸如此類。

            注:第三種錯誤,redis進程終結,本文並無討論這種錯誤。

  

     二、redis事務的用法

         redis事務是經過MULTI,EXEC,DISCARD和WATCH四個原語實現的。

         MULTI命令用於開啓一個事務,它老是返回OK。

         MULTI執行以後,客戶端能夠繼續向服務器發送任意多條命令,這些命令不會當即被執行,而是被放到一個隊列中,當EXEC命令被調用時,全部隊列中的命令纔會被執行。

         另外一方面,經過調用DISCARD,客戶端能夠清空事務隊列,並放棄執行事務。

 

 

 

 

Redis的持久化方式?  

    兩種持久化方式:

      RDB和AOF  深刻了解RDB和AOF的做用原理,剩下的就是根據實際狀況來制定合適的策略了,再複雜一點,也就是定製一個高可用的,數據安全的策略了。

   

      在RDB方式下,你有兩種選擇,一種是手動執行持久化數據命令來讓redis進行一次數據快照,另外一種則是根據你所配置的配置文件 的 策略,達到策略的某些條件時來自動持久化數據。  

      而手動執行持久化命令,你依然有兩種選擇,那就是save命令和bgsave命令。   save操做在Redis主線程中工做,所以會阻塞其餘請求操做,應該避免使用。   

      bgSave則是調用Fork,產生子進程,父進程繼續處理請求。子進程將數據寫入臨時文件,並在寫完後,替換原有的.rdb文件。

      Fork發生時,父子進程內存共享,因此爲了避免影響子進程作數據快照,在這期間修改的數據,將會被複制一份,而不進共享內存。  

      因此說,RDB所持久化的數據,是Fork發生時的數據。在這樣的條件下進行持久化數據,若是由於某些狀況宕機,則會丟失一段時間的數據。

      若是你的實際狀況對數據丟失沒那麼敏感,丟失的也能夠從傳統數據庫中獲取或者說丟失部分也無所謂,那麼你能夠選擇RDB持久化方式。  

      再談一下配置文件的策略,實際上它和bgsave命令持久化原理是相同的。    

 

      AOF持久化方式:

      配置文件中的appendonly修改成yes。開啓AOF持久化後,你所執行的每一條指令,都會被記錄到appendonly.aof文件中。  

      但事實上,並不會當即將命令寫入到硬盤文件中,而是寫入到硬盤緩存,在接下來的策略中,配置多久來從硬盤緩存寫入到硬盤文件。

      因此在必定程度必定條件下,仍是會有數據丟失,不過你能夠大大減小數據損失。  這裏是配置AOF持久化的策略。

      redis默認使用everysec,就是說每秒持久化一次,而always則是每次操做都會當即寫入aof文件中。  

      而no則是不主動進行同步操做,是默認30s一次。固然always必定是效率最低的,我的認爲everysec就夠用了,數據安全性能又高。

 

 

   Redis也容許咱們同時使用兩種方式,再重啓redis後會從aof中恢復數據,由於aof比rdb數據損失小嘛。    

      深刻理解Redis的兩種持久化方式:  RDB每次進行快照方式會從新記錄整個數據集的全部信息。RDB在恢復數據時更快,能夠最大化redis性能,子進程對父進程無任何性能影響。

      AOF有序的記錄了redis的命令操做。意外狀況下數據丟失甚少。他不斷地對aof文件添加操做日誌記錄,你可能會說,這樣的文件得多麼龐大呀。

      是的,的確會變得龐大,但redis會有優化的策略,好比你對一個key1鍵的操做,set key1 001 ,  set key1 002, set key1 003。那優化的結果就是將前兩條去掉咯,

      那具體優化的配置在配置文件中對應的是  https://images2015.cnblogs.com/blog/686162/201608/686162-20160809211516715-145676984.png   

      前者是指超過上一次aof重寫aof文件大小的百分之多少,會再次優化,若是沒有重寫過,則以啓動時爲主。

      後者是限制了容許重寫的最小aof文件大小。bgrewriteaof命令是手動重寫命令,會fork子進程,在臨時文件中重建數據庫狀態,對原aof無任何影響,

      當重建舊的狀態後,也會把fork發生後的一段時間內的數據一併追加到臨時文件,最後替換原有aof文件,新的命令繼續向新的aof文件中追加。

 

  Redis數據庫簡介:    

      Redis是一種高級key-value數據庫。它跟memcached相似,不過數據能夠持久化,並且支持的數據類型很豐富。

      有字符串,鏈表,集 合和有序集合。支持在服務器端計算集合的並,交和補集(difference)等,還支持多種排序功能。

      因此Redis也能夠被當作是一個數據結構服務 器。   Redis的全部數據都是保存在內存中,而後不按期的經過異步方式保存到磁盤上(這稱爲「半持久化模式」);

      也能夠把每一次數據變化都寫入到一個append only file(aof)裏面(這稱爲「全持久化模式」)。   

  Redis數據庫簡介:   

      默認使用everysec,就是說每秒持久化一次,而always則是每次操做都會當即寫入aof文件中。

      默認使用everysec,就是說每秒持久化一次,而always則是每次操做都會當即寫入aof文件中。

      因爲Redis的數據都存放在內存中,若是沒有配置持久化,redis重啓後數據就全丟失了,因而須要開啓redis的持久化功能,將數據保存到磁盤上,

      當redis重啓後,能夠從磁盤中恢復數據。redis提供兩種方式進行持久化,一種是RDB持久化(原理是將Reids在內存中的數據庫記錄定時dump到磁盤上的RDB持久化),

      另一種是AOF(append only file)持久化(原理是將Reids的操做日誌以追加的方式寫入文件)。

      那麼這兩種持久化方式有什麼區別呢,改如何選擇呢?網上看了大多數都是介紹這兩種方式怎麼配置,怎麼使用,就是沒有介紹兩者的區別,在什麼應用場景下使用。

 

 

兩者的區別   RDB存在哪些優點呢?

       1). 一旦採用該方式,那麼你的整個Redis數據庫將只包含一個文件,這對於文件備份而言是很是完美的。

        好比,你可能打算每一個小時歸檔一次最近24小時的數據,同時還要天天歸檔一次最近30天的數據。

        經過這樣的備份策略,一旦系統出現災難性故障,咱們能夠很是容易的進行恢復。

       2). 對於災難恢復而言,RDB是很是不錯的選擇。由於咱們能夠很是輕鬆的將一個單獨的文件壓縮後再轉移到其它存儲介質上。

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

       4). 相比於AOF機制,若是數據集很大,RDB的啓動效率會更高。

 

     RDB又存在哪些劣勢呢?

       1). 若是你想保證數據的高可用性,即最大限度的避免數據丟失,那麼RDB將不是一個很好的選擇。由於系統一旦在定時持久化以前出現宕機現象,此前沒有來得及寫入磁盤的數據都將丟失。

       2). 因爲RDB是經過fork子進程來協助完成數據持久化工做的,所以,若是當數據集較大時,可能會致使整個服務器中止服務幾百毫秒,甚至是1秒鐘。

 

     AOF的優點有哪些呢?

       1). 該機制能夠帶來更高的數據安全性,即數據持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不一樣步。

        事實上,每秒同步也是異步完成的,其效率也是很是高的,所差的是一旦系統出現宕機現象,那麼這一秒鐘以內修改的數據將會丟失。

        而每修改同步,咱們能夠將其視爲同步持久化,即每次發生的數據變化都會被當即記錄到磁盤中。能夠預見,這種方式在效率上是最低的。至於無同步,無需多言,我想你們都能正確的理解它。

       2). 因爲該機制對日誌文件的寫入操做採用的是append模式,所以在寫入過程當中即便出現宕機現象,也不會破壞日誌文件中已經存在的內容。

        然而若是咱們本次操做只是寫入了一半數據就出現了系統崩潰問題,不用擔憂,在Redis下一次啓動以前,咱們能夠經過redis-check-aof工具來幫助咱們解決數據一致性的問題。

       3). 若是日誌過大,Redis能夠自動啓用rewrite機制。即Redis以append模式不斷的將修改數據寫入到老的磁盤文件中,同時Redis還會建立一個新的文件用於記錄此期間有哪些修改命令被執行。

        所以在進行rewrite切換時能夠更好的保證數據安全性。

       4). AOF包含一個格式清晰、易於理解的日誌文件用於記錄全部的修改操做。事實上,咱們也能夠經過該文件完成數據的重建。

 

     AOF的劣勢有哪些呢?

       1). 對於相同數量的數據集而言,AOF文件一般要大於RDB文件。RDB 在恢復大數據集時的速度比 AOF 的恢復速度要快。

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

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

        rdb這個就更有些 eventually consistent的意思了。

 

 

 

經常使用配置

     RDB持久化配置

     Redis會將數據集的快照dump到dump.rdb文件中。此外,咱們也能夠經過配置文件來修改Redis服務器dump快照的頻率,在打開6379.conf文件以後,咱們搜索save,能夠看到下面的配置信息:

       save 900 1              #在900秒(15分鐘)以後,若是至少有1個key發生變化,則dump內存快照。

       save 300 10            #在300秒(5分鐘)以後,若是至少有10個key發生變化,則dump內存快照。

       save 60 10000        #在60秒(1分鐘)以後,若是至少有10000個key發生變化,則dump內存快照。

 

     AOF持久化配置

     在Redis的配置文件中存在三種同步方式,它們分別是:

       appendfsync always     #每次有數據修改發生時都會寫入AOF文件。

       appendfsync everysec  #每秒鐘同步一次,該策略爲AOF的缺省策略。

       appendfsync no          #從不一樣步。高效可是數據不會被持久化。

相關文章
相關標籤/搜索