Redis面试
数据类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| String: 字符串 Hash: 散列 List: 列表 Set: 集合 Sorted Set: 有序集合 sadd smembers set01:查看 sismenber key member:判断 scard key:length srandmenber key number/-number :随机取出 spop key [count]:随机移除 smove set01 set02 key sinter set01 [set02]:都有 sunion set01 set02 set03 list(有序有下标) lpop/rpop(移除表尾) lpush/rpush(尾) lrange lindex key index:指定列表下标 llen:length lrem key count value
hash(无序不重复) hset key k1 v1 [k2 v2] hmset stu1002 id 100 name lisi hget key k1 hmget key k1 k2 hgetall:所有 k和v hlen hexists hvals hkeys stu001:获取所有key hvals stu001:获取所以value hsetnx:k有择失败
zset(有序集合 不能重复)
|
docker-redis
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 1. config get requirepass 查看redis密码 2. *docker exec -it redis /bin/bash* 进入redis容器 3. redis-cli -h host -p port -a password 进入redis 4. docker container rm 删除容器
# 关闭redis服务器 redis-cli -h 127.0.0.1 -p 6379 shutdown # 杀死redis服务器(比较暴力,谨慎使用) sudo kill -9 pid 进程号 # 指定配置文件启动redis sudo redis-server /etc/redis/redis.conf # 查看redis服务器进程 ps -ef | grep redis ps aux | grep redis # 启动redis客户端 redis-cli redis-cli -h ip地址 redis-cli -p 端口号
|
1.3springboot集成redis
1 2 3 4 5
| # 引入maven依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
|
缓存穿透、缓存击穿、缓存雪崩
一、缓存穿透
描述
指访问一个缓存和数据库中都不存在的key,由于这个key在缓存中不存在,则会到数据库中查询,数据库中也不存在该key,无法将数据添加到缓存中,所以每次都会访问数据库导致数据库压力增大。
解决方法
将空key添加到缓存中。
使用布隆过滤器过滤空key。
一般对于这种访问可能由于遭到攻击引起,可以对请求进行身份鉴权、数据合法行校验等。
二、缓存击穿
描述
指大量请求访问缓存中的一个key时,该key过期了,导致这些请求都去直接访问数据库,短时间大量的请求可能会将数据库击垮。
解决方法
- 添加互斥锁或分布式锁,让一个线程去访问数据库,将数据添加到缓存中后,其他线程直接从缓存中获取。(通常方案-p44)
添加锁 setnx lock 1 100ttl(防止死锁)
删除锁delete lock



三、缓存雪崩
描述
指在系统运行过程中,缓存服务宕机或大量的key值同时过期,导致所有请求都直接访问数据库导致数据库压力增大。
解决方法
将key的过期时间打散,避免大量key同时过期。
对缓存服务做高可用处理。
加互斥锁,同一key值只允许一个线程去访问数据库,其余线程等待写入后直接从缓存中获取。
一致性
redis内存回收
ttl过期时间
主动更新
- 先删除缓存,在更新数据库
- 先更新数据库在删除缓存(一般方案,操作内存快,操作数据库慢)

redisssion



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
|
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException { long time = unit.toMillis(waitTime); long current = System.currentTimeMillis(); long threadId = Thread.currentThread().getId(); Long ttl = tryAcquire(leaseTime, unit, threadId); if (ttl == null) { return true; } time -= System.currentTimeMillis() - current; if (time <= 0) { acquireFailed(threadId); return false; } current = System.currentTimeMillis(); RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId); if (!await(subscribeFuture, time, TimeUnit.MILLISECONDS)) { if (!subscribeFuture.cancel(false)) { subscribeFuture.onComplete((res, e) -> { if (e == null) { unsubscribe(subscribeFuture, threadId); } }); } acquireFailed(threadId); return false; }
try { time -= System.currentTimeMillis() - current; if (time <= 0) { acquireFailed(threadId); return false; } while (true) { long currentTime = System.currentTimeMillis(); ttl = tryAcquire(leaseTime, unit, threadId); if (ttl == null) { return true; }
time -= System.currentTimeMillis() - currentTime; if (time <= 0) { acquireFailed(threadId); return false; }
currentTime = System.currentTimeMillis(); if (ttl >= 0 && ttl < time) { getEntry(threadId).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS); } else { getEntry(threadId).getLatch().tryAcquire(time, TimeUnit.MILLISECONDS); }
time -= System.currentTimeMillis() - currentTime; if (time <= 0) { acquireFailed(threadId); return false; } } } finally { unsubscribe(subscribeFuture, threadId); }
}
|
业务未执行完ttl过期问题
redisson锁
可重入:redis的hash结构
可重试:waitTime 、leaseTiem(TTl)、锁释放消息订阅
业务超时锁过期问题:判断leasTiem是否等于-1启用watchdog(看门狗)
分布式集群一致性问题:不分主库和从库,multLock多把锁结合成一把锁
秒杀业务解耦


生成订单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
local voucherId = ARGV[1]
local userId = ARGV[2]
local orderId = ARGV[3]
local stockKey = 'seckill:stock:' .. voucherId
local orderKey = 'seckill:order:' .. voucherId
if(tonumber(redis.call('get', stockKey)) <= 0) then return 1 end
if(redis.call('sismember', orderKey, userId) == 1) then return 2 end
redis.call('incrby', stockKey, -1)
redis.call('sadd', orderKey, userId)
redis.call('xadd', 'stream.orders', '*', 'userId', userId, 'voucherId', voucherId, 'id', orderId) return 0
|
消费订单
- redis:扣减库存,使用阻塞队列指令LRPUSH、LRPOP
