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
@Autowired private 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 shutdown redis-server redis.conf
iv. RDB 和 AOF 长久化策略的比照
1) RDB 模式
save 900 1 => 如果在 900 秒内, 执行了一次更新操作, 则长久化一次 save 300 10 save 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.conf cp redis.conf shards/6380.conf cp redis.conf shards/6381.conf # 批改端口号 cd /shards vim 6380.conf # 开启服务 redis-server 6379.conf redis-server 6380.conf redis-server 6381.conf # 查看服务器状态 ps -ef | grep redis
ii. 编写测试方法
/* 用户通过客户端程序, 动静链接 3 台 redis 服务器 */ @Test public 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 @Bean public 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
@Autowired private ShardedJedis jedis;