Redis

持久化

  1. RDB持久化  全量同步    存储到dump.rdb文件  。(redis.conf文件中,save 900 1,900秒内至少一次修改则保存rdb文件,做同步)
  2. AOF持久化  增量同步    (conf文件中,每次写操作都会被记录,appendfsync everysec(默认)/always。


淘汰策略

noeviction:当内存使用达到阈值的时候,所有引起申请内存的命令会报错。
allkeys-lru:在主键空间中,优先移除最近未使用的key。(推荐)
volatile-lru:在设置了过期时间的键空间中,优先移除最近未使用的key。
allkeys-random:在主键空间中,随机移除某个key。
volatile-random:在设置了过期时间的键空间中,随机移除某个key。
volatile-ttl:在设置了过期时间的键空间中,具有更早过期时间的key优先移除。

  • 设置redis内存大小

在redis.conf文件中,"maxmemory <bytes>" 设置Redis 内存大小的限制,比如:maxmemory 300mb。当数据达到限定大小后,会选择配置的策略淘汰数据

  • 设置redis淘汰策略

通过配置"maxmemory-policy"设置Redis的淘汰策略。比如:maxmemory-policy volatile-lru


 Redis自动过期机制(如订单超时)

 key有效期,过期回调

  • 开启:

当我们的key失效时,可以执行我们的客户端回调监听的方法。

需要在Redis中配置: notify-keyspace-events "Ex"

  • 部分代码:
@Configuration
public class RedisListenerConfig {
    @Bean
    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        return container;
    }
}
@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
    public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }

    /**
     * 待支付
     */
    private static final Integer ORDER_STAYPAY = 0;
    /**
     * 失效
     */
    private static final Integer ORDER_INVALID = 2;
    @Autowired
    private OrderMapper orderMapper;

    /**
     * Redis失效事件 key
     *
     * @param message
     * @param pattern
     */
    @Override
    public void onMessage(Message message, byte[] pattern) {
        String expiraKey = message.toString();
        // 根据key查询 value 如果还还是为待支付状态 将订单改为已经超时~~
        OrderEntity orderNumber = orderMapper.getOrderNumber(expiraKey);
        System.out.println(expiraKey);
        if (orderNumber == null) {
            return;
        }
        if (orderNumber.getOrderStatus().equals(ORDER_STAYPAY)) {
            // 将订单状态改为已经失效
            orderMapper.updateOrderStatus(expiraKey, ORDER_INVALID);
        }
    }
}


事务操作

multi 开启事务(开始事务后,所有set存放在队列中,exec后一起提交,提交之后才能查到)

exec  提交事务

watch 可以监听一个或者多个key,在提交事务之前是否有发生了变化 如果发生边了变化就不会提交事务,没有发生变化才可以提交事务(原理 版本号码 乐观锁 )

discard 取消提交事务

例:

watch name
multi
set name xiaoxiao
exec

注意:Redis官方是没有提供回滚方法, 只提供了取消事务。


分布式锁

RedisUtil(获取redis连接)

public class RedisUtil {
    //protected static Logger logger = Logger.getLogger(RedisUtil.class);

    private static String IP = "127.0.0.1";

    //Redis的端口号
    private static int PORT = 6379;

    //可用连接实例的最大数目,默认值为8;
    //如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。
    private static int MAX_ACTIVE = 100;

    //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。
    private static int MAX_IDLE = 20;

    //等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;
    private static int MAX_WAIT = 3000;

    private static int TIMEOUT = 3000;

    //在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
    private static boolean TEST_ON_BORROW = true;

    //在return给pool时,是否提前进行validate操作;
    private static boolean TEST_ON_RETURN = true;

    private static JedisPool jedisPool = null;

    /**
     * redis过期时间,以秒为单位
     */
    public final static int EXRP_HOUR = 60 * 60; //一小时
    public final static int EXRP_DAY = 60 * 60 * 24; //一天
    public final static int EXRP_MONTH = 60 * 60 * 24 * 30; //一个月

    /**
     * 初始化Redis连接池
     */
    private static void initialPool() {
        try {
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxTotal(MAX_ACTIVE);
            config.setMaxIdle(MAX_IDLE);
            config.setMaxWaitMillis(MAX_WAIT);
            config.setTestOnBorrow(TEST_ON_BORROW);

            jedisPool = new JedisPool(config, IP, PORT, TIMEOUT, null);
        } catch (Exception e) {
            //logger.error("First create JedisPool error : "+e);
            e.getMessage();
        }
    }


    /**
     * 在多线程环境同步初始化
     */
    private static synchronized void poolInit() {
        if (jedisPool == null) {
            initialPool();
        }
    }


    /**
     * 同步获取Jedis实例
     *
     * @return Jedis
     */
    public synchronized static Jedis getJedis() {
        if (jedisPool == null) {
            poolInit();
        }
        Jedis jedis = null;
        try {
            if (jedisPool != null) {
                jedis = jedisPool.getResource();
            }
        } catch (Exception e) {
            e.getMessage();
            // logger.error("Get jedis error : "+e);
        }
        return jedis;
    }


    /**
     * 释放jedis资源
     *
     * @param jedis
     */
    public static void returnResource(final Jedis jedis) {
        if (jedis != null && jedisPool != null) {
            jedisPool.returnResource(jedis);
        }
    }

    public static Long sadd(String key, String... members) {
        Jedis jedis = null;
        Long res = null;
        try {
            jedis = getJedis();
            res = jedis.sadd(key, members);
        } catch (Exception e) {
            //logger.error("sadd  error : "+e);
            e.getMessage();
        }
        return res;
    }
}

RedisLockUtil(获取事务锁,释放事务锁)

public class RedisLockUtil {
    private static int lockSuccess = 1;

    /**
     * @param lockKey      在Redis中创建的key值
     * @param notLockTime 尝试获取锁超时时间
     * @return 返回lock成功值
     */
    public String getLock(String lockKey, int notLockTime, int timeOut) {
        //获取Redis连接
        Jedis jedis = RedisUtil.getJedis();
        // 计算我们尝试获取锁超时时间
        Long endTime = System.currentTimeMillis() + notLockTime;
        //  当前系统时间小于endTime说明获取锁没有超时 继续循环 否则情况下推出循环
        while (System.currentTimeMillis() < endTime) {
            String lockValue = UUID.randomUUID().toString();
            // 当多个不同的jvm同时创建一个相同的redisKey 只要谁能够创建成功谁就能够获取锁
            if (jedis.setnx(lockKey, lockValue) == lockSuccess) {
                // 加上有效期
                jedis.expire(lockKey, timeOut / 1000);
                return lockValue;
                // 退出循环
            } // 否则情况下 继续循环
        }
        try {
            if (jedis != null) {
                jedis.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 释放锁
     * @return
     */
    public boolean unLock(String locKey, String lockValue) {
        //获取Redis连接
        Jedis jedis = RedisUtil.getJedis();
        try {
            // 判断获取锁的时候保证自己删除自己
            if (lockValue.equals(jedis.get(locKey))) {
                return jedis.del(locKey) > 0 ? true : false;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return false;
    }
}

OrderService(测试)

public class OrderService {

    private static final String LOCK_KEY = "lock";

    public static void service() {
        // 1.获取锁
        RedisLockUtil redisLock = new RedisLockUtil();
        String lockValue = redisLock.getLock(LOCK_KEY, 5000, 15000);
        if (StringUtils.isEmpty(lockValue)) {
            System.out.println(Thread.currentThread().getName() + ",获取锁失败了");
            return;
        }
        // 执行我们的业务逻辑
        System.out.println(Thread.currentThread().getName() + ",获取锁成功:lockValue:" + lockValue);
        // 3.释放锁
        redisLock.unLock(LOCK_KEY, lockValue);
    }
    public static void main(String[] args) {
        service();
    }

    /***
     *
     * 尝试获取锁为什么次数限制?
     * 如果我们业务逻辑5s 内没有执行完毕呢?
     *
     * 分场景:
     * 1.锁的超时时间根据业务场景来预估
     * 2.可以自己延迟锁的时间
     * 3.在提交事务的时候检查锁是否已经超时 如果已经超时则回滚(手动回滚)否则提交。
     *
     * 仅限于单机版本
     */
}

主从复制

  • 基本概念

单个Redis如果因为某种原因宕机的话,可能会导致Redis服务不可用。

可以使用主从复制实现一主多从,主节点负责写的操作,从节点负责读的操作,主节点会定期将数据同步到从节点中,保证数据一致性的问题。

  • 相关配置

Redis.conf

# replicaof <masterip> <masterport>

slaveof 192.168.212.155(主Redis服务器ip)   6379

masterauth 123456(密码)

查看主从节点命令: info replication

  •  主从复制原理过程

1.需要在从redis 服务器配置在slaveof 执行 主redis 服务ip地址和端口号192.168.1.110:6379 (主的服务) 和密码;
2.从Redis 服务器和主Redis 服务器建立Socket 长连接; 

3.采用全量和增量形式将数据同步给从 Redis 服务器.

        全量:从Redis 首次启动的时候  (二进制执行文件文件)rdb文件

        增量:主Redis 每次有新的Set 请求时候 aof日志文件


哨兵机制

  •  哨兵机制原理
  1.  哨兵机制每个10s时间只需要配置监听我们的主节点就可以获取当前整个Redis集群的环境列表,采用info 命令形式。
  2. 哨兵不建议是单机的,最好每个Redis节点都需要配置哨兵监听。
  3. 哨兵集群原理是如何:多个哨兵都执行同一个主的master节点,订阅到相同都通道,有新的哨兵加入都会向通道中发送自己服务的信息,该通道的订阅者可以发现新哨兵的加入,随后相互建立长连接。
  4. Master的故障发现 单个哨兵会向主的master节点发送ping的命令,如果master节点没有及时的响应,哨兵会认为该master节点为“主观不可用状态”会发送给其他都哨兵确认该Master节点是否不可用,当前确认的哨兵节点数>=quorum(可配置),会实现重新选举。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值