当我们网站的数据量过大时,使用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.clients</groupId><artifactId>jedis</artifactId><version>3.3.0</version></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-placeholder location="classpath:redis.properties"/>

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

 <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"><!--最大连接数--><property name="maxTotal" value="${redis.pool.maxActive}" /><!--最多空闲连接数--><property name="maxIdle" value="${redis.pool.maxIdle}" /><!--最大等待时间--><property name="maxWaitMillis" value="${redis.pool.maxWait}" /><!--检查连接有效性--><property name="testOnBorrow" value="${redis.pool.testOnBorrow}" /></bean>

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

 <bean id="jedisPool" class="redis.clients.jedis.JedisPool"><constructor-arg name="poolConfig" ref="jedisPoolConfig"/><constructor-arg name="host" value="${redis.hostname}"/><constructor-arg name="port" value="${redis.port}" type="int"/><constructor-arg name="timeout" value="${redis.timeout}" type="int"/><constructor-arg name="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;public class JedisUtil {private JedisPool jedisPool;public void setJedisPool(JedisPool jedisPool) {this.jedisPool = jedisPool;}public JedisPool getJedisPool() {return jedisPool;}//获取一个jedis连接public Jedis getJedis() {return jedisPool.getResource();}//键public class Keys {public boolean exists(String key) {     //查询键是否存在Jedis jedis = getJedis();boolean exis = jedis.exists(key);jedis.close();return exis;}//清除键值对public long del(String... keys) {Jedis jedis = getJedis();long count = jedis.del(keys);jedis.close();return count;}}//字符串类型public class Strings {public String get(String key) {     //根据键获取值Jedis jedis = getJedis();String value = jedis.get(key);jedis.close();return value;}public long setnx(String key, String value) {   //设置键值对Jedis jedis = getJedis();long num = jedis.setnx(key, value);jedis.close();return num;}}
}

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

    <bean id="jedisUtil" class="com.tory.shop.cache.JedisUtil"><property name="jedisPool" ref="jedisPool"/></bean><bean id="jedisKeys" class="com.tory.shop.cache.JedisUtil$Keys"><constructor-arg ref="jedisUtil"></constructor-arg></bean><bean id="jedisStrings" class="com.tory.shop.cache.JedisUtil$Strings"><constructor-arg ref="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;@Service
public class AreaServiceImpl implements AreaService {@Autowiredprivate AreaDao areaDao;@Autowiredprivate JedisUtil.Strings jedisStrings;@Autowiredprivate JedisUtil.Keys jedisKeys;public List<Area> getAreaList() {ObjectMapper objectMapper = new ObjectMapper();List<Area> areaList = new ArrayList<>();//若不存在则从数据库中查询并保存到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 Void addArea(Area area) {//添加店铺信息int affectedRows = areaDao.insertArea(area);if (affectedRows <= 0)throw new RuntimeException("插入数据库失败!");//删除Redis中缓存jedisKeys.del("arealist");}
}

使用Redis进行数据库缓存相关推荐

  1. Redis与数据库缓存一致性问题

    一.Redis 数据一致性问题产生的原因 对 Redis和数据库的操作有 2 种方案: 1.先操作(删除) Redis,再操作数据库 2.先操作数据库,再操作(删除) Redis 上述二种方案,都希望 ...

  2. redis做数据库缓存

    只读缓存 使用只读缓存时,是先把修改写到后端数据库中,再把缓存中的数据删除.当下次访问这个数据时,会以后端数据库中的值为准,重新加载到缓存中. 这样做的优点是,数据库和缓存可以保证完全一致,并且缓存中 ...

  3. 系统架构设计师考试题库重点案例:分布式数据库缓存设计

    某企业是为城市高端用户提供高品质蔬菜生鲜服务的初创企业,创业初期为快速开展业务,该企业采用轻量型的开发架构(脚本语言+关系型数据库)研制了一套业务系统.业务开展后受到用户普遍欢迎,用户数和业务数量迅速 ...

  4. java缓存_使用Redis和Java进行数据库缓存

    数据库缓存是处理这些性能问题的最常见策略之一.缓存涉及将数据库查询的结果保存在更快,更容易访问的位置.正确完成后,缓存将减少查询响应时间,减少数据库负载并降低成本. 但是,缓存也需要小心处理,因为它们 ...

  5. 构建高性能数据库缓存之redis主从复制

    一.什么是redis主从复制? 主从复制,当用户往Master端写入数据时,通过Redis Sync机制将数据文件发送至Slave,Slave也会执行相同的操作确保数据一致:且实现Redis的主从复制 ...

  6. 数据库缓存服务—Redis配置与优化

    文章目录 一.缓存概念 1.1 系统缓存 1.2 缓存保存位置及分层结构 1.2.1 DNS缓存 1.2.2 应用层缓存 1.2.3 数据层缓存 1.2.4 硬件缓存 二.关系型数据库与非关系型数据库 ...

  7. 数据库缓存服务——NoSQL之Redis配置与优化

    一.缓存概念 缓存是为了调节速度不一致的两个或多个不同的物质的速度,在中间对速度较慢的一方起到加速作用,比如CPU的一级.二级缓存是保存了CPU最近经常访问的数据,内存是保存CPU经常访问硬盘的数据, ...

  8. redis做mysql缓存的优点_面试官:如何保障数据库和redis缓存的一致性

    随着互联网的高速发展,使用互联网产品的人也越来越多,团队不可避免得也会面对越来越复杂的高并发业务场景(如下图),比如热点视频/文章的观看(读场景),热点视频/文章的评论,点赞等(写场景). 众所周知, ...

  9. 分布式数据库缓存的基本概念?MemCache和redis的详细比较?

    分布式数据库缓存指的是在高并发环境下,为了减轻数据库压力和提高系统响应时间,在数据库系统和应用系统之间增加的独立缓存系统. 目前市场上常见的数据库缓存系统是MemChace和Redis,他们的主要区别 ...

最新文章

  1. PLM的关键点—实施篇
  2. e.target与e.currentTarget的作用
  3. Redis入门教程(二)
  4. autojs怎么post协议_超9成人都理解错了HTTP中GET与POST的区别
  5. JavaScript —— this、闭包、原型、异步
  6. Package require os(darwin) not compatible with your platform(win32)
  7. html聚光灯特效,css实现聚光灯效果的代码分享
  8. spring boot整合shiro继承redis_spring-boot-plus集成Shiro+JWT权限管理
  9. Spring Cloud 分布式 微服务 最佳实践之一
  10. Mybatis foreach 批量插入
  11. Discuz常见小问题-如何为每个板块设置不同的图标
  12. 第1章 别让医生欺负你
  13. mount、umount 挂载卸载命令
  14. ArcMAP栅格数据裁剪小技巧
  15. 书城项目 软件可行性分析报告
  16. 隐枚举法求解0-1整数规划
  17. Compose Modifier
  18. 全网最简单的方法QQ透明头像设置方法(小白教程)几分钟搞定
  19. 【Word】下载的word文档(doc格式)编辑后出现(同文件名.files)的文件夹--解决办法
  20. 【密码学】HMAC与HS256算法

热门文章

  1. CentOS7如何进入单用户模式?
  2. 华为hcia网络课程
  3. java实现手动派单,一种无分区外卖派单系统的回程单派单方法与流程
  4. 哪些cmd命令可以修复Windows
  5. python+pyGame 黑白棋游戏
  6. 手把手写C++服务器(31):服务器性能提升关键——IO复用技术【两万字长文】
  7. 上海小学生为表达友谊赠送同学百元钞票
  8. 学会preload和prefetch
  9. 如何实现精致扫雷游戏(可扩散可标记)---保姆级教程
  10. 24.WEB安全基础环境搭建 WIN7物理机的环境搭建