redis缓存数据库

    科技2025-04-18  8

    文章目录

    redist特点redis安装及配置redis-javaredis的线程模型redis 常见数据结构以及使用场景分析StringHashListSetSorted Set redis 设置过期时间redis 内存淘汰机制(MySQL里有2000w数据,Redis中只存20w的数据,如何保证Redis中的数据都是热点数据?)redis 持久化机制(怎么保证 redis 挂掉之后再重启数据可以进行恢复)redis 事务缓存雪崩和缓存穿透问题解决方案**缓存雪崩****缓存穿透** 如何保证缓存与数据库双写时的数据一致性?

    面试还搞不懂redis,快看看这40道面试题(含答案和思维导图)

    redist特点

    高性能,持久存储,适应高并发的应用场景。相比许多键值数据存储,Redis拥有一套较为丰富的数据类型。Redis数据库完全在内存中,使用磁盘仅用于持久性。Redis可以将数据复制到任意数量的从服务器。操作都是原子性:所有Redis操作是原子的,这保证了如果两个客户端同时访问的Redis服务器将获得更新后的值。多功能实用工具:Redis是一个多实用的工具,例如缓存,消息队列使用(Redis原生支持发布/订阅),任何短暂的数据,应用程序,如Web应用程序会话,网页命中计数等。

    redis安装及配置

    windows安装文件目录说明 redis配置(reids.windows.conf) 启动并执行 在cmd中执行:redis-server.exe redis.windows.conf,启用服务在cmd中执行:redis-cli.exe -h ip地址 -p 端口 -a 密码,使用

    redis-java

    jedis单机使用 Jedis是Java连接Redis的驱动包,具备操作Redis的所有API,而且使用简单。引入jar包: jedis-2.7.2.jarcommons-pool2-2.3.jar 运行代码 String uri = "redis://x:root@127.0.0.1:6379/0"; URI u = new URI(uri);//资源对象 Jedis jedis = new Jedis(u); for(int i=0;i<100;i++) { jedis.set("b"+i, i+""); } jedis.close(); SharedJedis客户端分片集群 多redis实例启动 拷贝redis.windows.conf,修改新的端口为6380,启动第二个redis实例。 spring整合配置 redis.properties redis.pool.maxTotal=100 redis.pool.maxIdle=10 redis.pool.minIdle=5 redis.pool.lifo=false redis.pool.maxWait=1000 redis.uri.0=redis://bb:root@127.0.0.1:6379/0 redis.uri.1=redis://a22:root@127.0.0.1:6380/0 redis-config.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true"></context:property-placeholder> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="minIdle" value="${redis.pool.minIdel}"></property> <property name="maxIdle" value="${redis.pool.maxIdel}"></property> <property name="maxTotal" value="${redis.pool.maxTotal}"></property> </bean> <bean id="jedisPool" class="redis.clients.jedis.ShardedJedisPool"> <constructor-arg index="0" ref="poolConfig"></constructor-arg> <constructor-arg index="1"> <list> <bean class="redis.clients.jedis.JedisShardInfo"> <constructor-arg index="0" value="${redis.url1}"></constructor-arg> </bean> <bean class="redis.clients.jedis.JedisShardInfo"> <constructor-arg index="0" value="${redis.url2}"></constructor-arg> </bean> </list> </constructor-arg> </bean> </beans> 简单应用详见:

    ssm+log+redis登录demo

    redis的线程模型

    参考地址:Redis 和 Memcached 有什么区别?Redis 的线程模型是什么?为什么单线程的 Redis 比多线程的 Memcached 效率要高得多?

    redis 内部使用文件事件处理器 file event handler,这个文件事件处理器是单线程的,所以 redis 才叫做单线程的模型。它采用 IO 多路复用机制同时监听多个 socket,根据 socket 上的事件来选择对应的事件处理器进行处理。

    文件事件处理器的结构包含 4 个部分:

    多个 socketIO 多路复用程序文件事件分派器事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)

    多个 socket 可能会并发产生不同的操作,每个操作对应不同的文件事件,但是 IO 多路复用程序会监听多个 socket,会将 socket 产生的事件放入队列中排队,事件分派器每次从队列中取出一个事件,把该事件交给对应的事件处理器进行处理。

    redis 常见数据结构以及使用场景分析

    String

    常用命令: set/get/getset/incr/decr/incrby/decrby/setnx/setex等。

    String数据结构是简单的key-value类型,value其实不仅可以是String,也可以是数字。 常规key-value缓存应用(验证码,临时的会话信息); 常规计数:微博数,粉丝数等。

    Hash

    常用命令: hset/hget/hmset/hexists/hdel/hgetAll/hkeys/hvals/hlen 等。

    hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值。 比如我们可以 hash 数据结构来存储用户信息,商品信息等等。

    List

    常用命令: lpush/lpop/lrange/llen/lindex等

    list 就是链表,Redis list 的应用场景非常多,也是Redis最重要的数据结构之一,比如微博的关注列表,粉丝列表,消息列表等功能都可以用Redis的 list 结构来实现。

    Redis list 的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。

    另外可以通过 lrange 命令,就是从某个元素开始读取多少个元素,可以基于 list 实现分页查询,这个很棒的一个功能,基于 redis 实现简单的高性能分页,可以做类似微博那种下拉不断分页的东西(一页一页的往下走),性能高。

    Set

    常用命令: sadd/spop/smembers/sdiff/sunion/sinter 等

    set 对外提供的功能与list类似是一个列表的功能,特殊之处在于 set 是可以自动排重的。

    当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。可以基于 set 轻易实现交集、并集、差集的操作。

    比如:在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。这个过程也就是求交集的过程。

    Sorted Set

    常用命令: zadd,zrange,zrank,zscore等

    和set相比,sorted set增加了一个权重参数score,使得集合中的元素能够按score进行有序排列。

    举例: 在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息,适合使用 Redis 中的 Sorted Set 结构进行存储。

    redis 设置过期时间

    Redis中有个设置时间过期的功能,即对存储在 redis 数据库中的值可以设置一个过期时间。作为一个缓存数据库,这是非常实用的。如我们一般项目中的 token 或者一些登录信息,尤其是短信验证码都是有时间限制的,按照传统的数据库处理方式,一般都是自己判断过期,这样无疑会严重影响项目性能。

    我们 set key 的时候,都可以给一个 expire time,就是过期时间,通过过期时间我们可以指定这个 key 可以存活的时间。

    如果假设你设置了一批 key 只能存活1个小时,那么接下来1小时后,redis是怎么对这批key进行删除的?

    定期删除+惰性删除。

    定期删除:redis默认是每隔 100ms 就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。注意这里是随机抽取的。为什么要随机呢?你想一想假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很大的负载!

    惰性删除 :定期删除可能会导致很多过期 key 到了时间并没有被删除掉。所以就有了惰性删除。假如你的过期 key,靠定期删除没有被删除掉,还停留在内存里,除非你的系统去查一下那个 key,才会被redis给删除掉。这就是所谓的惰性删除,也是够懒的哈!

    但是仅仅通过设置过期时间还是有问题的。我们想一下:如果定期删除漏掉了很多过期 key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?如果大量过期key堆积在内存里,导致redis内存块耗尽了。怎么解决这个问题呢? redis 内存淘汰机制。

    redis 内存淘汰机制(MySQL里有2000w数据,Redis中只存20w的数据,如何保证Redis中的数据都是热点数据?)

    redis 提供 6种数据淘汰策略: volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧! 4.0版本后增加以下两种: volatile-lfu:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰allkeys-lfu:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的key

    redis 持久化机制(怎么保证 redis 挂掉之后再重启数据可以进行恢复)

    很多时候我们需要持久化数据也就是将内存中的数据写入到硬盘里面,大部分原因是为了之后重用数据(比如重启机器、机器故障之后恢复数据),或者是为了防止系统故障而将数据备份到一个远程位置。

    Redis不同于Memcached的很重一点就是,Redis支持持久化,而且支持两种不同的持久化操作。Redis的一种持久化方式叫快照(snapshotting,RDB),另一种方式是只追加文件(append-only file,AOF)。这两种方法各有千秋,下面我会详细这两种持久化方法是什么,怎么用,如何选择适合自己的持久化方法。

    快照(snapshotting)持久化(RDB)

    Redis可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。Redis创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis主从结构,主要用来提高Redis性能),还可以将快照留在原地以便重启服务器的时候使用。

    快照持久化是Redis默认采用的持久化方式,在redis.conf配置文件中默认有此下配置:

    save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。 save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。 save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。

    AOF(append-only file)持久化

    与快照持久化相比,AOF持久化 的实时性更好,因此已成为主流的持久化方案。默认情况下Redis没有开启AOF(append only file)方式的持久化,可以通过appendonly参数开启:

    appendonly yes

    开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof。

    在Redis的配置文件中存在三种不同的 AOF 持久化方式,它们分别是:

    appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度 appendfsync everysec #每秒钟同步一次,显示地将多个写命令同步到硬盘 appendfsync no #让操作系统决定何时进行同步 为了兼顾数据和写入性能,用户可以考虑 appendfsync everysec选项 ,让Redis每秒同步一次AOF文件,Redis性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。当硬盘忙于执行写入操作的时候,Redis还会优雅的放慢自己的速度以便适应硬盘的最大写入速度。

    redis 事务

    Redis 通过 MULTI、EXEC、WATCH 等命令来实现事务(transaction)功能。事务提供了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求,它会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求。

    在传统的关系式数据库中,常常用 ACID 性质来检验事务功能的可靠性和安全性。在 Redis 中,事务总是具有原子性(Atomicity)、一致性(Consistency)和隔离性(Isolation),并且当 Redis 运行在某种特定的持久化模式下时,事务也具有持久性(Durability)。

    缓存雪崩和缓存穿透问题解决方案

    缓存雪崩

    什么是缓存雪崩?

    简介:缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。

    有哪些解决办法?

    事前:尽量保证整个 redis 集群的高可用性,发现机器宕机尽快补上。选择合适的内存淘汰策略。事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL崩掉事后:利用 redis 持久化机制保存的数据尽快恢复缓存

    缓存穿透

    什么是缓存穿透?

    缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。举个例子:某个黑客故意制造我们缓存中不存在的 key 发起大量请求,导致大量请求落到数据库。

    一般MySQL 默认的最大连接数在 150 左右,这个可以通过 show variables like '%max_connections%';命令来查看。最大连接数一个还只是一个指标,cpu,内存,磁盘,网络等无力条件都是其运行指标,这些指标都会限制其并发能力!所以,一般 3000 个并发请求就能打死大部分数据库了。

    有哪些解决办法?

    最基本的就是首先做好参数校验,一些不合法的参数请求直接抛出异常信息返回给客户端。比如查询的数据库 id 不能小于 0、传入的邮箱格式不对的时候直接返回错误消息给客户端等等。

    缓存无效key布隆过滤器

    不了解布隆过滤器?一文给你整的明明白白!

    如何保证缓存与数据库双写时的数据一致性?

    如何保证缓存与数据库的双写一致性?

    Processed: 0.012, SQL: 8