day 12 Redis分片

此文档是依据上课流程整顿。更多细节及图片请参见刘老师的专栏

江哥的专栏

《cgb2008-京淘day12》

一. AOP形式实现商品分类缓存
  1. 业务需要

    通过自定义注解的模式,动静实现缓存操作。拦挡的指标是带有指定注解的办法。通过注解获取其中的key和超时工夫。

  2. 编辑@CacheFind注解

    @Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface CacheFind {    public String key();    public int seconds() default 0; //不须要设置超时工夫}
  3. 自定义注解的用法

    == ItemCatServiceImpl ==@CacheFind(key = "ITEM_CAT_PARENT",seconds = 7*24*60*60) //具体的key主动补齐public List<EasyUITree> findItemCatCache(long pid) {}
  4. 编辑CacheAop

    @Autowiredprivate Jedis jedis;/* 实现思路:    找到类,办法名字,参数列表的类型    key = 用户自定义的前缀+用户的参数 [0,xx]    判断key是否存在,如果存在,获取其中的数据JSON 转化为具体的对象(返回值类型)        反之执行指标办法,取得返回值,将返回值转化为JSON格局保留到缓存中 */@Around("@annotation(cacheFind)")public Object around(ProceedingJoinPoint joinPoint, CacheFind cacheFind){    System.out.println("==盘绕告诉开始==");    Object result = null;    //1.获取key的前缀    String key = cacheFind.key();    System.out.println("key = "+key);    //2.获取办法参数    Object[] args = joinPoint.getArgs();    String argString = Arrays.toString(args);    key = key+"::"+argString;    System.out.println("key = "+key);    try {        //3.判断缓存中是否有数据        if(jedis.exists(key)){            String json = jedis.get(key);            //5.获取返回值类型            Class clazz = ((MethodSignature)joinPoint.getSignature()).getReturnType();            result = ObjectMapperUtil.toObj(json,clazz);            System.out.println("==执行数据库==");        }else{ //缓存中没有数据            result = joinPoint.proceed();            String resultJSON = ObjectMapperUtil.toJSON(result);            //4.判断数据中是否有超时工夫            if(cacheFind.seconds()>0){                jedis.setex(key, cacheFind.seconds(), resultJSON);            }else {                jedis.set(key, resultJSON);            }            System.out.println("==执行数据库==");        }    } catch (Throwable throwable) {        throwable.printStackTrace();        throw new RuntimeException(throwable);    }    System.out.println("==盘绕告诉完结==");    return result;}
  5. 为查问商品类目标办法加上注解

    @Override@CacheFind(key = "ITEM_CAT")public String findCatNameByCid(int cid) {    return itemCatMapper.findCatNameByCid(cid);}
二. Redis根本个性
  1. 长久化策略

    i. 为什么要长久化?

    Redis中的记录都保留在内存中,如果内存断电或者服务器宕机,则内存数据间接失落。为了避免这一景象,须要将数据定期进行保护。

    ii. RDB模式

    1) RDB模式是Redis的默认的长久化策略,无需手动开启;

    2) Redis会定期执行RDB长久化操作。毛病:可能导致内存数据失落;

    3) RDB记录的是内存数据的快照,后续的快照会笼罩之前的快照,每次只保留最新的数据,效率更高;

    4) 如果数据容许大量失落,能够应用RDB模式。

    命令:

    # 立刻执行长久化操作save => 会造成线程阻塞# 后盾执行长久化操作bgsave => 不会造成阻塞,不分明他何时实现,不能保障立刻执行

    iii. AOF模式

    1) 默认的条件下是敞开的,须要手动的开启,开启之后RDB模式生效。然而如果手动执行save命令,也会生成RDB文件;

    2) 记录的是程序运行的过程,所以数据不失落;

    3) 因为记录的是程序运行的过程,长久化文件会很大,须要定期维护。

    开启AOF

    1) 编辑配置文件

    appendonly yes# The name of the append only file (default: "appendonly.aof")appendfilename "appendonly.aof" 

    2) 重启redis服务器

    redis-cli shutdownredis-server redis.conf

    iv. RDB和AOF长久化策略的比照

    1) RDB模式

    save 900 1 => 如果在900秒内,执行了一次更新操作,则长久化一次save 300 10save 60 1000 操作越快,则长久化的周期越短

    2) AOF模式

    appendfsync always 用户执行一次更新操作,则长久化一次 异步操作appendfsync everysec 每秒操作一次appendfsync no 不被动操作,个别不必

    v. 总结

    如果数据容许大量失落,首选RDB模式;如果数据不容许失落,首选AOF。

    企业策略:既满足效率,又要满足数据不失落。配置多个Redis服务器,主机应用RDB模式,读写效率快;从机应用AOF模式,备份数据。

    vi. 面试题

    小李误操作将redis服务器执行了flushAll的命令,作为项目经理你会如何解决?

    须要将从库中的AOF文件进行编辑,删除多余的flushAll命令,之后重启即可。

  2. 内存优化策略

    i. LRU算法

    1) 最近起码应用置换算法 - 淘汰最久未被应用的数据;

    2) 判断维度:工夫t

    ii. LFU算法

    1) 最不常常应用置换算法

    2) 判断维度:应用次数

    iii. 随机算法:随机删除

    iv. ttl算法:将剩余时间短的删除

    # volatile-lru -> Evict using approximated LRU among the keys with an expire set.# allkeys-lru-> Evict any key using approximated LRU.# volatile-lfu -> Evict using approximated LFU among the keys with an expire set.# allkeys-lfu -> Evict any key using approximated LFU.# volatile-random -> Remove a random key among the ones with an expire set.# allkeys-random -> Remove a random key, any key.# volatile-ttl -> Remove the key with the nearest expire time (minor TTL)# noeviction -> Don't evict anything, just return an error on write operations.
  3. Redis常见面试题

    业务场景:高并发环境下用户长时间对服务器进行操作,可能产生如下的问题:

    i. 什么是缓存穿透?

    高并发环境下,拜访数据库和缓存中都不存在的数据。导致大量的用户间接拜访数据库,而导致数据库连贯异样。

    解决方案:禁用IP,限度IP的拜访/限流 每秒最多拜访3次,布隆过滤器(用于检索一个元素是否存在某一个汇合中)

    二进制向量 -> 因为哈希碰撞,可能有多个key有雷同的地位,所以,布隆过滤器认为数据存在,那么数据可能存在;如果布隆过滤器认为数据不存在,则数据肯定不存在。

    解决方案:扩容二维向量的长度,应用多个哈希算法进行计算

    1G内存,如何判断200亿是否无效?

    ii. 什么是缓存击穿?

    某些热点数据在缓存中忽然生效,导致大量用户间接拜访数据库,导致并发压力过大,数据库异样。

    解决方案:将热点数据的超时工夫设置长一点,设置多级缓存(超时工夫采纳随机算法)

    iii. 什么是缓存雪崩?

    在缓存服务器中,因为大量的缓存数据生效,导致用户拜访的命中率过低,间接拜访数据库。flushAll会导致缓存雪崩,设定超时工夫,应采纳随机算法。

    解决办法:设置多级缓存

三. Redis分片机制
  1. Redis性能优化

    单台redis内存容量是无限的,然而如果有海量的数据要求实现缓存存储,要应用多个redis节点。这种构造称为Redis的分片机制。

  2. 分片机制配置

    i. 配置布局

    筹备三台redis服务器,端口号别离是6379, 6380, 6381

    # 敞开redis服务器redis-cli shutdown# 创立文件夹mkdir shards# 将三台服务器配置文件挪动到文件夹中cp redis.conf shards/6379.confcp redis.conf shards/6380.confcp redis.conf shards/6381.conf# 批改端口号cd /shardsvim 6380.conf# 开启服务redis-server 6379.confredis-server 6380.confredis-server 6381.conf# 查看服务器状态ps -ef | grep redis

    ii. 编写测试方法

    /* 用户通过客户端程序,动静链接3台redis服务器 */@Testpublic void testShards(){    List<JedisShardInfo> list = new ArrayList<>();    list.add(new JedisShardInfo("192.168.126.129",6379));    list.add(new JedisShardInfo("192.168.126.129",6380));    list.add(new JedisShardInfo("192.168.126.129",6381));    ShardedJedis shardedJedis = new ShardedJedis(list);    shardedJedis.set("shards","redis分片操作");    System.out.println(shardedJedis.get("shards"));}
  3. SpringBoot整合Redis分片

    i. 批改redis.porperties配置文件

    # 配置redis分片机制redis.nodes=192.168.126.129:6379,192.168.126.129:6380,192.168.126.129:6381

    ii. 批改RedisConfig配置类

    @Value("${redis.nodes}")private String nodes; //node,node,node@Beanpublic ShardedJedis shardedJedis(){    String[] nodeArr = nodes.split(",");    List<JedisShardInfo> list = new ArrayList<>();    for (String node:nodeArr){ //node = host:port        String host = node.split(":")[0];        int port = Integer.parseInt(node.split(":")[1]);        list.add(new JedisShardInfo(host,port));    }    ShardedJedis shardedJedis = new ShardedJedis(list);    return shardedJedis;}

    iii. 批改CacheAop

    @Autowiredprivate ShardedJedis jedis;