希望长大对我而言,是可以做更多想做的事,而不是被迫做更多不想做的事...... 首页 redis哨兵 丁D 学无止境 2019-07-09 22:05 534597已阅读 redis 哨兵 摘要哨兵安装,java连接redis哨兵模式,扩展jredis读写分离 ###目录 - redis安装 - 哨兵安装 - java连接哨兵 - 扩展jredis - rdb和aof [redis命令参考](http://redisdoc.com/topic/sentinel.html#id6) ###redis安装 [redis安装](http://www.54288.top/article/view.do?articleId=91) ###哨兵安装 **注意防火墙,注意防火墙,注意防火墙** 1. 在3台机器部署哨兵,组成一个集群 2. 修改配置文件 3. 启动redis 启动哨兵 ```js mkdir /etc/sentinal //哨兵配置文件目录 mkdir -p /var/sentinal/5000 //工作目录 //配置配置文件进行修改 cp /root/redis-3.2.8/sentinel.conf /etc/sentinal/ protected-mode no port 5000 bind 192.168.31.187 127.0.0.1 dir /var/sentinal/5000 sentinel monitor mymaster 192.168.31.187 6379 2 sentinel down-after-milliseconds mymaster 30000 sentinel failover-timeout mymaster 60000 sentinel parallel-syncs mymaster 1 sentinel auth-pass mymaster 123456 下面配置在文件中没有 自己新增 daemonize yes logfile "/var/redis/sen5000.log" //不同的机器。配置对应得ip地址 启动 redis-sentinel /etc/sentinal/5000.conf ``` 配置解释 ```js sentinel monitor master-group-name hostname port quorum quorum的解释如下: (1)至少多少个哨兵要一致同意,master进程挂掉了,或者slave进程挂掉了,或者要启动一个故障转移操作 (2)quorum是用来识别故障的,真正执行故障转移的时候,还是要在哨兵集群执行选举,选举一个哨兵进程出来执行故障转移操作 (3)假设有5个哨兵,quorum设置了2,那么如果5个哨兵中的2个都认为master挂掉了; 2个哨兵中的一个就会做一个选举,选举一个哨兵出来,执行故障转移; 如果5个哨兵中有3个哨兵都是运行的,那么故障转移就会被允许执行 down-after-milliseconds,超过多少毫秒跟一个redis实例断了连接,哨兵就可能认为这个redis实例挂了 parallel-syncs,新的master别切换之后,同时有多少个slave被切换到去连接新master,重新做同步,数字越低,花费的时间越多 假设你的redis是1个master,4个slave 然后master宕机了,4个slave中有1个切换成了master,剩下3个slave就要挂到新的master上面去 这个时候,如果parallel-syncs是1,那么3个slave,一个一个地挂接到新的master上面去,1个挂接完,而且从新的master sync完数据之后,再挂接下一个 如果parallel-syncs是3,那么一次性就会把所有slave挂接到新的master上去 failover-timeout,执行故障转移的timeout超时时长 ``` ```js 哨兵相互发现 每个哨兵都能去监控到对应的redis master,并能够自动发现对应的slave 哨兵之间,互相会自动进行发现,用的就是之前说的pub/sub,消息发布和订阅channel消息系统和机制 ``` ###java连接哨兵 spring.xml ```js classpath:jdbc.properties dialect=mysql ``` pom.xml ```js org.springframework.data spring-data-redis 1.5.2.RELEASE redis.clients jedis 2.9.0 ``` redis.properties ```js redis.groupname=mymaster redis.password=123456 redis.sentinel.host1=192.168.144.3 redis.sentinel.port1=5000 redis.sentinel.host2=192.168.144.4 redis.sentinel.port2=5000 redis.sentinel.host3=192.168.144.8 redis.sentinel.port3=5000 redis.pool.maxTotal=1024 redis.pool.maxIdle=200 redis.pool.maxWaitMillis=1000 redis.pool.testOnBorrow=true redis.pool.timeBetweenEvictionRunsMillis=30000 redis.pool.minEvictableIdleTimeMillis=30000 redis.pool.softMinEvictableIdleTimeMillis=10000 redis.pool.numTestsPerEvictionRun=1024 #1000*60*60*1 redis.pool.expire=3600000 redis.pool.unlock=false ``` ###结果 ```js master故障能自动转移 但是JedisSentinel 读写都是连接master,没有读写分离 slave只能用于故障转移 需要读写分离要自己扩展JedisSentinel 注意: 密码 没密码可以不要 ``` ###扩展jredis 由于java连接哨兵模式,但是每次连接的都是master(不管读写) 没有做到读写分离。需要自己扩展jredis来支持读写分离 参考 https://www.cnblogs.com/moonandstar08/p/7482143.html https://www.jack-yin.com/coding/spring-boot/2683.html 注意::验证方法,这里都shi //RedisTemplate ```js ``` TWReadOnlyJedisConnectionFactory.java ```js package com.ding.utils; import org.apache.commons.collections.CollectionUtils; import org.springframework.data.redis.connection.RedisNode; import org.springframework.data.redis.connection.RedisSentinelConfiguration; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.util.ReflectionUtils; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPoolConfig; import redis.clients.jedis.JedisShardInfo; import redis.clients.util.Pool; import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; public class TWReadOnlyJedisConnectionFactory extends JedisConnectionFactory { private static final Method GET_TIMEOUT_METHOD; static { Method getTimeoutMethodCandidate = ReflectionUtils.findMethod(JedisShardInfo.class,"getTimeout"); if(null == getTimeoutMethodCandidate) { getTimeoutMethodCandidate = ReflectionUtils.findMethod(JedisShardInfo.class,"getTimeout"); } GET_TIMEOUT_METHOD=getTimeoutMethodCandidate; } public TWReadOnlyJedisConnectionFactory() { super(); } public TWReadOnlyJedisConnectionFactory(JedisShardInfo shardInfo) { super(shardInfo); } public TWReadOnlyJedisConnectionFactory(JedisPoolConfig poolConfig){ this((RedisSentinelConfiguration)null,poolConfig); } public TWReadOnlyJedisConnectionFactory(RedisSentinelConfiguration sentinelConfig){ this(sentinelConfig,null); } public TWReadOnlyJedisConnectionFactory(RedisSentinelConfiguration sentinelConfig,JedisPoolConfig poolConfig){ super(sentinelConfig,poolConfig); } @Override public void afterPropertiesSet() { try { super.afterPropertiesSet(); }catch(Exception e) { } } protected Pool createRedisSentinelPool(RedisSentinelConfiguration config){ JedisSentinelSlavePool pool1 = new JedisSentinelSlavePool(config.getMaster().getName(), convertToJedisSentinelSet(config.getSentinels()), getPoolConfig()!=null?getPoolConfig():new JedisPoolConfig(), getTimeout(), getPassword()); return pool1; } private Set convertToJedisSentinelSet(Collection nodes) { if(CollectionUtils.isEmpty(nodes)) { return Collections.emptySet(); } Set convertedNodes = new LinkedHashSet(nodes.size()); for(RedisNode node : nodes) { convertedNodes.add(node.asString()); } return convertedNodes; } private int getTimeOutFrom(JedisShardInfo shardInfo){ return (Integer) ReflectionUtils.invokeMethod(GET_TIMEOUT_METHOD,shardInfo); } } ``` JedisSentinelSlavePool.java ```js package com.ding.utils; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPubSub; import redis.clients.jedis.Protocol; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.util.Pool; import java.security.InvalidParameterException; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; public class JedisSentinelSlavePool extends Pool { private final String masterName; protected GenericObjectPoolConfig poolConfig; protected int connectionTimeout = Protocol.DEFAULT_TIMEOUT; protected int soTimeout = Protocol.DEFAULT_TIMEOUT; protected String password; protected int database = Protocol.DEFAULT_DATABASE; protected String clientName; protected final Set masterListeners = new HashSet(); protected Logger logger = LoggerFactory.getLogger(JedisSentinelSlavePool.class.getName()); private volatile JedisSentinelSlaveFactory factory; private volatile HostAndPort currentSentinel; private Set sentinels; public JedisSentinelSlavePool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig) { this(masterName, sentinels, poolConfig, Protocol.DEFAULT_TIMEOUT, null, Protocol.DEFAULT_DATABASE); } public JedisSentinelSlavePool(String masterName, Set sentinels) { this(masterName, sentinels, new GenericObjectPoolConfig(), Protocol.DEFAULT_TIMEOUT, null, Protocol.DEFAULT_DATABASE); } public JedisSentinelSlavePool(String masterName, Set sentinels, String password) { this(masterName, sentinels, new GenericObjectPoolConfig(), Protocol.DEFAULT_TIMEOUT, password); } public JedisSentinelSlavePool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, int timeout, final String password) { this(masterName, sentinels, poolConfig, timeout, password, Protocol.DEFAULT_DATABASE); System.out.println("======================================="); } public JedisSentinelSlavePool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, final int timeout) { this(masterName, sentinels, poolConfig, timeout, null, Protocol.DEFAULT_DATABASE); } public JedisSentinelSlavePool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, final String password) { this(masterName, sentinels, poolConfig, Protocol.DEFAULT_TIMEOUT, password); } public JedisSentinelSlavePool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, int timeout, final String password, final int database) { this(masterName, sentinels, poolConfig, timeout, timeout, password, database); } public JedisSentinelSlavePool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, int timeout, final String password, final int database, final String clientName) { this(masterName, sentinels, poolConfig, timeout, timeout, password, database, clientName); } public JedisSentinelSlavePool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, final int timeout, final int soTimeout, final String password, final int database) { this(masterName, sentinels, poolConfig, timeout, soTimeout, password, database, null); } public JedisSentinelSlavePool(String masterName, Set sentinels, final GenericObjectPoolConfig poolConfig, final int connectionTimeout, final int soTimeout, final String password, final int database, final String clientName) { this.poolConfig = poolConfig; this.connectionTimeout = connectionTimeout; this.soTimeout = soTimeout; this.password = password; this.database = database; this.clientName = clientName; this.masterName = masterName; this.sentinels = sentinels; HostAndPort aSentinel = initsentinels(this.sentinels, masterName); initPool(aSentinel); } public void destroy() { for (JedisSentinelSlavePool.MasterListener m : masterListeners) { m.shutdown(); } super.destroy(); } public HostAndPort getCurrentSentinel() { return currentSentinel; } private void initPool(HostAndPort sentinel) { System.out.println("123123113312"); if (!sentinel.equals(currentSentinel)) { currentSentinel = sentinel; if (factory == null) { factory = new JedisSentinelSlaveFactory(sentinel.getHost(), sentinel.getPort(), connectionTimeout, soTimeout, password, database, clientName, false, null, null, null,masterName); initPool(poolConfig, factory); } else { factory.setHostAndPortOfASentinel(currentSentinel); // although we clear the pool, we still have to check the // returned object // in getResource, this call only clears idle instances, not // borrowed instances internalPool.clear(); } logger.info("Created JedisPool to sentinel at " + sentinel); } } private HostAndPort initsentinels(Set sentinels, final String masterName) { HostAndPort aSentinel = null; boolean sentinelAvailable = false; logger.info("Trying to find a valid sentinel from available Sentinels..."); for (String sentinelStr : sentinels) { final HostAndPort hap = HostAndPort.parseString(sentinelStr); logger.info("Connecting to Sentinel " + hap); Jedis jedis = null; try { jedis = new Jedis(hap.getHost(), hap.getPort()); sentinelAvailable = true; List masterAddr = jedis.sentinelGetMasterAddrByName(masterName); if (masterAddr == null || masterAddr.size() != 2) { logger.warn("Can not get master addr from sentinel, master name: " + masterName + ". Sentinel: " + hap + "."); continue; } aSentinel = hap; logger.info("Found a Redis Sentinel at " + aSentinel); break; } catch (JedisException e) { logger.warn("Cannot get master address from sentinel running @ " + hap + ". Reason: " + e + ". Trying next one."); } finally { if (jedis != null) { jedis.close(); } } } if (aSentinel == null) { if (sentinelAvailable) { // can connect to sentinel, but master name seems to not monitored throw new JedisException("Can connect to sentinel, but " + masterName + " seems to be not monitored..."); } else { throw new JedisConnectionException("All sentinels down, cannot determine where is " + masterName + " master is running..."); } } logger.info("Found Redis sentinel running at " + aSentinel + ", starting Sentinel listeners..."); for (String sentinel : sentinels) { final HostAndPort hap = HostAndPort.parseString(sentinel); JedisSentinelSlavePool.MasterListener masterListener = new JedisSentinelSlavePool.MasterListener(masterName, hap.getHost(), hap.getPort()); // whether MasterListener threads are alive or not, process can be stopped masterListener.setDaemon(true); masterListeners.add(masterListener); masterListener.start(); } return aSentinel; } /** * @deprecated starting from Jedis 3.0 this method will not be exposed. Resource cleanup should be * done using @see {@link redis.clients.jedis.Jedis#close()} */ @Override @Deprecated public void returnBrokenResource(final Jedis resource) { if (resource != null) { returnBrokenResourceObject(resource); } } /** * @deprecated starting from Jedis 3.0 this method will not be exposed. Resource cleanup should be * done using @see {@link redis.clients.jedis.Jedis#close()} */ @Override @Deprecated public void returnResource(final Jedis resource) { if (resource != null) { resource.resetState(); returnResourceObject(resource); } } private HostAndPort toHostAndPort(List getMasterAddrByNameResult) { String host = getMasterAddrByNameResult.get(0); int port = Integer.parseInt(getMasterAddrByNameResult.get(1)); return new HostAndPort(host, port); } protected class MasterListener extends Thread { protected String masterName; protected String host; protected int port; protected long subscribeRetryWaitTimeMillis = 5000; protected volatile Jedis j; protected AtomicBoolean running = new AtomicBoolean(false); protected MasterListener() { } public MasterListener(String masterName, String host, int port) { super(String.format("MasterListener-%s-[%s:%d]", masterName, host, port)); this.masterName = masterName; this.host = host; this.port = port; } public MasterListener(String masterName, String host, int port, long subscribeRetryWaitTimeMillis) { this(masterName, host, port); this.subscribeRetryWaitTimeMillis = subscribeRetryWaitTimeMillis; } @Override public void run() { running.set(true); while (running.get()) { j = new Jedis(host, port); try { // double check that it is not being shutdown if (!running.get()) { break; } j.subscribe(new SentinelSlaveChangePubSub(), "+switch-master","+slave","+sdown","+odown","+reboot"); } catch (JedisConnectionException e) { if (running.get()) { logger.error("Lost connection to Sentinel at " + host + ":" + port + ". Sleeping 5000ms and retrying.", e); try { Thread.sleep(subscribeRetryWaitTimeMillis); } catch (InterruptedException e1) { logger.info( "Sleep interrupted: ", e1); } } else { logger.info("Unsubscribing from Sentinel at " + host + ":" + port); } } finally { j.close(); } } } public void shutdown() { try { logger.info("Shutting down listener on " + host + ":" + port); running.set(false); // This isn't good, the Jedis object is not thread safe if (j != null) { j.disconnect(); } } catch (Exception e) { logger.error("Caught exception while shutting down: ", e); } } private class SentinelSlaveChangePubSub extends JedisPubSub { @Override public void onMessage(String channel, String message) { if(masterName==null) { logger.error("Master Name is null!"); throw new InvalidParameterException("Master Name is null!"); } logger.info("Get message on chanel: " + channel + " published: " + message + "." + " current sentinel " + host + ":" + port ); String[] msg = message.split(" "); List msgList = Arrays.asList(msg); if(msgList.isEmpty()) {return;} boolean needResetPool = false; if( masterName.equalsIgnoreCase(msgList.get(0))) { //message from channel +switch-master //message looks like [+switch-master mymaster 192.168.0.2 6479 192.168.0.1 6479] needResetPool = true; } int tmpIndex = msgList.indexOf("@") + 1; //message looks like [+reboot slave 192.168.0.3:6479 192.168.0.3 6479 @ mymaster 192.168.0.1 6479] if(tmpIndex >0 && masterName.equalsIgnoreCase(msgList.get(tmpIndex)) ) { //message from other channels needResetPool = true; } if(needResetPool) { HostAndPort aSentinel = initsentinels(sentinels, masterName); initPool(aSentinel); } else { logger.info("message is not for master " + masterName); } } } } } ``` JedisSentinelSlaveFactory.java ```js package com.ding.utils; import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.DefaultPooledObject; import redis.clients.jedis.BinaryJedis; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.exceptions.InvalidURIException; import redis.clients.jedis.exceptions.JedisException; import redis.clients.util.JedisURIHelper; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocketFactory; import java.net.URI; import java.security.SecureRandom; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; public class JedisSentinelSlaveFactory implements PooledObjectFactory { private final String masterName; private final int retryTimeWhenRetrieveSlave = 5; private final AtomicReference hostAndPortOfASentinel = new AtomicReference(); private final int connectionTimeout; private final int soTimeout; private final String password; private final int database; private final String clientName; private final boolean ssl; private final SSLSocketFactory sslSocketFactory; private SSLParameters sslParameters; private HostnameVerifier hostnameVerifier; public JedisSentinelSlaveFactory(final String host, final int port, final int connectionTimeout, final int soTimeout, final String password, final int database, final String clientName, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier,String masterName) { this.hostAndPortOfASentinel.set(new HostAndPort(host, port)); this.connectionTimeout = connectionTimeout; this.soTimeout = soTimeout; this.password = password; this.database = database; this.clientName = clientName; this.ssl = ssl; this.sslSocketFactory = sslSocketFactory; this.sslParameters = sslParameters; this.hostnameVerifier = hostnameVerifier; this.masterName = masterName; } public JedisSentinelSlaveFactory(final URI uri, final int connectionTimeout, final int soTimeout, final String clientName, final boolean ssl, final SSLSocketFactory sslSocketFactory, final SSLParameters sslParameters, final HostnameVerifier hostnameVerifier,String masterName) { if (!JedisURIHelper.isValid(uri)) { throw new InvalidURIException(String.format( "Cannot open Redis connection due invalid URI. %s", uri.toString())); } this.hostAndPortOfASentinel.set(new HostAndPort(uri.getHost(), uri.getPort())); this.connectionTimeout = connectionTimeout; this.soTimeout = soTimeout; this.password = JedisURIHelper.getPassword(uri); this.database = JedisURIHelper.getDBIndex(uri); this.clientName = clientName; this.ssl = ssl; this.sslSocketFactory = sslSocketFactory; this.sslParameters = sslParameters; this.hostnameVerifier = hostnameVerifier; this.masterName = masterName; } public void setHostAndPortOfASentinel(final HostAndPort hostAndPortOfASentinel) { this.hostAndPortOfASentinel.set(hostAndPortOfASentinel); } @Override public void activateObject(PooledObject pooledJedis) throws Exception { final BinaryJedis jedis = pooledJedis.getObject(); if (jedis.getDB() != database) { jedis.select(database); } } @Override public void destroyObject(PooledObject pooledJedis) throws Exception { final BinaryJedis jedis = pooledJedis.getObject(); if (jedis.isConnected()) { try { try { jedis.quit(); } catch (Exception e) { } jedis.disconnect(); } catch (Exception e) { } } } @Override public PooledObject makeObject() throws Exception { final Jedis jedisSentinel = getASentinel(); List> slaves = jedisSentinel.sentinelSlaves(this.masterName); if(slaves == null || slaves.isEmpty()) { throw new JedisException(String.format("No valid slave for master: %s",this.masterName)); } DefaultPooledObject result = tryToGetSlave(slaves); if(null != result) { return result; } else { throw new JedisException(String.format("No valid slave for master: %s, after try %d times.", this.masterName,retryTimeWhenRetrieveSlave)); } } private DefaultPooledObject tryToGetSlave(List> slaves) { SecureRandom sr = new SecureRandom(); int retry = retryTimeWhenRetrieveSlave; while(retry >= 0) { retry--; int randomIndex = sr.nextInt(slaves.size()); String host = slaves.get(randomIndex).get("ip"); String port = slaves.get(randomIndex).get("port"); final Jedis jedisSlave = new Jedis(host,Integer.valueOf(port), connectionTimeout,soTimeout, ssl, sslSocketFactory,sslParameters, hostnameVerifier); try { jedisSlave.connect(); if (null != this.password) { jedisSlave.auth(this.password); } if (database != 0) { jedisSlave.select(database); } if (clientName != null) { jedisSlave.clientSetname(clientName); } return new DefaultPooledObject(jedisSlave); } catch (Exception e) { jedisSlave.close(); slaves.remove(randomIndex); continue; } } return null; } private Jedis getASentinel() { final HostAndPort hostAndPort = this.hostAndPortOfASentinel.get(); final Jedis jedis = new Jedis(hostAndPort.getHost(), hostAndPort.getPort(), connectionTimeout, soTimeout, ssl, sslSocketFactory, sslParameters, hostnameVerifier); try { jedis.connect(); } catch (JedisException je) { jedis.close(); throw je; } return jedis; } @Override public void passivateObject(PooledObject pooledJedis) throws Exception { // TODO maybe should select db 0? Not sure right now. } @Override public boolean validateObject(PooledObject pooledJedis) { final BinaryJedis jedis = pooledJedis.getObject(); try { HostAndPort hostAndPort = this.hostAndPortOfASentinel.get(); String connectionHost = jedis.getClient().getHost(); int connectionPort = jedis.getClient().getPort(); // return hostAndPort.getHost().equals(connectionHost) // && hostAndPort.getPort() == connectionPort && jedis.isConnected() // && jedis.ping().equals("PONG"); return true; } catch (final Exception e) { return false; } } } ``` ### RDB 快照的运作方式: 当 Redis 需要保存 dump.rdb 文件时, 服务器执行以下操作: Redis 调用 fork() ,同时拥有父进程和子进程。 子进程将数据集写入到一个临时 RDB 文件中。 当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。 这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益。 只进行追加操作的文件(append-only file,AOF) 快照功能并不是非常耐久(durable): 如果 Redis 因为某些原因而造成故障停机, 那么服务器将丢失最近写入、且仍未保存到快照中的那些数据。尽管对于某些程序来说, 数据的耐久性并不是最重要的考虑因素, 但是对于那些追求完全耐久能力 RDB优点: (1)RDB会生成多个数据文件,每个数据文件都代表了某一个时刻中redis的数据,这种多个数据文件的方式,非常适合做冷备。 (3).RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。 RDB缺点 (1)如果redis要故障时要尽可能少的丢失数据,RDB没有AOF好,例如1:00进行的快照,在1:10又要进行快照的时候宕机了,这个时候就会丢失10分钟的数据。 (2)RDB每次fork出子进程来执行RDB快照生成文件时,如果文件特别大,可能会导致客户端提供服务暂停数毫秒或者几秒 ### AOF Redis 执行 fork() ,现在同时拥有父进程和子进程。 子进程开始将新 AOF 文件的内容写入到临时文件。对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾: 这样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾。现在 Redis 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 AOF 文件的末尾。 AOF的缺点 (1)对于同一份文件AOF文件比RDB数据快照要大。 (2)AOF开启后支持写的QPS会比RDB支持的写的QPS低,因为AOF一般会配置成每秒fsync操作,每秒的fsync操作还是很高的 (3)数据恢复比较慢,不适合做冷备。 AOF的优点: (1)AOF可以更好的保护数据不丢失,一般AOF会以每隔1秒,通过后台的一个线程去执行一次fsync操作,如果redis进程挂掉,最多丢失1秒的数据。 很赞哦! (3) 上一篇:Redis+Twemproxy+HAProxy+Keepalived 下一篇:hystrix基础 目录 点击排行 Elasticsearch6.3.2之x-pack redis哨兵 2019-07-09 22:05 Redis+Twemproxy+HAProxy+Keepalived 2019-07-12 17:20 GC优化策略和相关实践案例 2019-10-10 10:54 JVM垃圾回收器 2019-10-10 10:23 标签云 Java Spring MVC Mybatis Ansible Elasticsearch Redis Hive Docker Kubernetes RocketMQ Jenkins Nginx 友情链接 郑晓博客 佛布朗斯基 凉风有信 MarkHoo's Blog 冰洛博客 南实博客 Rui | 丁D Java研发工程师 生活可以用「没办法」三个字概括。但别人的没办法是「腿长,没办法」、「长得好看,没办法」、「有才华,没办法」。而你的没办法,是真的没办法。 请作者喝咖啡