1.什么是Redis?
Redis是一个高性能的key-value数据库,它是完全开源免费的,而且Redis是一个NOSQL,即非关系类型数据库,是为了解决高并发、高扩展、大数据存储等一系列的问题而产生的数据库解决方案。
Redis是一个以key-value存储的数据库结构型服务器,它支持的数据库结构类型包括:字符串(String)、链表(lists)、哈希表(hash)、集合(set)、有序集合(Zset)等。为了保证读取的效率,Redis把数据对象都存储在内存当中,它还可以支持周期性的把更新的数据写入磁盘文件中。而且它还提供了交集和并集,以及一些不同方式排序的操作。
2.Redis具体支持哪些数据类型?
String字符串
命令:set key value
String类型是二进制安全的,它可以包含任何数据,如图片或序列化对象等。String类型是Redis最基本的数据类型,一个键最大能存储512MB。
Hash(哈希)
命令:hmset name key1 value key2 vakue2
Redis hash 是一个键值(key=>value)对集合
Redis hash 是一个String类型的field和value的映射表,hash特别适合用于存储对象。
List(链表)
Redis列表是简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边)
命令:lpush name value key对应list的头部添加字符串元素
命令:rpush name value key对应list的尾部添加字符串元素
命令:Irem name index key对应list中删除count个和valu相同的元素
命令:llen name 返回key对应list的长度
Set(集合)
命令:sadd name value
Redis的Set是String类型的无序集合,集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
zset(sorted set:有序集合)
命令:zadd name score value
Redis zset 和 set 一样也是String类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。Redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
3.什么是Redis持久化?Redis有哪几种持久化方式?
Redis持久化是指把内存的数据写到磁盘中去,防止服务因宕机导致内存数据丢失。
Redis提供了两种持久化方式:RDB(默认)和AOF
RDB是Redis DataBase的缩写,功能核心函数rdbSave(生成RDB文件)和rdbLoad(从文件加载内存)两个函数。
AOF是Append-only file的缩写,每当执行服务器(定时)任务或者函数时flushAppendOnlyFile函数都会被调用,这个函数执行以下两个工作。
AOF写入与保存
WRITE:根据条件,将aof_buf中的缓存写入到AOF文件
SAVE:根据条件,调用fsync或fdatasync函数,将AOF文件保存到磁盘中。
4.什么是缓存穿透?如何避免?
缓存穿透是指一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB),一些恶意的请求会故意查询不存在的key,导致请求量大,造成后端系统压力大,这就是缓存穿透。
如何避免?
(1)对查询结果为空的情况也进行缓存,缓存时间设置短一点或key对应的数据insert后清理缓存。
(2)对一定不存在的key进行过滤,可以把所有可能存在的key放到一个大的Bitmap中,查询时通过bitmap过滤。
5.什么是缓存雪崩?如何避免?
缓存雪崩是指当缓存服务器重启或大量缓存集中在某一个时间段失效,这样在失效时,会给后端系统带来很大压力,导致系统崩溃。
如何避免?
(1)在缓存失效后,使用加锁或队列的方式来控制读数据库写缓存的线程数量。如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
(2)实现二级缓存方式,A1为原始缓存,A2为拷贝缓存,A1失效时,切换访问A2,A1缓存失效时间设置为短期,A2设置为长期。
(3)不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
6.为什么用Redis而不用Map、Guava做缓存?
缓存可以划分为本地缓存和分布式缓存:
以Java为例,使用自带Map或者Guava类实现实现的是本地缓存,主要特点是轻量以及快速,它们的生命周期会随着JVM的销毁而结束,且在多实例的情况下,每个实例都需要各自保存一份缓存,缓存不具有一致性。
使用Redis或Memcached等被称为分布式缓存,在多实例的情况下,各实例共用一份缓存数据,具有一致性,但是需要保持Redis或Memcached服务的高可用。
7.Redis过期键都有哪些删除策略?
Redis是key-value数据库,可以设置Redis中缓存key的过期时间。Redis的过期策略是指当Redis中缓存的key过期,Redis是如何处理。常见的删除策略有以下三种:
定时删除
在设置key的过期时间的同时,创建一个定时器,让定时器到过期时间就立即执行对key的删除操作。需要注意的是这种策略可以立即清除过期的数据,对内存很友好,但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
惰性删除
只有当访问一个key时,才会判断这个key是否已过期,如果过期就删除,反之没有过期,就返回改key。需要注意的是这种策略可以最大化地节省CPU资源,但是对内存非常不友好。甚至极端情况可能出现大量地过期key,因没有再次被访问从而不会被删除,占用大量内存。
定期删除
每隔一段时间,程序对数据库进行一次检查,删除里面地过期键,置于要删除哪些数据库地哪些过期键,则由算法决定。需要注意的是这种策略是前两者的一个折中方案,通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
expires字典保存所有设置过期key的过期时间数据。
key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。
其中定时删除和定期删除为主动删除策略,惰性删除为被动删除策略,Redis中同时使用了惰性删除和定期删除两种删除策略。
8.Jedis和Redisson有什么优缺点?
Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持。
Redisson是一个高级的分布式协调Redis客户端,能帮助用户在分布式环境中轻松实现一些Java的对象。Redisson实现了分布式和可扩展的Java数据结构,和Jedis相比,功能较为简单,不支持字符串操作,不支持排序,事务,管道,分区等Redis特性。Redisson的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。
9.Redis如何设置密码以及验证密码?
Redis设置密码:config set requirepass 123456
Redis授权密码:auth 123456
10.Redis事务命令都有哪几个?
MULTI、EXEC、DISCARD、WATCH
multi:标记一个事务块地开始,返回ok
exec:执行所有事务块内,事务块内所有命令执行地先后顺序的返回值,操作被,返回空值null
discard:取消事务,放弃执行事务块内的所有命令,返回ok
watch:监视key在事务执行之前是否被其他指令改动,若已修改则事务内的指令取消执行,返回ok
unwatch:取消watch命令对key的监视,返回ok
11.Redis key 如何设置过期时间和永久有效?
Redis key 设置过期时间使用EXPIRE命令
Redis key 设置永久有效使用PERSIST命令
12.Redis回收进程是如何工作的?
客户端运行新的命令,添加了新的数据时Redis检查内存使用情况,如果大于maxmemory的限制,则根据设定好的策略进行回收。新的命令被执行等,通过不断地穿越内存限制的边界,不断达到边界后回收到边界以下。如果一个命令的结果导致大量内存被使用,例如很大集合的交集保存到一个新的key,不用多久这个使用的内存数量就会超越内存限制。
13.Redis回收使用的是什么算法?
Redis回收使用的是LUP算法,LUP是Least Recently Used 的缩写,即最近最少使用页面置换算法,是为虚拟页式存储管理服务的。
14.Redis如何实现大量数据插入?
Redis2.6版本开始redis-cli支持一种新的数据插入方式,被称之为pipe mode的新模式,用于执行大量数据插入工作。
15.Redis和其他key-value存储有什么不同?
Redis有着更为复杂的数据结构并且提供对他们的原子性操作,这是一个不同于其他数据库的进化路径。Redis数据类型都是基于基本数据结构,同时对开发人员透明,无需进行额外的抽象。
Redis运行在内存中,但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,应为数据量不能大于硬件内存。
内存数据库方面的优点是在内存比在磁盘上相同复杂的数据结构上操作更为简单,这样Redis可以做很多内部复杂性很强的事情。同时,在磁盘格式方面它们是紧凑并以追加的方式产生,因为它们不需要进行随机访问。
16.Redis和Memcached有哪些区别?
(1)Redis是单进程单线程的,使用了I/O口多路复用器,高并发情况下不存在数据安全问题;而Memcached支持多线程。
(2)Redis存储key-value结构的数据,Value支持多种数据类型,比如String、Hash、Set、SortedSet、List类型;而Memcached仅支持简单的key-value结构的数据。
(3)Redis支持数据持久化,服务器重启后数据可以恢复;而Memcached不支持数据持久化,服务器重启后数据无法恢复。
(4)Redis中List类型支持排序;而Memcached不支持排序
(5)Redis中value值最大可以存储512MB;而Memcached中key的最大长度为255个字符,value最大可以存储1MB。
(6)Memcached和Redis在数据的写入效率基本相差无几,但是在数据的读取尤其是批量数据的读取时,Memcached的效率更高。
Redis使用单核,而Memcached可以使用多核,平均每一个核上Redis在存储小数据时比Memcached性能更高。而在100k以上的数据中,Memcached性能要高于Redis。虽然Redis最近在存储大数据的性能上进行优化,但是比起Memcached,还是稍有逊色。
17.Redis都有哪些使用场景?
(1)延迟操作
订单入库时数据量过于庞大,可以用Redis缓存数据量,减轻数据库压力,再分批处理数据。
(2)点赞、好友等相互关系
(3)Session共享
使用Redis可以实现单点登录,通过expire控制是否失效问题
(4)分布式锁
(5)计数器
(6)排行耪
使用Redis中SortedSet类型进行数据的排序
(7)模糊查询
数据量大时可以使用Redis缓存,进行模糊搜索
(8)队列
Redis支持list push和list pop命令,因此很方便的执行队列操作
18.Redis持久化数据如何实现扩容?
Redis当作缓存使用时,可以通过一致性哈希实现动态扩容缩容。
Redis当作持久化存储数据使用时,必须使用固定的keys-to-nodes映射关系,节点的数量一旦确当不能变化。否则的话(即Redis节点需要动态变化的情况)必须使用可以在运行时进行数据再平衡的一套系统,而当前只有Redis集群可以做到这样。
19.Redis将内存占满后会发生什么问题?
如果达到设置的上限,Redis的写命令会返回错误信息,但是读命令还可以正常返回。可以配置内存淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。
20.Redis如何处理数据存储实现内存优化?
可以通过使用Hash、list、sorted set、set等集合类型数据,因为通常情况下很多小的key-value可以用更紧凑的方式存放到一起,尽可能使用散列表(hashes),散列表(意思是说散列表里面存储的数少)使用的内存非常小,所以应该尽可能地将你的数据模型抽象到一个散列表里面。
比如web系统中有一个用户对象,不要为这个用户地名称、姓氏、邮箱、密码设置单独的key,而是把这个用户的所有信息存储在一张散列表周末中。
21.什么是Redis事务?
Redis事务的本质是通过MULTI、EXEC、WATCH等一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中,总的来说redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
22.Redis事务支持隔离性吗?
Redis是单进程程序且保证在执行事务时,不会对事务进行中断,事务可以运行直到执行完所有事务队列中的命令为止。因此,Redis的事务总是带有隔离性的。
23.Redis事务能否保证原子性,是否支持回滚?
Redis中单条命令是原子性执行的,但事务不能保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。
24.Redis集群会产生数据丢生情况吗?
Redis并不能保证数据的强一致性,也就是说在实际中Redis集群在待定的条件下可能会产生数据丢生的情况。且Redis集群之间是通过异步复制的。
25.Redis是单线程的,如何提高多核CPU的利用率?
可以在同一个服务器部署多个Redis实例,并把它们当作不同的服务器来使用,在某些时候,无论如何一个服务器是不够的,所以,如果你想使用多个CPU,你可以考虑一下分片(shard)。
26.Redis有什么优势?
1.性能高,速度快
Redis命令执行速度非常快,读写性能可达到10w/秒,快的原因主要有以下几个因素:
- 数据存储在内存中,直接与内存连接。
- 由相对底层的C语言实现,离操作系统更近。
- 实现源码很精湛,仅仅几万行代码,简单稳定
- 使用了单线程模型,无多线程竞争、锁等问题
2.丰富的数据结构
27.如何解决Redis和数据库双写一致性的问题?
当数据时效性要求很高时,需要保证缓存中的数据与数据库中的保持一致,而且需要保证缓存节点和副本中的数据保持一致,不能出现差异现象,这就比较依赖缓存的过期和更新策略。一般会在数据发生更改时,主动更新缓存中的数据或者移除对应的缓存。
- 采取正确更新策略,先更新数据库,再删缓存
- 可能存在删除缓存失败的问题,提供一个消息队列作为补偿措施
Redis如何在实际项目中部署
SpringBoot整合Redis操作
要想在程序中能够使用java代码操作redis,需要在pom文件中添加如下依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
application.properties文件中指定redis的位置
# redis位置的配置
spring.redis.host=localhost
spring.redis.port=6379
application.properties文件中添加启动session共享的配置
# 设置利用redis实现session共享
spring.session.store-type=redis
因为我们要实现session共享,所以在SpringBoot启动类中添加注解
@SpringBootApplication
@EnableEurekaClient
@MapperScan("cn.tedu.knows.sys.mapper")
@EnableRedisHttpSession
public class KnowsSysApplication {
public static void main(String[] args) {
SpringApplication.run(KnowsSysApplication.class, args);
}
}
测试Redis的使用
主要是通过调用redisTemplate下的opsForValue()的方法来完成对key-value的赋值取值操作
@Resource
RedisTemplate<String,String> redisTemplate;
@Test
void testHello(){
//增
/*redisTemplate.opsForValue().set("msg","my message");
System.out.println("ok");*/
//查
/*String msg=redisTemplate.opsForValue().get("msg");
System.out.println(msg);*/
//删除
redisTemplate.delete("msg");
}
以上操作是在一个微服务下进行的,要想进行Session共享,需要对其他微服务进行同样的配置,即三大操作——1.pom文件添加依赖,2.application.properties文件中配置指定redis的位置以及添加启动session共享,3.启动类上添加注解@EnableRedisHttpSession