1. Redis应用场景

  1. 利用 redis 中字符串类型完成 项目中手机验证码存储的实现

    验证码一般都具有时效性,我们在redis中可以设置一个key的超时时间,
    当用户在超时时间之内响应时,会与redis中的数据进行对比,验证验证码的正确性
    当用户在超时时间之外响应,数据在redis中已经被删除,无法进行验证
    
  2. 利用 redis 中字符串类型完成 具有时效性业务功能

    比如说在平常买票或者网上购物的时候, 12306 淘宝
    订单还有15分钟失效,我们可以将订单信息存入redis,
    当数据失效时我们可以返回订单失效。
    
  3. 利用 redis 做分布式集群系统中 Session 共享

  4. 利用 redis 中 zset数据类型 元素 分数 完成排行榜之类的功能

    ZSet是可排序的set,可以利用ZSet做排行榜排名之类的功能,显示排行前20的信息等等
    
  5. 利用 redis 实现分布式缓存

    可以把数据库中的数据放入缓存中,日后在查询的时候不需要走数据库了,而是直接走缓存,
    缓存中没有的从数据库中查,查完放入缓存
    
  6. 利用 redis 存储认证之后的 token 信息 微信小程序 微信公众号 ——> 令牌(token)redis 超时

  7. 利用 redis 解决分布式集群系统中分布式锁的问题

    在一个java虚拟机中,解决锁的方法有很多,比如: synchronized,但是我们要注意synchronized
    只能解决一台机器上的线程共享问题,现在我们有多台机器,多台虚拟机,synchronized并不能保证
    这多台机器之间的线程安全问题。因为 redis 是单进程单线程的,我们可以使用 redis 解决多台
    机器之间的线程安全问题。
    

2. Redis中分布式缓存的实现

# 什么是缓存定义: 就是计算机内存中的一段数据# 内存中的数据特点1. 读写快 2. 断电立即消失# 缓存解决了什么问题1. 提高网站吞吐量,提高网站运行效率(从缓存中取数据就相当于从内存中取数据,读写更快)
2. 核心解决问题: 缓存的存在减轻了数据库的访问压力(如果数据在缓存中有,直接在缓存中取就可以了)# 误区既然缓存可以提高提高效率,那项目中所有数据都加入缓存岂不是更好?
并不是
注意:使用缓存时一定是数据库中数据极少发生修改,更多用于查询这种情况。当修改比较多时,我们每次不仅要在数据库中查询新的数据,还要对缓存中的数据做替换,这种情况下使用缓存会极大地降低系统的效率。# 本地缓存和分布式缓存的区别本地缓存: 存在于应用服务器内存中的数据称之为本地缓存  (local cache)
分布式缓存: 存储在当前应用服务器内存之外的数据称之为分布式缓存 (distribute cache)# 什么是集群,什么是分布式集群: 将同一种服务器的多个节点放在一起共同对系统提供服务的过程称之为集群
分布式: 由多个不同的服务集群共同对系统提供服务,这个系统称之为分布式系统(distribute system)分布式是在集群基础之上构建的# 利用mybatis自身本地缓存结合redis实现分布式缓存a. mybatis中应用级缓存(二级缓存) SqlSessionFactory 级别缓存  所有会话共享b. 如何开启(二级缓存) 只需要在 mapper.xml 中加入下面的一条语句<cache/>在mapper.xml中加入上面那句话开启二级缓存使用的是本地缓存c. 查看Cache标签缓存实现结论: Mybatis底层默认使用的是 org.apache.ibatis.cache.impl.PerpetualCache 实现d. 自定义RedisCache实现1. 通过mybatis默认cache源码得知 可以使用自定义Cache类 实现 Cache接口 并对里面的方法做实现2. 使用RedisCache实现<cache type="com.baizhi.redis.RedisCache"/>e. 当表之间有关联关系时,我们可以让有关联关系查询之间的DAO的查询指向同一个缓存

2.1 mybatis的二级缓存

mybatis中的二级缓存是本地缓存,二级缓存是SqlSessionFactory级别的,所有会话共享

如果想要使用mybatis的二级缓存,只需要在 mapper配置文件中加入 下面这条语句 即可

<cache/>
  • 在使用mybatis的二级缓存往缓存中存储对象时,对象也必须实现序列化,这和redis是一样的

包结构

  1. 引入依赖
<!--引入redis相关的依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--mybatis-->
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.3</version>
</dependency>
<!--mysql-->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.38</version>
</dependency>
<!--druid-->
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.19</version>
</dependency>
  1. 编写 application.properties 配置文件

application.properties 配置文件

# 修改SpringBoot应用的端口号
server.port=8989# 配置与redis相关的数据
spring.redis.host=192.168.72.129
spring.redis.port=7000
spring.redis.database=0# 数据源
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/2001?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root# 指定mapper配置文件的位置以及起别名
mybatis.mapper-locations=classpath:com/baizhi/mapper/*.xml
mybatis.type-aliases-package=com.baizhi.entity# 设置日志的级别
logging.level.com.baizhi.dao=debug

在入口类上加上 @MapperScan(“com.baizhi.dao”) 注解扫描dao接口所在的包

// 入口类
@SpringBootApplication
@MapperScan("com.baizhi.dao")   // 在入口类上加上这个注解让它扫描dao接口所在的包
public class RedisDay2Application {public static void main(String[] args) {SpringApplication.run(RedisDay2Application.class, args);}}
  1. 相关类

UserDAO

public interface UserDAO {List<User> findAll();
}

User

// 下面这两个注解的作用是创建get、set方法,使用了下面的这两个注解就不需要写get、set方法了,会自动创建
@Data
@Accessors(chain = true)
public class User implements Serializable { // 将对象放入缓存是对象必须序列化private String id;private String name;private Integer age;private Date bir;
}

UserService

public interface UserService {List<User> findAll();
}

UserServiceImpl

@Service
@Transactional
public class UserServiceImpl implements UserService { @Autowiredprivate UserDAO userDAO;@Overridepublic List<User> findAll() {return userDAO.findAll();}
}

RedisDay2Application入口类

@SpringBootApplication
@MapperScan("com.baizhi.dao")   // 扫描dao接口所在的包
public class RedisDay2Application {public static void main(String[] args) {SpringApplication.run(RedisDay2Application.class, args);}}

UserDAOMapper.xml配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.baizhi.dao.UserDAO"><!--开启mybatis中二级缓存--><cache/><!--findAll--><select id="findAll" resultType="com.baizhi.entity.User">select id, name, age, bir from t_user</select>
</mapper>
  1. 测试类
// 下面两个注解相当于启动SpringBoot应用
@SpringBootTest(classes = RedisDay2Application.class)   // 让SpringBoot启动起来,并指向入口类
@RunWith(SpringRunner.class)        // 让测试运行于Spring环境public class TestUserService {@Autowiredprivate UserService userService;@Testpublic void test(){userService.findAll().forEach(user -> System.out.println("user = " + user));System.out.println("============================");userService.findAll().forEach(user -> System.out.println("user = " + user));}
}

结果

我们可以看到在第二次查询的时候击中了缓存,并没有向数据库中查询数据,而是直接击中缓存,从缓存中拿取数据。

补充

mybatis中的二级缓存是本地缓存,使用了应用的内存,这是它的不足之处,且当应用重启之后,缓存中的数据会丢失,我们需要先从数据库中读取,之后才能使用缓存,我们想要让应用中的内存全部用于处理响应,这就需要用到下面的分布式缓存了。

2.2 mybatis和redis实现分布式缓存

我们在mapper配置文件中加入

 <cache/>

就可以使用 mybatis 的二级缓存,实际上使用的是PerpetualCache,和下面这句话是一个意思

 <cache type="org.apache.ibatis.cache.impl.PerpetualCache"/>

mybatis中的二级缓存默认使用的是PerpetualCache,底层实际上是java中的HashMap

我们怎么把mybatis中的二级缓存改为redis呢,在做自定义 Cathe 的时候自定义一个类像 PerpetualCache 内部一样

简单的实现并且实现 Cache 接口并且在mapper配置文件中指定使用这个我们自定义的RedisCache即可。不过在这

之前我们需要先想一想如何获取redisTemplate对象,在操作RedisCache的时候我们肯定需要用到RedisTemplate对

象,而RedisCache是由mybatis管理的,并不是由工厂管理的,那我们怎么拿到RedisCache对象呢,我们可以创建

一个工厂工具类,通过工具类传入指定对象的名字去获取我们想要的在工厂中的对象(比如: redisTemplate)

如果想要获取工厂并获取工厂中的对象可以建一个工具类

// 用来获取SpringBoot创建好的工厂
@Component
public class ApplicationContextUtils implements ApplicationContextAware {// 保留下来工厂private static ApplicationContext applicationContext;// 将创建好的工厂以参数的形式传递给这个类@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}// 提供在工厂中获取对象的方法  RedisTemplate  redisTemplatepublic static Object getBeanName(String name){return applicationContext.getBean(name);}
}

接下来我们来实现自定义的RedisCache

因为在使用redis整合mybatis时,在使用hash类型时,value中的key太长影响效率,所以我们需要使用md5算法修改value中的key为一个32位的16进制的数

// 自定义redis缓存实现
public class RedisCache implements Cache {// 当前放入缓存的mapper的namespace// 大Keyprivate final String id;// 必须存在一个类型为String的id的构造方法public RedisCache(String id) {//System.out.println("id ==============> " + id);this.id = id;}// 返回cache唯一标识不能为空@Overridepublic String getId() {return id;}// 向缓存中放入数据@Override// 大Key  value中的keypublic void putObject(Object o, Object o1) {//System.out.println("o = " + o.toString());//System.out.println("o1 = " + o1);// 使用redis 中 Hash类型作为缓存存储模型 Key hashkey value// 大Key是id  value中的key是o1  value中的值是o1getRedisTemplate().opsForHash().put(id.toString(), getKeyToMD5(o.toString()), o1);// 针对不同的业务模块设置不同的缓存时间//if(id.equals("com.baizhi.dao.UserDAO")){//    getRedisTemplate().expire(id.toString(), 60,  TimeUnit.MILLISECONDS);//}//if(id.equals("com.baizhi.dao.EmpDAO")){getRedisTemplate().expire(id.toString(), 30,  TimeUnit.MILLISECONDS);//}}//从缓存中获取数据@Override// 大Keypublic Object getObject(Object o) {//System.out.println("o = " + o.toString());return getRedisTemplate().opsForHash().get(id.toString(), getKeyToMD5(o.toString()));}// 注意: 这个方法为mybatis的保留方法 默认没有实现 后续版本可能会实现@Override// 大Keypublic Object removeObject(Object o) {//System.out.println("根据指定key删除缓存");return null;}// 只要调用增、删、该,都会调用clear方法清空缓存@Overridepublic void clear() {// 清空namespacegetRedisTemplate().delete(id.toString()); // 清空缓存}// 用来计算缓存数量@Overridepublic int getSize() {// 获取Hash中key value数量return getRedisTemplate().opsForHash().size(id.toString()).intValue();}// 封装redisTemplatepublic RedisTemplate getRedisTemplate(){// 根据key从redis的hash类型中获取数据// 通过application工具类获取RedisTemplate对象RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBeanName("redisTemplate");redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer());return redisTemplate;}// 封装一个对value中的key进行md5操作的方法private String getKeyToMD5(String key){return DigestUtils.md5DigestAsHex(key.getBytes());}
}

在实现自定义RedisCache时我们必须满足以下几点要求:

  1. 实现一个String类型的id的构造方法
  2. 返回唯一标识id不能为空
  3. 除了 removeObject 方法以外,其他方法都要做相应的实现

如果我们想要使用自定义的RedisCache的话,我们还需要在mapper配置文件中指定使用这个RedisCache

<!--使用自定义的RedisCache-->
<cache type="com.baizhi.redis.RedisCache"/>

还有几点需要注意:

  • 存入redis中的数据是Hash类型,大Key是mapper配置文件中namespace中的名字,value中的key是大Key和查询语句等组合成的字符串,不过我们使用了md5技术,所以value中的key是一个32位的16进制的字符串

  • 在进行增、删、改操作的时候会清空缓存

  • 关于md5的小知识:

    用两个文件,一个是 aa.txt 一个是 bb.txt 怎么判断两个文件的内容是一致的呢,可以使用MD5算法,不同的内容通过MD5算法得到的结果一定不同。

另外:

如果我们使用的不同DAO的操作之间的表没有关联关系时,我们在使用缓存时使它们各自是用自己的缓存,比如现在有两个mapper配置文件,一个是User、一个是Emp,当这两个DAO对应的表没有关系时,mapper配置文件中缓存这样写:

User的Mapper

<!--开启mybatis中二级缓存-->
<cache type="com.baizhi.redis.RedisCache"/>

Emp的Mapper

<!--开启mybatis中二级缓存-->
<cache type="com.baizhi.redis.RedisCache"/>

当表之间有关联关系时:

User的Mapper

<!--开启mybatis中二级缓存-->
<cache type="com.baizhi.redis.RedisCache"/>

Emp的Mapper

<!--关联关系缓存处理--> <!--引用另一方的缓存-->
<cache-ref namespace="com.baizhi.dao.UserDAO"/>

当然也可以两个都使用Emp为缓存的key,只要两个区域共享即可

接下来我们写一个小案例来看一下Redis缓存在项目中的应用:

包结构:

  1. 引入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional>
</dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
<!--引入redis相关的依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--mybatis-->
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.3</version>
</dependency>
<!--mysql-->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.38</version>
</dependency>
<!--druid-->
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.19</version>
</dependency>
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><scope>test</scope>
</dependency>
  1. application.properties 配置文件
# 修改SpringBoot应用的端口号
server.port=8989# 配置与redis相关的数据
spring.redis.host=192.168.72.129
spring.redis.port=7000
spring.redis.database=0# 数据源
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/2001?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root# 指定mapper配置文件的位置以及起别名
mybatis.mapper-locations=classpath:com/baizhi/mapper/*.xml
mybatis.type-aliases-package=com.baizhi.entity# 设置日志的级别
logging.level.com.baizhi.dao=debug
  1. 相关类

DAO:

public interface EmpDAO {List<Emp> findAll();
}
public interface UserDAO {List<User> findAll();User findById(String id);void delete(String id);void save(User user);void update(User user);
}

资源类(放入缓存中的对象必须实现序列化):

@Data
@Accessors(chain = true)
public class Emp implements Serializable {private String id;private String name;
}
// 下面这两个注解的作用是创建get、set方法,使用了下面的这两个注解就不需要写get、set方法了,会自动创建
@Data
@Accessors(chain = true)
public class User implements Serializable { // 在使用RedisTemplate操作redis的时候对象必须序列化private String id;private String name;private Integer age;private Date bir;
}

service:

public interface EmpService {List<Emp> findAll();
}
@Service
@Transactional
public class EmpServiceImpl implements EmpService{@Autowiredprivate EmpDAO empDAO;@Overridepublic List<Emp> findAll() {return empDAO.findAll();}
}
public interface UserService {List<User> findAll();User findById(String id);void delete(String id);void save(User user);void update(User user);
}
@Service
@Transactional
public class UserServiceImpl implements UserService {@Autowiredprivate UserDAO userDAO;@Overridepublic List<User> findAll() {return userDAO.findAll();}@Overridepublic User findById(String id) {return userDAO.findById(id);}@Overridepublic void delete(String id) {userDAO.delete(id);}@Overridepublic void save(User user) {user.setId(UUID.randomUUID().toString());userDAO.save(user);}@Overridepublic void update(User user) {userDAO.update(user);}
}

入口类:

@SpringBootApplication
@MapperScan("com.baizhi.dao")   // 扫描dao接口所在的包
public class RedisDay2Application {public static void main(String[] args) {SpringApplication.run(RedisDay2Application.class, args);}}

ApplicationContextUtils工具类(获取工厂中的对象):

// 用来获取SpringBoot创建好的工厂
@Component
public class ApplicationContextUtils implements ApplicationContextAware {// 保留下来工厂private static ApplicationContext applicationContext;// 将创建好的工厂以参数的形式传递给这个类@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}// 提供在工厂中获取对象的方法  RedisTemplate  redisTemplatepublic static Object getBeanName(String name){return applicationContext.getBean(name);}
}

自定义的RedisCache类:

// 自定义redis缓存实现
public class RedisCache implements Cache {// 当前放入缓存的mapper的namespace// 大Keyprivate final String id;// 必须存在一个类型为String的id的构造方法public RedisCache(String id) {System.out.println("id ==============> " + id);this.id = id;}// 返回cache唯一标识不能为空@Overridepublic String getId() {return id;}// 向缓存中放入数据@Override// value中的Key  value中的值public void putObject(Object o, Object o1) {//System.out.println("o = " + o.toString());//System.out.println("o1 = " + o1);// 使用redis 中 Hash类型作为缓存存储模型 Key hashkey value// 大Key是id  value中的key是o1  value中的值是o1getRedisTemplate().opsForHash().put(id.toString(), getKeyToMD5(o.toString()), o1);// 针对不同的业务模块设置不同的缓存时间//if(id.equals("com.baizhi.dao.UserDAO")){//    getRedisTemplate().expire(id.toString(), 60,  TimeUnit.MILLISECONDS);//}//if(id.equals("com.baizhi.dao.EmpDAO")){getRedisTemplate().expire(id.toString(), 30,  TimeUnit.MILLISECONDS);//}}//从缓存中获取数据@Override// 大Keypublic Object getObject(Object o) {//System.out.println("o = " + o.toString());return getRedisTemplate().opsForHash().get(id.toString(), getKeyToMD5(o.toString()));}// 注意: 这个方法为mybatis的保留方法 默认没有实现 后续版本可能会实现@Override// 大Keypublic Object removeObject(Object o) {//System.out.println("根据指定key删除缓存");return null;}// 只要调用增、删、该,都会调用clear方法清空缓存@Overridepublic void clear() {// 清空namespacegetRedisTemplate().delete(id.toString()); // 清空缓存}// 用来计算缓存数量@Overridepublic int getSize() {// 获取Hash中key value数量return getRedisTemplate().opsForHash().size(id.toString()).intValue();}// 封装redisTemplatepublic RedisTemplate getRedisTemplate(){// 根据key从redis的hash类型中获取数据// 通过application工具类获取RedisTemplate对象RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBeanName("redisTemplate");redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer());return redisTemplate;}// 封装一个对value中的key进行md5操作的方法private String getKeyToMD5(String key){return DigestUtils.md5DigestAsHex(key.getBytes());}
}

Mapper配置文件:

EmpDAOMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.baizhi.dao.EmpDAO"><!--使用自定义的RedisCache,如果DAO之间的操作表之间无关系使用这个--><!--<cache type="com.baizhi.redis.RedisCache"/>--><!--关联关系缓存处理时使用这个--> <!--引用另一方的缓存--><cache-ref namespace="com.baizhi.dao.UserDAO"/><!--findAll--><select id="findAll" resultType="Emp">select id, name from t_emp</select></mapper>

UserDAOMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.baizhi.dao.UserDAO"><!--开启mybatis中二级缓存--><cache type="com.baizhi.redis.RedisCache"/><!--findAll--><select id="findAll" resultType="com.baizhi.entity.User">select id, name, age, bir from t_user</select><!--findById--><select id="findById" parameterType="String" resultType="User">select id, name, age, bir from t_user where id = #{id}</select><!--delete--><delete id="delete" parameterType="String">delete from t_user where id = #{id}</delete><!--save--><insert id="save" parameterType="User">insert into t_user values(#{id}, #{name}, #{age}, #{bir})</insert><!--update--><update id="update" parameterType="User">update t_user set name = #{name}, age = #{age}, bir = #{bir} where id = #{id}</update>
</mapper>

  1. 测试类

TestEmpService

// 下面两个注解相当于启动SpringBoot应用
@SpringBootTest(classes = RedisDay2Application.class)   // 让SpringBoot启动起来,并指向入口类
@RunWith(SpringRunner.class)        // 让测试运行于Spring环境
public class TestEmpService {@Autowiredprivate EmpService empService;@Testpublic void testFindAll(){empService.findAll().forEach(emp -> System.out.println(emp));System.out.println("=============================");empService.findAll().forEach(emp -> System.out.println(emp));}@Testpublic void testmd5(){// org.springframework.util.DigestUtils 包下的 DigestUtilsString key = "-1628801192:1544301354:com.baizhi.dao.EmpDAO.findAll:0:2147483647:select id, name from t_emp:SqlSessionFactoryBean";// 将key转化为32位的16进制的字符串String s = DigestUtils.md5DigestAsHex(key.getBytes());System.out.println("s = " + s);}
}

TestUserService:

// 下面两个注解相当于启动SpringBoot应用
@SpringBootTest(classes = RedisDay2Application.class)   // 让SpringBoot启动起来,并指向入口类
@RunWith(SpringRunner.class)        // 让测试运行于Spring环境public class TestUserService {@Autowiredprivate UserService userService;@Testpublic void testFindAll(){userService.findAll().forEach(user -> System.out.println("user = " + user));System.out.println("============================");userService.findAll().forEach(user -> System.out.println("user = " + user));}@Testpublic void testFindId(){System.out.println(userService.findById("1"));System.out.println("============================");System.out.println(userService.findById("1"));}@Testpublic void testDelete(){userService.delete("1");System.out.println("============================");}@Testpublic void testSave(){User user = new User();user.setName("王五");user.setAge(20);user.setBir(new Date());userService.save(user);System.out.println("============================");}@Testpublic void testUpdate(){User user = new User();user.setId("2");user.setName("赵六");user.setAge(20);user.setBir(new Date());userService.update(user);System.out.println("============================");}
}

2.3 总结

  • 缓存穿透是数据库没有key的情况大量请求访问数据库
  • 缓存击穿是一个热点数据的key失效了,此时大量请求访问数据库
  • 缓存雪崩是大量数据在同一时刻失效,大量请求访问数据库,数据库压力突增

Redis应用场景以及分布式缓存的实现相关推荐

  1. 分布式锁的应用场景_分布式缓存技术Redis:高级应用(主从、事务与锁、持久化)...

    安全性设置 设置客户端操作秘密 redis安装好后,默认情况下登陆客户端和使用命令操作时不需要密码的.某些情况下,为了安全起见,我们可以设置在客户端连接后进行任何操作之前都要进行密码验证.修改redi ...

  2. Redis分布式缓存集群技术

    Redis分布式缓存集群技术(也支持持久化),是关系型数据库的互补产品 特点:追求高性能\高并发,对数据一致性要求比数据库要差一些. # 1. Redis在集群架构中的角色及工作流程     1)内存 ...

  3. 中间件(1)分布式缓存

    为了提高网站性能,一般都会使用到缓存,缓存的数据源包括数据库,外部接口等,缓存一般分为两种,本地缓存和分布式缓存,这里主要总结的是分布式缓存. Memcached vs Redis 最常用的分布式缓存 ...

  4. (转自微博) 分布式缓存架构基础

    文章不错,与大家共享之 文章转自点击打开链接 对于构建高性能.高可用的大型互联网系统,缓存是不可或缺的组成部分,微博的架构体系也是构建于缓存之上.本次课程介绍分布式缓存的相关知识,希望通过本次课程大家 ...

  5. 技术交流:分布式缓存的原理及应用

    文章目录 分布式缓存的原理及应用 缓存(进程级缓存与分布式缓存) 分布式缓存 Ehcache的原理及应用 Ehcache的原理 Ehcache的特点 Ehcache的架构 Ehcache的存储方式 E ...

  6. 分布式面试全家桶:分布式事务+分布式锁+分布式缓存+分布式面试题+分布式项目

    一.分布式事务 1.分布式事务简介 2.Seata简介 3.Seata-Server安装 4.Seata配置Nacos注册中心和配置中心 5.Seata-AT模式   6.XA协议 7.Seata整体 ...

  7. 用分布式缓存提升ASP.NET Core性能

    得益于纯净.轻量化并且跨平台支持的特性,ASP.NET Core作为热门Web应用开发框架,其高性能传输和负载均衡的支持已广受青睐.实际上,10-20台Web服务器还是轻松驾驭的.有了多服务器负载的支 ...

  8. redis set 超时_redis分布式锁3种实现方式对比分析总结

    我在这篇文章提到了分布式锁,但没有展开来讲,抛砖引玉,今天就来说说高并发服务编程中的redis分布式锁. 这里罗列出3种redis实现的分布式锁,并分别对比说明各自特点. Redis单实例分布式锁 实 ...

  9. 分布式缓存Redis应用场景解析

    Redis的应用场景非常广泛.虽然Redis是一个key-value的内存数据库,但在实际场景中,Redis经常被作为缓存来使用,如面对数据高并发的读写.海量数据的读写等. 举个例子,A网站首页一天有 ...

最新文章

  1. 44 Wild card Matching
  2. 判断是否十六进制格式字符串
  3. 【模型迭代】拒绝推断(RI)
  4. 关于redis的文章
  5. 每个努力奋斗过的人,被不公正的际遇砸了满头包的时候,都有那么一瞬间的代入感。出生就是hard模式的人,早已经历了太多的劳其筋骨饿其体肤,再多的人为考验只会摧毁人对美好的向往。...
  6. SpringBoot实战教程(2)| 整合knife4j3.0.3
  7. Struts项目中引入了过滤器filter后出现中文乱码情况
  8. 系统学习NLP(二十八)--GPT
  9. 可变参数函数——以printf为例子
  10. reg 正则表达式^
  11. Nginx从入门到入坟(九)- Nginx静态资源如何防盗链
  12. 【场景化解决方案】OA审批与用友U9数据集成
  13. 怎么彻底粉碎文件夹?文件还能恢复吗?操作过程在这里
  14. 数学计算机小论文范文,数学与生活论文范文
  15. 在线OPML压缩工具
  16. 04 爬取周杰伦首页歌单
  17. unity不规则碰撞_Unity中的刚体和碰撞器
  18. GMS(Google Mobile Services)简介
  19. 十三款流行的无线网络黑客工具介绍
  20. 【秋招】秋招最全指南,如何准备,如何投递,以及面试攻略大全分享!

热门文章

  1. Cloudera Manager安装streamsets
  2. 渐变填充Gradient
  3. iOS 自定义相册图片编辑页面
  4. LinuxProbe学习笔记(十六)
  5. 肯德基宅急送,你值得学习的滚动屏
  6. WCF 创建简单的CF程序VS2008
  7. 博通Jericho2芯片曝光,10 Tb/s傲视群雄
  8. 【Sharing】开发与研发
  9. webpack4+react 多页面打包
  10. 2022年全国职业院校技能大赛试题10(中职组)