前言
一、Redis
- 开源的
- 使用C语言编写
- 高性能基于内存运行的
- 非关系型数据库NoSql
- Key-Value数据库
二、数据类型
1、String
- 字符串,string类型是二进制安全的,所以redis的string 可以包含任何数据,比如jpg图片、序列化的对象
2、Hash
3、List
- 列表,相当于LinkedList
- 底层是通过链表来实现的,所以对于list来说首尾添加、删除 操作很快,但索引定位非常慢
- 可以利用它来实现一个栈、队列
4、Set
5、Zset
- 有序集合,不可重复
- 不是先添加的在前面,后添加的在后面
- 每个元素都会关联一个double类型的分数,通过分数来为集合中的成员进行从小到大排序
三、基础知识
- Redis是单进程单线程的网络模型,用的是epoll网络模型,网络模型都是单线程异步非阻塞处理网络请求
- Redis默认拥有16个数据库,并且每个数据库的数据是隔离的不能共享,不支持自定义数据库的名字,每个数据库都以编号命名db1,数据库编号从0开始,默认使用db0数据库;集群就没有数据库的概念,集群模式下只有一个db0,可以将项目名称做为该项目所有redis的key前缀,以避免多个项目共用一个redis存在key一样的情况,便于管理:项目名:业务名:key
- 多个数据库之间并不是完全隔离的,比如FLUSHALL命令可以清空一个Redis实例中所有数据
库中的数据,所以说这些db更像是命名空间,且不适宜存储不同应用程序的数据 - Redis提供的所有API操作,相对于服务端方面都是one by one执行的,命令是一个接着一个执行的,不存在并行执行的情况
四、Redis应用场景
1、热点数据的缓存
- 由于redis访问速度快,很适合用来存储热点数据,例如 热点商品、热点信息(明星出轨)等访问量较高的数据
2、限时业务的运用
- redis中可以使用expire设置一个key的生存时间,到时间后会删除它,利用这一特性可以运用在限时的优惠活动信息、手机验证码等
3、计数、限流相关问题
- redis种的增量increment可以实现原子性的递增,可以运用于高并发的秒杀活动、分布式序列号的生成、具体业务,比如限制一个手机号发多少条验证码、一个接口一分钟限制多少请求、一段时间内一个账号登录密码错误次数
- 登录密码错误次数限制
String count=valueOperations.get(key);
if(count!=null&&Long.valueOf(count).equals(failTimes)){
Long expire=stringRedisTemplate.getExpire(key,TimeUnit.MINUTES);
return "您输入密码错误次数过多,请{0}分钟后重试",expire;
}
Long count=valueOperations.increment(key,1L);
if(count.equals(failTimes)){
stringRedisTemplate.expire(key,10,TimeUnit.MINUTES);
return "您输入密码错误次数过多,请{0}分钟后重试",expire;
}
stringRedisTemplate.delete(key);
- 序列号生成
当项目迁移时数据库中数据有最大序列号,但是redis中没有,此时初始化为0是不对的;应该先判断redis中为空,再查询数据库中业务表使用的最大序列号,如果数据库中不为空,则利用缓存击穿的解决方式1来set最大序列号,然后再递增
public String generateNewBatch(){
HashOperations<String,String,String> hashOperations=stringRedisTemplate.opsForHash();
ValueOperations<String,String> valueOperations=stringRedisTemplate.opsForValue();
String key="project"+":";
String batchKey=key+"batch";
String batchOne="1";
long batch;
final long step=1L;
Boolean exists=hashOperations.hasKey(batchKey,batchOne);
if(exists){
batch=hashOperations.increment(batchKey,batchOne,step);
save(new SyBatch().setSyId(batchOne).setBatch(batch));
}else{
String batchUpdateKey=key+"batch_update"+":"+batchOne;
if(valueOperations.setIfAbsent(batchUpdateKey,batchUpdateKey,Duration.ofSeconds(30L))){
Long maxBatch=syBatchMapper.queryMaxBatchBySyId(batchOne);
if(maxBatch==null){
batch=hashOperations.increment(batchKey,syIdStr,step);
save(new SyBatch().setSyId(batchOne).setBatch(batch));
hashOperations.put(batchKey,batchOne,String.valueOf(batch));
}else{
batch=maxBatch+step;
save(new SyBatch().setSyId(syId).setBatch(batch)
hashOperations.put(batchKey,batchOne,String.valueOf(batch));
}
stringRedisTemplate.delete(batchUpdateKey);
}else{
Thread.sleep(30000);
generateNewBatch();
}
}
return LocalDate.now().toString().replaceAll("-","")+String.format("%02d",batch);
}
4、排行榜
- redis的zSet可以根据score顺序排列,新闻id作为value,点击数作为score,新闻点击一次,对应的新闻的score+1,最后根据reverseRange,获取指定区间的点击数最多的新闻
5、分布式锁
- redis的setIfAbsent只有不存在才会set进去,服务器是集群的,定时任务可能在两台机器上都会运行,所以在定时任务中首先set一个lock,如果set成功则执行,set失败则该定时 任务正在执行,无需再执行;再结合过期时间
if(redisTemplate.setIfAbsent(key,value)){
}else{
}
6、消息队列
- redis的List操作的 左、右push、pop操作
- 队列做触发式操作
7、点赞、签到、打卡
- redis的Set,微博id作为key
- 点赞的用户 = add这个集合
- 取消点赞 = remove这个用户
- 是否点赞 = 判断这个集合中是否存在该用户
- 点赞的所有用户 = 获取这个集合中的所有用户点赞数 = 集的size
8、共同好友