day 12 Redis分片
此文档是依据上课流程整顿。更多细节及图片请参见刘老师的专栏
江哥的专栏
《cgb2008-京淘day12》
一. AOP形式实现商品分类缓存
- 业务需要
通过自定义注解的模式,动静实现缓存操作。拦挡的指标是带有指定注解的办法。通过注解获取其中的key和超时工夫。
编辑@CacheFind注解
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface CacheFind { public String key(); public int seconds() default 0; //不须要设置超时工夫}
自定义注解的用法
== ItemCatServiceImpl ==@CacheFind(key = "ITEM_CAT_PARENT",seconds = 7*24*60*60) //具体的key主动补齐public List<EasyUITree> findItemCatCache(long pid) {}
编辑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;}
为查问商品类目标办法加上注解
@Override@CacheFind(key = "ITEM_CAT")public String findCatNameByCid(int cid) { return itemCatMapper.findCatNameByCid(cid);}
二. Redis根本个性
长久化策略
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命令,之后重启即可。
内存优化策略
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.
- Redis常见面试题
业务场景:高并发环境下用户长时间对服务器进行操作,可能产生如下的问题:
i. 什么是缓存穿透?
高并发环境下,拜访数据库和缓存中都不存在的数据。导致大量的用户间接拜访数据库,而导致数据库连贯异样。
解决方案:禁用IP,限度IP的拜访/限流 每秒最多拜访3次,布隆过滤器(用于检索一个元素是否存在某一个汇合中)
二进制向量 -> 因为哈希碰撞,可能有多个key有雷同的地位,所以,布隆过滤器认为数据存在,那么数据可能存在;如果布隆过滤器认为数据不存在,则数据肯定不存在。
解决方案:扩容二维向量的长度,应用多个哈希算法进行计算
1G内存,如何判断200亿是否无效?
ii. 什么是缓存击穿?
某些热点数据在缓存中忽然生效,导致大量用户间接拜访数据库,导致并发压力过大,数据库异样。
解决方案:将热点数据的超时工夫设置长一点,设置多级缓存(超时工夫采纳随机算法)
iii. 什么是缓存雪崩?
在缓存服务器中,因为大量的缓存数据生效,导致用户拜访的命中率过低,间接拜访数据库。flushAll会导致缓存雪崩,设定超时工夫,应采纳随机算法。
解决办法:设置多级缓存
三. Redis分片机制
- Redis性能优化
单台redis内存容量是无限的,然而如果有海量的数据要求实现缓存存储,要应用多个redis节点。这种构造称为Redis的分片机制。
分片机制配置
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"));}
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;