文章目录
- 0. 参考文章
- 1. 什么是redis
- 1.1 redis为什么要使用单线程的:
- 2. 为什么要用redis.
- 3 . 如何理解redis的k-v
- 4. Redis官方为什么不提供Windows版本?
- 5. 热点数据问题
- 6. redis常用命令
- 7. Redis回收使用的是什么算法?
- 8. Redis的内存占用情况怎么样?
- 9. 都有哪些办法可以降低Redis的内存使用情况呢?
- 10. Redis提供了哪几种持久化方式?有什么区别
- 11. aor与rdb区别:
- 11.1 rdb持久化原理
- 12. 什么是混合持久化?作用?
- 13. 日志文件重写?做了什么事情。
- 14. redis做如何异步队列
- 15. Pipeline有什么好处,为什么要用pipeline?
- 16. redis 主从复制,哨兵模式
- 17. Redis有哪些适合的场景?
- 18. 如何保证数据库与redis中数据一致性
- 19. redis缓存击穿、缓存雪崩、缓存穿透
- 20 基于redis实现分布式锁
- 21 Redis相比memcached有哪些优势?
- 22 假如Redis里面有1亿个key,其中有10w个key是以某个固定kangyu的已知的前缀开头的,如何将它们全部找出来?
- 五种数据类型使用场景
0. 参考文章
阿里巴巴大牛文章-掘金
lengyue1024-github
1. 什么是redis
基于k-v这种模式的内存数据库,整个操作都是在内存中进行操作,且能通过异步把内存中的数据同步到磁盘中。性能非常,每次可以读写10w次的操作。单线程。
1.1 redis为什么要使用单线程的:
- 因为多线程涉及cpu上下文切换问题,对于在内存中操作的redis来说挺得不偿失的,上下文切换需要时间呀,且多线程也提高了代码的复杂度,设计各种锁。
- 官方:
因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。
2. 为什么要用redis.
a. 因为整个系统最大的瓶颈就在于数据库,为什么减少数据库的IO操作,同时也为了用户更好的体验。
b. 支持多个数据结构
c. value最大可以存512M的数据。
d. 内部采用多路复用技术(速度快…emmmmmmm…看了半天也没明白)。
3 . 如何理解redis的k-v
a. key值是二进制安全的,所以把字符串或者JPEG的文件内容当做key也是可以的。
b. v支持5中数据类型的结构,包括: String、list、set、hash、sorted set
4. Redis官方为什么不提供Windows版本?
linux版本已经非常稳定,开发可能会带来兼容性等各种问题。
5. 热点数据问题
MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据?
可以通过redis的淘汰策略来保证。
淘汰策略一共有以下六种
noeviction(默认): 如果内存达到最大的时候,只允许读,不允许写。
allkeys-lru: 尝试回收最少使用的键,让新添加的数据有数据存放。
allkeys-random: 回收随机的键使得新添加的数据有空间存放。
volatile-lru: 回收最少使用的键,但仅限于有过期时间键。
volatile-random:回收随机的键,但仅限于有过期时间键。
volatile-ttl: 回收在过期集合的键,并且优先回收存活时间(TTL)较短的键
已经过期的键自动删除了,不需要回收。
6. redis常用命令
键key: keys*,exist key(判断key是否存在),ttl(查看key的过期时间),expire key为给定的key设置过期时间,type key(查看key的类型)
string:
get 获取值
set 赋值
del 删除
incr 增加
decr 减少
setnx key value : key不存在时候设置key的值。
mset key1 value key2 value : 同时赋值多个
mget key1 key 2 : 获取所有(一个或多个)给定 key 的值
list:
字符串列表,内部用的是Linked list ,查询较慢,增删改较慢
lpush key value:从左边新增一个值
rpush key value: 从右边新增一个值
lrange key 0 -1: 获取指定范围内的值。-1代表最后一个,-2代表倒数第二个
lpop:获取并移除列表的第一个元素
rpop: 获取并移除列表的最后一个元素
set:
sadd key value1 value 2
hash:
是一个键值对集合,适合存储对象。
命令和string类型的差不多,前面加了个h而已。
hset,hget,hmset,hmget
7. Redis回收使用的是什么算法?
Lru算法。
8. Redis的内存占用情况怎么样?
100w的数据,kv都是数字,存放在redis中,内存占用量才84m。
用info查看内存使用率
9. 都有哪些办法可以降低Redis的内存使用情况呢?
可以好好利用Hash,list,sorted set,set等集合类型数据,因为通常情况下很多小的Key-Value可以用更紧凑的方式存放到一起。
10. Redis提供了哪几种持久化方式?有什么区别
RDB与AOF两种方式
11. aor与rdb区别:
a. RDB是以二进制的方式全量(所有的内存数据)持久化存储,而AOF是以命令的方式增量(新添加的命令)持久化存储。
b. 由于是二进制方式存储,所有从本地磁盘加载数据到内存中的时候速度快,且占用内存小。而AOF相对而言速度慢,占用内存高。
c. RDB默认情况下是每多少秒有多少次改动从而持久化,而AOF是每秒或者每执行一行命令就持久化一次。因此RDB可能存在数据丢失,而AOF仅仅会丢失很少的数据(默认1s内)
11.1 rdb持久化原理
两个关键字:fork、cow
创建出和主线程一模一样的线程,然后进行读写到一个零时文件中。待持久化结束了,再用这个零时文件代替上一次持久化文件。整个过程,主线程不进行任何io操作,确保了极高的性能。
注意(个人猜想):比如我在10点的时候开始持久化,那么10点过后的数据不会持久化。
12. 什么是混合持久化?作用?
混合持久化: 同时开启aof与rdb,最终听aof的。
在4.0以前不支持混合持久化,4.0以后支持混合持久化,5.0开始默认打开混合持久化。
作用:当开了混合持久化后,aof日志文件重写的部分会变成rdb格式的二进制文件。
13. 日志文件重写?做了什么事情。
rdb由于是二进制文件,所以没有重写概念。重写仅仅针对aof,aof重写和两个参数关联(auto-aof-rewrite-percentage 、auto-aof-rewrite-min-size)。
例如:(比率:auto-aof-rewrite-percentage 80,内存大小:auto-aof-rewrite-min-size64mb)当持久化文件大小大于64m*0.8的时候,开始重写。
做了什么事情:
重写只的是对命令进行重新压缩,比如之前我们set a 1, set a 2 。 那么重写后文件里仅仅是保存了set a 2 ,因为set a 1 确实没用了。
14. redis做如何异步队列
使用list的rpush与lpop来进行操作。在实际开发中lpop死循环一直去拿值,为了减轻服务器的压力可以sleep等待一会。
可以不用sleep吗?
当然可以,可以用lpop的升级版本阻塞式blpop。阻塞式还可以指定阻塞时间。
15. Pipeline有什么好处,为什么要用pipeline?
将多个命令封装后,一次性去执行,减少了IO操作。提高了效率。
16. redis 主从复制,哨兵模式
为了防止单个节点挂掉后无法使用,所以通过主从复制来搭建集群,主负责读写,而从的一方只能读。 这样主一旦挂了,从立马就能变成主(哨兵模式可以自动使从变成主,如果不用哨兵模式必须手动去把从变成主。)
17. Redis有哪些适合的场景?
1. 数据缓存
2. spring session redis 分布式登录
3. 队列
4. 计数器 incr
5. 由于redis提供了过期时间,所以支付的还可以设置过期时间。
18. 如何保证数据库与redis中数据一致性
1. 实时保存,在把数据保存到数据库的时候,更新redis
2. 异步更新,定时任务去做。
3. 在dounion的时候,做了一个更新redis按钮。
19. redis缓存击穿、缓存雪崩、缓存穿透
**缓存穿透:** 缓存和数据库都不存在的值,那么这个时候如果有人恶搞的话,数据库很容易挂掉。
如何解决:
1. 如果数据不存在,那么给这个值设为空,且设置过期时间5s-20s秒。
2. 采用布隆过滤器
3. 加条件判断,比如过滤掉id为负数的。
**缓存击穿:**: 某个有过期时间的键失效了,这个时候大量的请求过来了,db会挂掉。
如何解决:
1. 设置永不过期
2. 采用互斥锁(setnx),每次只允许一个线程获取到这把锁,其他的睡眠几秒,然后再次去尝试获取。
互斥锁:在编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对
应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
缓存雪崩:大量有过期时间的key在同一时间过期了,是缓存击穿的放大版本。
如何解决:在每个时间后面加个随机毫秒。
20 基于redis实现分布式锁
可以通过setnx互斥锁来实现。
/**
* @Author kangyu
* @Description
* 1. 使用setnx实现,
* @Date 15:49 2019/11/25
* @Param []
* @return java.lang.String
**/
@ResponseBody
@RequestMapping("/reduceStock")
public String reduceStock() {
String k = "k";
String v = UUID.randomUUID() + "";
System.out.println(1122);
//jedis setnx使用 如果存在则返回false,不存在返回true
Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent(k, v);
if (!aBoolean) {
System.out.println("error");
return "error";
}
System.out.println("开始:" + v);
stringRedisTemplate.expire(k, 2 , TimeUnit.SECONDS);
try {
String stockNum = stringRedisTemplate.opsForValue().get("stockNum");
System.out.println("库存数量:" + stockNum);
if (Integer.valueOf(stockNum) > 0) {
stringRedisTemplate.opsForValue().set("stockNum", (Integer.valueOf(stockNum) - 1) + "");
} else {
System.out.println("库存不足");
}
} finally {
System.out.println("结束:" + v);
// 只释放自己的锁,不释放其他线程的锁
if (v.equals(stringRedisTemplate.opsForValue().get(k))) {
System.out.println("释放自身的锁机制");
stringRedisTemplate.delete(k);
}
}
return "seccess";
}
/**
* @Author kangyu
* @Description
* 1. redisson实现,
* @Date 15:49 2019/11/25
* @Param []
* @return java.lang.String
**/
@ResponseBody
@RequestMapping("/redisson_reduceStock")
public String redisson_reduceStock() {
String k = "k";
RLock lock = redisson.getLock(k);
lock.lock(30, TimeUnit.SECONDS);
System.out.println("开始:" + UUID.randomUUID());
try {
String stockNum = stringRedisTemplate.opsForValue().get("stockNum");
System.out.println("库存数量:" + stockNum);
if (Integer.valueOf(stockNum) > 0) {
stringRedisTemplate.opsForValue().set("stockNum", (Integer.valueOf(stockNum) - 1) + "");
} else {
System.out.println("库存不足");
}
} finally {
System.out.println("结束");
lock.unlock();
}
return "seccess";
}
21 Redis相比memcached有哪些优势?
- memcached没有做持久化
- redis速度更快
- memcached数据类型只有String
22 假如Redis里面有1亿个key,其中有10w个key是以某个固定kangyu的已知的前缀开头的,如何将它们全部找出来?
- 可以使用keys kangyu* 命令查找出来
但是使用keys命令有个缺陷就是会阻塞线程。 - 可以使用scan来代替,不阻塞,并且可以指定返回的条数。
基于游标, scan 0(游标) match a*(匹配值) count 100000(单词遍历多少个数)
会返回两个值
1) “0” 表示已经查完了,如果返回"111"数字,那么下个游标值就是111,
2) 匹配到的数值。
缺点:
对于 SCAN 这类增量式迭代命令来说, 因为在对键进行增量式迭代的过程中, 键可能会被修改, 所以增量式迭代命令只能对被返回的元素提供有限的保证。
五种数据类型使用场景
hash: 存储对象信息
set: 唯一性,存储访问所有独立ip