1.redis中的数据类型有哪些?
Redis有5种基础数据结构,分别为String(字符串),list(列表)、set(集合)、hash(哈希)和zset(有序集合)。
list—->linkedList,hash—->hashmap,set—->hashset,zset—->hashmap+sorteSet
2.为什么说redis能够快速运行?
1).绝大部分请求是纯粹的内存操作(非常迅速)
2).采用单线程,避免了不必要的上下文切换和竞争条件
3).非阻塞IO-IO多路复用
3.redis有几种持久化机制?
RDB持久化机制:
通过创建快照(压缩的二进制文件)的方式进行持久化,保存某个时间点的全量数据。
RDB持久化是Redis默认的持久化方式。RDB持久化的触发包括手动触发和自动触发两种方式
AOF持久化
AOF持久化,持久化即记录所有变更数据库状态的指令,以append的形式追加保存到AOF文件中。
在服务器下次启动时,就可以通过载入和执行AOF文件中。在服务器下次启动时,可以载入和执行AOF
文件中保存的命令,来还原服务器关闭前的数据库状态。
RDB、AOF混合持久化
redis从4.0开始支持RDB和AOF的混合持久化方案。首先由RDB定期完成内存快照的备份,
然后再由AOF完成两次RDB之间的数据备份,由这两部分共同构成持久化文件。该方案的优点是
充分利用了RDB加载快、备份文件小及AOF尽可能不丢数据的特性。缺点是兼容性太差
redis持久化方案的建议:
如果Redis只是用来做缓存服务器,比如数据库查询数据后缓存,那可以不用考虑持久化,因为
AOF和RDB的优缺点:
RDB持久化:
优点:RDB文件紧凑,体积小,网络传输快,适合全量复制;
恢复速度比AOF快很多。当然,与AOF相比,RDB最重要的优点之一是对性能的影响相对较小。
缺点:做不到实时持久化,只能过一段时间持久化一次,比如会导致之间间隔的数据丢失
AOF持久化:
与RDB持久化相对应,AOF的优点在于支持秒级持久化,兼容性好,缺点是文件大、恢复速度慢、对性能
影响大。
4.缓存穿透、缓存击穿、缓存雪崩解决方案?
缓存穿透:
指查询一个一定不存在的数据,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次都要去
DB查询,可能导致DB挂掉
一句话:查询不存在数据击穿数据库
解决方案:
1.从数据库查询的数据为null,仍然存储null
2.布隆过滤器:将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个
bitmap拦截掉,从而避免了对DB的查询。
缓存击穿:
对于设置了过期时间的key,缓存在某个时间点过期的时候,恰好这个时间点对key有大量的并发请求过来,
这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把DB击垮。
一句话:大量数据访问一个key过期数据把数据库击垮
解决方案:
1.使用互斥锁:当缓存失效时,不立即去load db,先使用如Redis的setnx去设置一个互斥锁,当操作
成功返回时再进行load db的操作并回设缓存,否则重试get缓存的方法。
static Lock reenLock = new ReentrantLock();
public List
List
// 从缓存读取数据
result = getDataFromCache();
10000
if (result.isEmpty()) {
10000
if (reenLock.tryLock()) {
try {
System.out.println(“我拿到锁了,从DB获取数据库后写入缓存”);
// 从数据库查询数据
result = getDataFromDB();
// 将查询到的数据写入缓存
setDataToCache(result);
} finally {
reenLock.unlock();// 释放锁
}
} else {
result = getDataFromCache();// 先查一下缓存
if (result.isEmpty()) {
System.out.println(“我没拿到锁,缓存也没数据,先小憩一下”);
Thread.sleep(100);// 小憩一会儿
return getData04();// 重试
}
}
}
return result;
}
使用lock锁(分布式情况下可以用分布式锁)去尝试是否获取锁,然后自旋,这样可以完美解决锁的等待。
2.永远不过期:物理不过期,但是逻辑过期(后台异步线程去刷新)
3.可以使用双重判断锁,虽然可以阻止高并发请求打到数据库,但是如果一个有30个请求过来,第一个处理完写到缓存
后面的29个依旧要走锁,速度会比较慢。
缓存雪崩:设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力
过重雪崩。与缓存击穿的区别:雪崩是很多key,击穿是某一个key缓存。
解决方案:将缓存失效时间分散开,比如可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个
缓存的过期时间的重复率就会降低,很难引发集体失效的事件。
5.redis的集群模式?
Cluster集群采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接
结构特点:
1.所有redis节点彼此互相连接,内部使用二进制协议优化传输速度和宽带。
2.节点失效是通过集群中超过半数的节点检测失效时才会生效。(2n+1个节点,n+1个节点宕机,redis则宕机)
3.客户端与redis直接连接,不需要中间proxy,客户端不需要连接集群所有节点,连接集群的任意一个节点就行。
4.redis-cluster把所有的物理节点映射到0-16383个slot(槽)上(不一定是平均分配),cluster负责维护
node<—>slot<—>value。
5.redis集群预分好16384个桶,当需要在redis集群中放置一个key-value时,根据哈希算法算出16384的值,决定将key
放到哪个桶中。
如何获取数据,因为数据可能在不同的数据片中?
如果存入了一个值,按照redis cluster哈希槽的算法:CRC16(‘key’)384=6782。那么就会把这个key存储分配到
B上了。同样,当我连接(A,B,C)任何一个节点想获取’key’这个key时,也会这样的算法,
然后内部跳转到B节点上获取数据。
如何新增一个主节点:
新增一个节点D,redis cluster的这种做法是从各个节点的前面各取一部分slot到D上,
redis
redis cluster为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,
从节点则是从主节点拉去数据备份,当这个主节点挂掉后,就会有这个从节点选取一个来当主节点,
从而保证集群不会挂掉。
6.使用过redis分布式锁吗,他是怎么回事?
先拿setnx来争抢锁,
7.redis的内存淘汰机制?(内存满的时候)
redis的内存淘汰机制有以下几个:
noeviction: 当内存不足以容纳新写入数据时,新写入操作会报错,这个一般没人用吧,实在是太恶心了。
* allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)。
allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 key,这个一般没人用吧,
为啥要随机,肯定是把最近最少使用的 key 给干掉啊。
8.redis和mysql的数据不一致怎么办?
一般我们在更新数据库数据时,需要同步redis中缓存的数据
所以一般存在两种方法:
1)第一种方案:先执行update操作,再执行缓存清除。
2)第二种方案:先执行缓存清除,再执行update操作。
弊端:当存在并发请求时,很容易出现问题
1)第一种方案:当请求1执行update操作后,还未来得及进行缓存清除,此时请求2查询到并使用了redis。
中的旧数据。
2)第二种方案:当请求1执行清除缓存后,还未进行update操作,此时请求2查询到了旧数据并写入了redis。
采用延时双删策略:
redis.delKey(key);
db.updateDate(data);
Thread.sleep(800);
redis.delKey(key);
1)先淘汰缓存
2)再写数据库(这两步和原来一样)
3)休眠800ms,再次淘汰缓存
这么做,可以将800ms内所造成的缓存脏数据,再次删除。
9.mySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据?
使用redis淘汰策略:
volatile-lru:从已经设置过期时间的数据集中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集中任意选择数据淘汰
allkeys-lru:从数据集中挑选最近最少使用的数据淘汰
allkeys-random:从数据集中任意选择数据淘汰
no-enviction:静止驱逐数据
使用策略规则:
1.如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用
allkeys-lru
2.如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用allkeys-random
10.Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将它们全部找出来?
可以使用key指令扫描出指定模式的key列表。
对方接着问:如果这个redis正在给线上的业务提供服务,那使用key指令会有什么问题?
这个时候你要回答redis的一个关键特征:redis是单线程的,keys指令会导致线程阻塞一段时间,
线上服务会停顿,知道指令执行完毕,服务才能恢复。这个时候可以用scan指令,scan指令可以无阻塞的提取
key列表,但是会有一定的重复率,在客户端去一次重就行了,但是整体所花费的时间会比直接用keys
指令时间长。
11.项目中有没有用到redis事务
采用的是redis cluster集群架构,不同的key是有可能分布在不同的redis节点上的,
在这种情况下redis的事务机制是不生效的,其次,redis事务不支持回滚操作,所以基本不用。