热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

使用Redis进行数据库缓存

当我们网站的数据量过大时,使用Java频繁访问数据库会造成延迟过大、数据丢失等问题,这时候就需要使用缓存技术将经常访问的数据保存在缓存数据库以减少数据库访问。我们经常使用Red

当我们网站的数据量过大时,使用Java频繁访问数据库会造成延迟过大、数据丢失等问题,这时候就需要使用缓存技术将经常访问的数据保存在缓存数据库以减少数据库访问。我们经常使用Redis作为缓存数据库。

当客户端在申请数据时会优先发送请求到Redis,如果其中存在数据则直接返回,否则Redis向数据库发送请求。数据库查询到结果后将直接返回给客户端,同时将数据更新到Redis存储中。当数据库中的数据发生变化时,Redis对清空相应地键值对,当下次请求到达时就回去数据库中查询相应的数据并更新到Redis,这样就保证了Redis和数据库数据的一致性。

1、Redis

Redis 是速度非常快的非关系型(NoSQL)内存键值数据库,可以存储键和五种不同类型的值之间的映射。键的类型只能为字符串,值支持五种数据类型:字符串(String)、列表(List)、集合(Set)、散列表(Hash)、有序集合(Zset)。Redis的操作都是原子型的,所以不需要考虑并发事务管理问题。

关于Redis的安装配置在之前一篇文章中有记录:https://blog.csdn.net/theVicTory/article/details/107121264#t5

可以通过桌面工具Another Redis Desktop Manager对Redis进行远程管理,这是一款免费且好用的可视化工具
在这里插入图片描述

1.1、持久化策略

RDB策略

Redis Database快照方式是Redis默认的持久化方式,它使用fork函数复制出一份当前进程的副本(子进程);父进程继续处理客户端指令。子进程将内存的数据写入硬盘中的临时文件,写入完成后替换之前的旧文件。优点:1. 多进程处理,性能最快。缺点:1. 若子进程数据未写完而redis异常退出,就会丢失最后一次快照以后更改的所有数据; 2. 数据集比较大的时候,fork比较耗时,造成服务器在一段时间内停止处理客户端的请求。

它会在如下四个场景中被触发

  1. 自动保存规则:按照redis.conf文件中设置的快照保存规则进行持久化。如下所示,save 900 1代表900秒内如果发生一次key值的变化就进行持久化,同理save 300 10为300秒内发生10次key值变化就持久化
    在这里插入图片描述
  2. 执行flushall清除内存时会依据自动保存规则进行持久化
  3. 执行复制的时候
  4. 手动命令
    save:将内存的数据同步到磁盘中,这个操作会阻塞客户端的请求(比较耗时)。
    bgsave:在后台异步执行快照操作,这个操作不会阻塞客户端的请求。

redis默认的储存路径为./,即将持久化文件储存在当前执行目录下的dump.rdb文件,如果需要自定义储存路径,可以修改redis.conf中的dir为目标绝对路径
在这里插入图片描述

AOF策略

AOF(Append Only File)持久化会在执行redis命令的时候,把命令写入到.aof文件中,通过保存被执行的写命令来记录数据库状态。

AOF需要手动开启,在redis.conf中将appendonly默认值no改为yes。

随着时间.aof文件会变得越来越大,因此在其超过一定限制时后台会对它进行重写。即创建一个新的AOF文件来替代现有的AOF文件,新旧两个文件所保存的数据库状态是相同的,但是新的AOF文件命令更加简洁,通常体积会较旧AOF文件小很多。重写过程发生了停机,现有的.aof文件也不会丢失,所以它是绝对安全的。

在redis.conf文件中配置重写规则,如下所示,第一行auto-aof-rewrite-percentage 100代表新文件超过原文件100%后进行重写;第二行auto-aof-rewrite-min-size 64mb代表文件大小超过64MB后进行重写。
在这里插入图片描述

1.2、过期策略

reids是用内存来缓存的,内存资源很宝贵。所以我们 set key value 的时候一般都会给它设置过期时间。

定期删除:假设redis里放了10万个数据,redis默认会每隔100ms随机抽取设置了过期时间的key来删除。这会导致很多过期的key并没有被删除
惰性删除:当获取某个key的时候,redis会检查一下,这个key是否过期了,如果是就直接删除不返回。缺点很明显是过期且未被查询的key不会被删除
内存淘汰:由于上面的两个策略仍然会导致部分key堆积,所以当内存不足以容纳新写入数据时就会根据算法进行内存淘汰。如下所示为redis.conf中可以设置的内存淘汰策略。例如最常用的allkeys-lru是移除最近最少使用的key;volatile-ttl在设置了过期时间的键中,将更早过期时间的key优先移除。
在这里插入图片描述

2、使用Jedis操作redis

2.1、配置连接

Jedis是Redis官方推荐的Java连接开发工具,使用maven引入该依赖

<dependency><groupId>redis.clientsgroupId><artifactId>jedisartifactId><version>3.3.0version>dependency>

接着和jdbc.properties一样设置redis连接配置文件redis.properties,其位置在src\main\resources\redis.properties

redis.hostname=127.0.0.1
redis.port=6379
redis.password=1234
redis.timeout=30000
redis.database=0
redis.pool.maxActive=600
redis.pool.maxIdle=300
redis.pool.maxWait=3000
redis.pool.testOnBorrow=true

创建redis配置文件src\main\resources\spring\spring-redis.xml,首先在文件中引入配置文件中redis.properties

<context:property-placeholderlocation="classpath:redis.properties"/>

接着创建redis设置对象JedisPoolConfig,在其中通过${}读取redis.properties中的变量对连接数、等待时间等进行设置

<beanid="jedisPoolConfig"class="redis.clients.jedis.JedisPoolConfig"><propertyname="maxTotal"value="${redis.pool.maxActive}"/><propertyname="maxIdle"value="${redis.pool.maxIdle}"/><propertyname="maxWaitMillis"value="${redis.pool.maxWait}"/><propertyname="testOnBorrow"value="${redis.pool.testOnBorrow}"/>bean>

接着创建Redis连接池对象jedisPool,即传入连接池的配置对象、主机、端口号去初始化连接池对象

<beanid="jedisPool"class="redis.clients.jedis.JedisPool"><constructor-argname="poolConfig"ref="jedisPoolConfig"/><constructor-argname="host"value="${redis.hostname}"/><constructor-argname="port"value="${redis.port}"type="int"/><constructor-argname="timeout"value="${redis.timeout}"type="int"/><constructor-argname="password"value="${redis.password}"/>bean>

2.2、编写工具类

通过工具类JedisUtil完成对Redis数据库的实际操作

首先定义JedisPool的set/get方法用于设置、获取连接池jedisPool。

定义内部方法getJedis()用于从连接池获取一个具体的jedis连接对象,需要通过具体的jedis连接对象进行键值对的操作。

接着定义一个类Keys包含键相关的具体操作,这里定义了exists()方法用于判断键是否存在,其通过调用jedis.exists()实现;del()用于删除键值。

定义类Strings对应值为字符串的键值对相关操作,先定义了get()setnx()用于获取、设置字符串类型的键值对。其实现也是通过调用jedis对象的相关方法。

package com.tory.shop.cache;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;publicclassJedisUtil{private JedisPool jedisPool;publicvoidsetJedisPool(JedisPool jedisPool){this.jedisPool= jedisPool;}public JedisPoolgetJedisPool(){return jedisPool;}//获取一个jedis连接public JedisgetJedis(){return jedisPool.getResource();}//键publicclassKeys{publicbooleanexists(String key){//查询键是否存在
            Jedis jedis=getJedis();boolean exis= jedis.exists(key);
            jedis.close();return exis;}//清除键值对publiclongdel(String... keys){
            Jedis jedis=getJedis();long count= jedis.del(keys);
            jedis.close();return count;}}//字符串类型publicclassStrings{public Stringget(String key){//根据键获取值
            Jedis jedis=getJedis();
            String value= jedis.get(key);
            jedis.close();return value;}publiclongsetnx(String key, String value){//设置键值对
            Jedis jedis=getJedis();long num= jedis.setnx(key, value);
            jedis.close();return num;}}}

最后在spring-redis.xml文件中注册jedisUtil类及其子类jedisKeysjedisStrings

<beanid="jedisUtil"class="com.tory.shop.cache.JedisUtil"><propertyname="jedisPool"ref="jedisPool"/>bean><beanid="jedisKeys"class="com.tory.shop.cache.JedisUtil$Keys"><constructor-argref="jedisUtil">constructor-arg>bean><beanid="jedisStrings"class="com.tory.shop.cache.JedisUtil$Strings"><constructor-argref="jedisUtil">constructor-arg>bean>

3、使用Redis进行缓存

如下所示为获取Area相关信息的AreaService,在其中通过getAreaList()获取Area信息数组areaList,由于这是经常访问的信息,我们可以将其用Redis缓存起来。

通过@Autowired自动注入jedisKeys、jedisStrings两个对象。
当查询areaList的请求到达时首先通过jedisKeys判断“arealist”是否存在缓存中。若不存在则查询数据库,将得到的结果序列化为json字符串并通过jedisStrings保存在Redis中;若存在则从redis读取到“arealist”对应的json字符串,并反序列化为areaList对象数组。

当有更新area信息的操作到达时,需要清除Redis的缓存以保证其有效性。例如当执行addArea()操作时,除了调用areaDao完成数据库操作外,还需要通过jedisKeys.del()清除Redis中对应的“arealist”键值缓存。

package com.tory.shop.service.impl;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.JavaType;import com.fasterxml.jackson.databind.ObjectMapper;import com.tory.shop.cache.JedisUtil;import com.tory.shop.dao.AreaDao;import com.tory.shop.entity.Area;import com.tory.shop.service.AreaService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.ArrayList;import java.util.List;@ServicepublicclassAreaServiceImplimplementsAreaService{@Autowiredprivate AreaDao areaDao;@Autowiredprivate JedisUtil.Strings jedisStrings;@Autowiredprivate JedisUtil.Keys jedisKeys;public List<Area>getAreaList(){
        ObjectMapper objectMapper=newObjectMapper();
        List<Area> areaList=newArrayList<>();//若不存在则从数据库中查询并保存到Redis中if(!jedisKeys.exists("arealist")){
            areaList= areaDao.queryArea();
            String jsonStr= null;try{
                jsonStr= objectMapper.writeValueAsString(areaList);//areaList序列化为json字符串}catch(JsonProcessingException e){
                e.printStackTrace();}
            jedisStrings.setnx("arealist", jsonStr);}else{//如果缓存中存在则直接获取
            String jsonStr= jedisStrings.get("arealist");
            JavaType javaType= objectMapper.getTypeFactory().constructParametricType(ArrayList.class, Area.class);//json字符串反序列化为Area数组try{
                areaList= objectMapper.readValue(jsonStr, javaType);}catch(JsonProcessingException e){
                e.printStackTrace();}}return areaList;}public VoidaddArea(Area area){//添加店铺信息int affectedRows= areaDao.insertArea(area);if(affectedRows<=0)thrownewRuntimeException("插入数据库失败!");//删除Redis中缓存
        jedisKeys.del("arealist");}}

推荐阅读
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 本文介绍了PhysioNet网站提供的生理信号处理工具箱WFDB Toolbox for Matlab的安装和使用方法。通过下载并添加到Matlab路径中或直接在Matlab中输入相关内容,即可完成安装。该工具箱提供了一系列函数,可以方便地处理生理信号数据。详细的安装和使用方法可以参考本文内容。 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • 本文介绍了Redis中RDB文件和AOF文件的保存和还原机制。RDB文件用于保存和还原Redis服务器所有数据库中的键值对数据,SAVE命令和BGSAVE命令分别用于阻塞服务器和由子进程执行保存操作。同时执行SAVE命令和BGSAVE命令,以及同时执行两个BGSAVE命令都会产生竞争条件。服务器会保存所有用save选项设置的保存条件,当满足任意一个保存条件时,服务器会自动执行BGSAVE命令。此外,还介绍了RDB文件和AOF文件在操作方面的冲突以及同时执行大量磁盘写入操作的不良影响。 ... [详细]
  • 安装mysqlclient失败解决办法
    本文介绍了在MAC系统中,使用django使用mysql数据库报错的解决办法。通过源码安装mysqlclient或将mysql_config添加到系统环境变量中,可以解决安装mysqlclient失败的问题。同时,还介绍了查看mysql安装路径和使配置文件生效的方法。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • 移动端常用单位——rem的使用方法和注意事项
    本文介绍了移动端常用的单位rem的使用方法和注意事项,包括px、%、em、vw、vh等其他常用单位的比较。同时还介绍了如何通过JS获取视口宽度并动态调整rem的值,以适应不同设备的屏幕大小。此外,还提到了rem目前在移动端的主流地位。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
author-avatar
狼与鹰的爱_340
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有