面试-redis基础01

0. 参考文章

阿里巴巴大牛文章-掘金
lengyue1024-github

1. 什么是redis

基于k-v这种模式的内存数据库,整个操作都是在内存中进行操作,且能通过异步把内存中的数据同步到磁盘中。性能非常,每次可以读写10w次的操作。单线程。

1.1 redis为什么要使用单线程的:

  1. 因为多线程涉及cpu上下文切换问题,对于在内存中操作的redis来说挺得不偿失的,上下文切换需要时间呀,且多线程也提高了代码的复杂度,设计各种锁。
  2. 官方:
    因为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有哪些优势?

  1. memcached没有做持久化
  2. redis速度更快
  3. memcached数据类型只有String

22 假如Redis里面有1亿个key,其中有10w个key是以某个固定kangyu的已知的前缀开头的,如何将它们全部找出来?

  1. 可以使用keys kangyu* 命令查找出来
    但是使用keys命令有个缺陷就是会阻塞线程。
  2. 可以使用scan来代替,不阻塞,并且可以指定返回的条数。
    基于游标, scan 0(游标) match a*(匹配值) count 100000(单词遍历多少个数)
    在这里插入图片描述会返回两个值
    1) “0” 表示已经查完了,如果返回"111"数字,那么下个游标值就是111,
    2) 匹配到的数值。
    缺点:
    对于 SCAN 这类增量式迭代命令来说, 因为在对键进行增量式迭代的过程中, 键可能会被修改, 所以增量式迭代命令只能对被返回的元素提供有限的保证。

五种数据类型使用场景

hash: 存储对象信息
set: 唯一性,存储访问所有独立ip

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值