afredlyj / mynote

idea and note
1 stars 0 forks source link

Redis #9

Open afredlyj opened 8 years ago

afredlyj commented 8 years ago

Redis 的事务特性

  1. 原子性(Atomicity) 要么执行事务中的所有操作,要么一个操作都不执行。但是Redis 事务不支持回滚,也就是说,即使事务操作中有一个操作出错,Redis服务端也会将事务队列中的命令执行完毕。
  2. 一致性(Consistency) Redis通过错误检测和简单的设计保证事务一致性。
  3. 隔离性(Isolation)
  4. 持久性(Durability)
afredlyj commented 8 years ago

JedisCluster 的初始化

JedisCluster对象的初始化,需要传入Redis 服务器的HostPort

  public JedisCluster(Set<HostAndPort> nodes) {
    this(nodes, DEFAULT_TIMEOUT);
  }

其中构造函数的入参是node信息的Set,如果只传入集群中的单个节点信息,会怎样?答案是JedisCluster通过CLUSTER NODES命令获取集群的所有节点信息:

// JedisClusterConnectionHandler.java

  private void initializeSlotsCache(Set<HostAndPort> startNodes, GenericObjectPoolConfig poolConfig) {
    for (HostAndPort hostAndPort : startNodes) {
      Jedis jedis = new Jedis(hostAndPort.getHost(), hostAndPort.getPort());
      try {
        cache.discoverClusterNodesAndSlots(jedis);
        break;
      } catch (JedisConnectionException e) {
        // try next nodes
      } finally {
        if (jedis != null) {
          jedis.close();
        }
      }
    }

    for (HostAndPort node : startNodes) {
      cache.setNodeIfNotExist(node);
    }
  }

关键在于cache.discoverClusterNodesAndSlots(jedis)

public void discoverClusterNodesAndSlots(Jedis jedis) {
    w.lock();

    try {
      this.nodes.clear();
      this.slots.clear();

      String localNodes = jedis.clusterNodes();
      for (String nodeInfo : localNodes.split("\n")) {
        ClusterNodeInformation clusterNodeInfo = nodeInfoParser.parse(nodeInfo, new HostAndPort(
            jedis.getClient().getHost(), jedis.getClient().getPort()));

        HostAndPort targetNode = clusterNodeInfo.getNode();
        setNodeIfNotExist(targetNode);
        assignSlotsToNode(clusterNodeInfo.getAvailableSlots(), targetNode);
      }
    } finally {
      w.unlock();
    }
  }

该代码块会通过当前的Jedis Connection 发送命令,之后获取到服务器返回nodes信息,然后分配slots。

afredlyj commented 8 years ago

Redis replication 和 redis sharding (cluster) 的区别

Sharding is almost replication's antithesis, though they are orthogonal concepts and work well together.

Sharding, also known as partitioning, is splitting the data up by key; While replication, also know as mirroring, is to copy all data.

Sharding is useful to increase performance, reducing the hit and memory load on any one resource. Replication is useful for high availability of reads. If you read from multiple replicas, you will also reduce the hit rate on all resources, but the memory requirement for all resources remains the same. It should be noted that, while you can write to a slave, replication is master->slave only. So you cannot scale writes this way.

Suppose you have the following tuples: [1:Apple], [2:Banana], [3:Cherry], [4:Durian] and we have two machines A and B. With Sharding, we might store keys 2,4 on machine A; and keys 1,3 on machine B. With Replication, we store keys 1,2,3,4 on machine A and 1,2,3,4 on machine B.

Sharding is typically implemented by performing a consistent hash upon the key. The above example was implemented with the following hash function h(x){return x%2==0?A:B}.

To combine the concepts, We might replicate each shard. In the above cases, all of the data (2,4) of machine A could be replicated on machine C and all of the data (1,3) of machine B could be replicated on machine D.

Any key-value store (of which Redis is only one example) supports sharding, though certain cross-key functions will no longer work. Redis supports replication out of the box.

参考地址:stackoverflow

afredlyj commented 8 years ago

Redis 内存分配要点

Redis可以用过maxmemory选项限制Redis服务分配的最大内存,如果不配置该选项,可能导致整个服务器被吃光,所以建议加上。关于内存分配,有几个重要的点需要关注:

正文在这里。当Redis使用内存达到maxmemory配置的值时,会根据配置文件中的策略采取不同的措施(TODO)。

afredlyj commented 8 years ago

Redis 过期策略

在Redis中,如果一个key设置了过期时间,除非是删除该key或者重写key,不能通过其他方式修改timeout的值。可以通过expirepersist增加或删除key的过期时间。给key增加过期时间会相应增加key的存储空间。

过期和持久化

需要注意的是,Redis中的过期时间,并不是准确的,在2.4之前,可能存在0到1秒的偏差,2.6以后,这个偏差缩短到0到1毫秒。

Key的过期时间信息使用absolute Unix timestamps存储,也就是说,即使Redis服务暂时down掉,不会影响key的过期,同时,为了保证过期策略的正常运行,需要保证服务器时间的稳定性(Redis服务在运行过程中也会检查服务器时间),在多个服务器数据同步时尤其注意,要避免出现两个服务器时间相差很大的情况。

过期策略

Redis的key过期有两种方式:主动过期和被动过期。

当客户端请求某个key,并且发现该key已经过期时,称为主动过期,此时服务器会先删除这个过期key,然后再执行其他操作。并不是所有的key都是通过这种方式过期,可能存在最近很长时间没有被访问的key,Redis会定期的随机检查expire set(过期字典,key为数据库中的key,value为unix时间戳)中的key,如果发现有key过期,就直接将其删掉。

AOF、RDB和复制功能对过期的处理

afredlyj commented 8 years ago

Redis 集群

Redis集群中的所有slots 分配到节点之后,才能正常向外提供服务。

Redis集群并不能保证强一致性,也就是说,即使服务器向客户端返回了ok,该写操作仍然可能丢失,原因在于master和slave之间是异步复制,异步复制流程如下:

  1. 客户端向master发送写操作;
  2. 写入成功之后master回复OK;
  3. master将写操作同步给自己的slave。

由于master并没有等待slave的同步成功通知,然后返回客户端,可能出现这样的情况,在同步到slave的过程中,master挂机,而其他slave升级为master,那么这次写操作将会永久丢失。Redis同样提供了master和slave之间的同步写,但是会影响整个集群的功能,即使是这样,Redis也无法保证数据的强一致性,比如脑裂。

afredlyj commented 7 years ago

http://redis.io/topics/distlock

afredlyj commented 7 years ago

AOF 持久化

摘抄自《Redis设计与实现》

AOF持久化功能的实现可以分为命令追加(append)、文件写入和文件同步三个步骤。

命令追加

当打开AOF持久化时,redis server在执行完一个写命令后,会以协议格式将该写命令追加到aof_buf缓冲区。

文件写入和同步

redis server在处理写请求时会将写命令追加到aof_buf缓冲区中,并调用flushAppendOnly函数,伪代码如下:

def eventLoop():
while true:
    # 接受客户端请求并回复
    processFileEvents()
    # 处理时间事件
    processTimeEvents()
    # 考虑是否需要将aof_buf中的内容写入和保存到AOF文件中
    flushAppendOnly()

函数flushAppendOnly的行为由配置appendfsync值决定:

always : 将aof_buf中的所有内容都写入并同步到AOF文件
everysec : aof_buf内容所有写入AOF文件,如果上次同步时间距离当前时间超过一秒,那么再次同步AOF文件,同步操作由专门的线程负责
no : aof_buf内容所有写入AOF文件,但是不进行文件同步,同步的时间点由操作系统决定

AOF rewrite

由于AOF持久化是通过保存写命令来记录数据库状态,随着时间推移,AOF文件会越来越大,使用该文件恢复数据库的时间会越来越长。为了解决这个问题,Redis提供了AOF重写的功能。

文件重写功能并不依赖当前的AOF文件,而是直接读取当前数据库的状态,即先从数据库中遍历,读取键值,然后用一条命令记录这个键值对,如此循环。通过这种方式,可以去除掉原AOF文件中的冗余命令,缩小AOF文件的大小。