浅谈工做中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          #从不一样步。高效可是数据不会被持久化。

相关文章
相关标签/搜索