zoukankan      html  css  js  c++  java
  • [Go back to REDIS]

    Overview

    • 内存中的数据结构存储系统,可以用作数据库、缓存和消息中间件
    • redis底层数据结构:跳跃表 [为什么选skiplist而不是red-black tree]
    • 支持多种数据结构:String, hash, list, set, sorted set...
    • 内置: Replication, lua scripting, LRU eviction, transaction, persistence, Sentinel, Cluster. [具体往下看,分别有具体介绍~]

    单线程效率

    • 完全基于内存

    • 数据结构简单,对数据操作也简单

    • 多路IO复用模型 (参考link.)

    • 不要考虑各种锁

    • 不存在多线程上下文切换而消耗CPU

    事务

    • 事务是一个单独的隔离操作,事务中所有命令都会序列化、按顺序地执行,执行过程中不被其它客户端发来的命令请求打断。
    • 不支持rollback (redis认为失败的命令是由编程错误造成,不应出现在生产环境中;使redis内部保持简单且快速。)
    • 若事务中某些命令在执行时产生了错误,事务中的其他命令仍然会继续执行。
    • redis中关于事务的命令:MULTI、EXEC、DISCARD、WATCH

     

    CAS乐观锁

    • watch命令可以为redis事务提供CAS(check-and-set)行为
    • 典型的例子如下:
      val = GET mykey
      val = val + 1
      SET mykey $val
      

      上述代码在多个client争用时会产生不正确的结果。可以通过watch来解决:

      WATCH mykey
      val = GET mykey
      val = val + 1
      MULTI
      SET mykey $val
      EXEC
      

      上述代码会在exec执行之前监视mykey,如果有其他client修改了mykey的值,那么当前client的事务就会失败。程序要做的就是不断重试该操作,直到没有发生碰撞为止。
      watch命令从其被调用开始生效,一直到调用EXEC为止。

    • 实际上这就是乐观锁。(通常情况下不会产生大量的重试。)

    ACID

    以上,redis事务

    • 不满足原子性:redis事务在执行过程遇到错误,不会回滚,而是继续执行后续命令。

    • ?持久性:redis没有在事务层面有额外的持久性保证,所以仍然由redis提供的持久化模式决定。 [所以redis事务是不能保证持久性的,除非底层是aof always的持久化模式。]

    • 隔离性:redis事务执行过程中,不会处理其他命令。

    • 一致性:能保证。

    Persistence

    • bgsave做全量持久化:耗时长,不够实时,宕机时存在大量数据丢失。

      • bgsave的原理是什么?你给出两个词汇就可以了,fork和cow

      • fork是指redis通过创建子进程来进行bgsave操作。

      • cow指的是copy on write,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来。 [使用单独子进程来进行持久化,主进程不进行任何IO操作,保证了redis的高性能。 ]

      • 由于os的写时复制(copy on write)机制,父子进程会共享当前的物理页面,当父进程处理写请求时,os会为父进程要修改的页面创建副本,而不是写共享的页面。所以子进程的地址空间内的数据是fork时刻整个数据库的一个快照。
      • [注意:每次快照持久化都是将内存数据完整地写入到磁盘一次,并不是增量的。因而,在数据量比较大的时候必然会引起大量的磁盘IO。]
    • aof(Append-only file)做增量持久化: 将“操作+数据”以格式化指令的方式追加到操作日志文件的尾部。在append操作返回后才进行实际的数据变更。

    • —> 二者结合,在redis实例重启时,会使用bgsave持久化文件重新构建内存,再使用aof重放近期的操作指令来实现完整恢复重启之前的状态。

    • 突然断电的情况? —> 取决于aof的配置:如果不要求性能,在每条写指令时都sync一下磁盘,就不会丢失数据。但是在高性能的要求下每次都sync是不现实的,一般都使用定时sync,比如1s1次,这个时候最多就会丢失1s的数据。 [linux文件操作的“延迟写入”:并非每次write都会出发实际磁盘操作,而是进入buffer] aof记录同步选项如下

      • always:每一条aof记录都立即同步到文件,最安全的方式,但带来更多的磁盘操作和更大的阻塞延迟。

      • erverysec:每秒同步一次。如果遇到物理server故障,可能导致最近1s内aof记录丢失。

      • no:redis并不直接调用文件同步(并不是说不采用aof),而是交给os来处理 --> os可以根据buffer填充/通道空闲时间等择机触发同步。性能比较好,数据丢失量会与OS配置有关。

    以上,可以看出来redis并不十分适合“所有数据都需要机器可靠”的场景(may be relational db for ur choice)

    • RDB: 在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。
    • 默认的持久化方式
    • 优点:
      • 使用单独子进程来进行持久化,主进程不进行任何IO操作,保证了redis的高性能。
      • 恢复速度快。
    • 缺点:
      • 间隔一段时间进行持久化,容易发生数据丢失。
      • 每次保存RDB时,redis都要fork出一个子进程,在数据集比较大时,fork可能会十分耗时,造成server在一段时间内停止处理client请求。

    Partition

    • 按key分区
    • 作用:
      • 增大内存
      • 通过增加计算机来提高redis计算能力、网络带宽
    • 分区方式:
      • range:需要一张表存储数据到redis实例的映射关系。
      • hash:key必须是object_name:<id>的形式。
    • 分区实现方式:
      • 客户端分区:在client端就已经决定数据会被存储到哪个redis节点或从哪个redis节点读取。
      • 代理分区:client将请求发给代理,然后代理决定。redis和memcached的一种代理实现就是Twemproxy
      • 查询路由(Querying routing):client随即地请求任意一个redis实例,然后由redis将请求转发给正确的redis节点。
        Redis Cluster实现了一种混合形式的查询路由,但并不是直接将请求转发给正确的redis节点,而是在client端的帮助下直接redirected到正确的redis节点。
    • 缺点:
      • 涉及多个key的操作不被支持
      • 同时操作多个key,不能使用redis事务
      • 分区粒度是key,so it is not possible to shard a dataset with a single huge key like a very big sorted set
      • 分区时的动态扩容或缩容可能非常复杂。

    Redis集群

    Sentinel

    • redis sentinel着眼于高可用,在master宕机时会自动提升slave

    • Sentinel本身也是集群,避免SPOF。redis的客户端可以随意地连接任意一个sentinel来获得关于redis集群中的信息。 (sentinel集群自身也需要多数机制,也就是2个sentinel进程时,挂掉一个另一个就不可用了。)

    • sentinel的failover过程对client 是透明的。

      Set sentinels = new HashSet();
              sentinels.add(new HostAndPort("172.30.37.73", 26379).toString());
              sentinels.add(new HostAndPort("172.30.37.73", 26380).toString());
              sentinels.add(new HostAndPort("172.30.37.73", 26381).toString());
              JedisSentinelPool sentinelPool = new JedisSentinelPool("myredis", sentinels);
              System.out.println("Current master: " + sentinelPool.getCurrentHostMaster().toString());
              
              Jedis master = sentinelPool.getResource();
              master.set("username","tom");
              sentinelPool.returnResource(master);

    Redis Cluster

    • redis cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。 [Sharding]

    • Redis采取了P2P而非Proxy方式、异步复制、客户端重定向等设计,而牺牲了部分的一致性。

    • 可用性:在Cluster推出之前,可用性要靠Sentinel保证。有了集群之后也自动具有了Sentinel的监控和自动Failover能力。

    • 有了Cluster功能后,Redis从一个单纯的NoSQL内存数据库变成了分布式NoSQL数据库,CAP模型也从CP变成了AP

    • redis cluster VS codis

      • redis cluster基于smart client和无中心的设计,client必须按key的哈希将请求直接发送到对应的节点。 [转发] —> client不能直接像单机一样使用pipeline来提高效率,想同时执行多个请求来提速必须在client端自行实现异步逻辑。

      • 而codis因其有中心节点、基于proxy的设计,对client来说可以像对单机redis一样去操作proxy(除了一些命令不支持),还可以继续使用pipeline并且如果后台redis有多个的话速度会显著快于单redis的pipeline。

      • codis是一整套解决方案,还提供了auto-rebalance(迁移对上层业务透明)等。

    redis pipelining

    • Redis is a TCP server using the client-server model and what is called a Request/Response protocol.

    • The client sends a query to the server, and reads from the socket, usually in a blocking way, for the server response.

    Operations

    • keys VS scan

      • keys阻塞 [redis单线程]

      • scan非阻塞,但有一定重复概率

    FYI

  • 相关阅读:
    swift2.0学习之拓展
    CSDN处理问题神速,顶你,为你点32个赞!
    quick-cocos2d-x教程7:程序框架内framework文件夹分析
    Android自己定义Toast
    leetcode: Add Digits
    Effective Java:对于全部对象都通用的方法
    动态规划法-01背包问题
    loosejar原理简要分析
    eclipse中Client/Server程序生成exe
    Spring 新手教程(三) 注入和自己主动装配
  • 原文地址:https://www.cnblogs.com/wttttt/p/7718944.html
Copyright © 2011-2022 走看看