Open afredlyj opened 8 years ago
JedisCluster对象的初始化,需要传入Redis 服务器的Host
和Port
:
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。
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
Redis可以用过maxmemory
选项限制Redis服务分配的最大内存,如果不配置该选项,可能导致整个服务器被吃光,所以建议加上。关于内存分配,有几个重要的点需要关注:
malloc()
函数。这会导致Redis服务的常驻内存(RSS,Resident Set Size)居高不下,即使此时已经有一部分key被删除;info
命令观察到的用户使用内存为3G,当有新的数据需要添加时,RSS的值并不会增加,而是保证相对稳定,allocators 会重用之前被删除的(逻辑上)2GB空间,直到用完,RSS才会继续增加;正文在这里。当Redis使用内存达到maxmemory
配置的值时,会根据配置文件中的策略采取不同的措施(TODO
)。
在Redis中,如果一个key设置了过期时间,除非是删除该key或者重写key,不能通过其他方式修改timeout的值。可以通过expire
和persist
增加或删除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过期,就直接将其删掉。
SAVE
或者BGSAVE
命令生成RDB文件时,程序会检查数据库中的键,已经过期的键不会被保存打牌RDB文件中。同时在Redis服务器启动时,如果开启了RDB功能,在服务器载入RDB文件时,如果当前服务器是master,则载入时程序会检查文件中的键,只有未过期的键才会载入到数据库中,如果当前服务器是slave,文件中所有的键都会被载入,但是由于主从服务器在进行数据同步时,从服务器的数据被清空,所以即使载入过期键也不会影响。Redis集群中的所有slots 分配到节点之后,才能正常向外提供服务。
Redis集群并不能保证强一致性,也就是说,即使服务器向客户端返回了ok,该写操作仍然可能丢失,原因在于master和slave之间是异步复制,异步复制流程如下:
由于master并没有等待slave的同步成功通知,然后返回客户端,可能出现这样的情况,在同步到slave的过程中,master挂机,而其他slave升级为master,那么这次写操作将会永久丢失。Redis同样提供了master和slave之间的同步写,但是会影响整个集群的功能,即使是这样,Redis也无法保证数据的强一致性,比如脑裂。
摘抄自《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持久化是通过保存写命令来记录数据库状态,随着时间推移,AOF文件会越来越大,使用该文件恢复数据库的时间会越来越长。为了解决这个问题,Redis提供了AOF重写的功能。
文件重写功能并不依赖当前的AOF文件,而是直接读取当前数据库的状态,即先从数据库中遍历,读取键值,然后用一条命令记录这个键值对,如此循环。通过这种方式,可以去除掉原AOF文件中的冗余命令,缩小AOF文件的大小。
Redis 的事务特性