redis的使用场景

目录

[1. 热点数据缓存](#1. 热点数据缓存)

[1.1 什么是缓存?](#1.1 什么是缓存?)

[1.2 缓存的原理](#1.2 缓存的原理)

[1.3 什么样的数据适合放入缓存中](#1.3 什么样的数据适合放入缓存中)

[1.4 哪个组件可以作为缓存](#1.4 哪个组件可以作为缓存)

[1.5 java使用redis如何实现缓存功能](#1.5 java使用redis如何实现缓存功能)

[1.5.1 需要的依赖](#1.5.1 需要的依赖)

[1.5.2 配置文件](#1.5.2 配置文件)

[1.5.3 代码](#1.5.3 代码)

[1.5.4 发现](#1.5.4 发现)

[1.6 使用缓存注解完成缓存功能](#1.6 使用缓存注解完成缓存功能)

[2. 分布式锁](#2. 分布式锁)

2.1模拟高并发

[2.2 使用syn和lock锁](#2.2 使用syn和lock锁)

[2.3 模拟多服务器](#2.3 模拟多服务器)

[2.4 nginx代理集群](#2.4 nginx代理集群)

[2.5 使用redis解决分布式锁文件](#2.5 使用redis解决分布式锁文件)


1. 热点数据缓存

1.1 什么是缓存?

为了把一些经常访问的数据,放入缓存中以减少对数据库的访问频率。从而减少数据库的压力,提高程序的性能。(内存中存储)

1.2 缓存的原理

1.3 什么样的数据适合放入缓存中

  1. 查询频率高且修改频率低
  2. 数据安全性低

1.4 哪个组件可以作为缓存

  1. redis组件
  2. memory组件
  3. ehcache组件

1.5 java使用redis如何实现缓存功能

1.5.1 需要的依赖

XML 复制代码
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

1.5.2 配置文件

XML 复制代码
server.port=8080

#数据源
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/1suo?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=12345678

#mybatisplus的sql日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#mapper映射文件
mybatis-plus.mapper-locations=classpath*:mapper/*.xml

#redis配置
spring.redis.host=172.16.7.192
spring.redis.port=6379
spring.redis.database=1

1.5.3 代码

控制层

java 复制代码
package com.ls.controller;

import com.ls.entity.Clazz;
import com.ls.service.ClazzService;
import com.ls.vo.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @program: springboot-redis-cache
 * @description:
 * @author: 1suo
 * @create: 2024-07-24 19:56
 **/
@RestController
@RequestMapping("clazz")
public class ClazzController {
    @Autowired
    private ClazzService clazzService;

    @DeleteMapping("delete")
    public R delete(Integer id) {
        return clazzService.delete(id);
    }
    @GetMapping("get")
    public R get(Integer id) {
        return clazzService.get(id);
    }
    @GetMapping("getAll")
    public R getAll() {
        return clazzService.getAll();
    }
    @PostMapping("save")
    public R save(@RequestBody Clazz clazz) {
        return clazzService.save(clazz);
    }
    @PutMapping("update")
    public R update(Clazz clazz) {
        return clazzService.update(clazz);
    }
}

业务层

java 复制代码
package com.ls.service.impl;

import com.ls.dao.ClazzDao;
import com.ls.entity.Clazz;
import com.ls.service.ClazzService;
import com.ls.vo.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import sun.dc.pr.PRError;

import java.util.ArrayList;
import java.util.List;

/**
 * @program: springboot-redis-cache
 * @description:
 * @author: 1suo
 * @create: 2024-07-24 19:57
 **/
@Service
public class ClazzServiceImpl implements ClazzService {
    @Autowired
    private ClazzDao clazzDao;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Override
    public R save(Clazz clazz) {
        int insert = clazzDao.insert(clazz);
        return new R(200,"保存成功",clazz);
    }

    @Override
    public R update(Clazz clazz) {
        int update = clazzDao.updateById(clazz);
        if (update>0){
            redisTemplate.opsForValue().set("clazz::" + clazz.getCid(),clazz);
        }
        return new R(200,"更新成功",clazz);
    }

    @Override
    public R delete(Integer id) {
        int delete = clazzDao.deleteById(id);
        if (delete>0){
            redisTemplate.delete("clazz::" + id);
        }
        return new R(200,"删除成功",id);
    }

    @Override
    public R get(Integer id) {
        ValueOperations<String, Object> forValue = redisTemplate.opsForValue();
        Object o = forValue.get("clazz::" + id);
        if(o!=null){
            return new R(200,"查询成功",(Clazz)o);
        }
        Clazz clazz = clazzDao.selectById(id);
        if (clazz!=null){
            forValue.set("clazz::" + id,clazz);
        }
        return new R(500,"查询成功",clazz);
    }

    @Override
    public R getAll() {
        List<Clazz> list = clazzDao.selectList(null);
        return new R(200,"查询成功",list);
    }
}

dao层

java 复制代码
package com.ls.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ls.entity.Clazz;

/**
 * @program: springboot-redis-cache
 * @description:
 * @author: 1suo
 * @create: 2024-07-25 08:41
 **/
public interface ClazzDao extends BaseMapper<Clazz> {
}

实体类

java 复制代码
package com.ls.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @program: springboot-redis-cache
 * @description:
 * @author: 1suo
 * @create: 2024-07-24 19:56
 **/
@Data
@NoArgsConstructor
@AllArgsConstructor

@TableName("tbl_clazz")
public class Clazz {
    @TableId(type = IdType.AUTO)
    private Integer cid;

    private String cname;
}

配置类

java 复制代码
package com.ls.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
        RedisTemplate<String, Object> template = new RedisTemplate<>();//创建redisTemplate对象
        StringRedisSerializer serializer = new StringRedisSerializer();//字符串序列化对象
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);//Jackson的序列化对象
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setConnectionFactory(factory);
//        key序列化方式
        template.setKeySerializer(serializer);
//        value序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
//        value hashmap序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.setKeySerializer(serializer);
        return template;
    }
}

@MapperScan("com.ls.dao") 注解,将dao层自动代理为Mapper代理对象。

1.5.4 发现

业务层代码除了要维护核心业务功能外,额外还要维护缓存的代码。

解决: 使用AOP面向切面编程。(spring框架用aop切面实现)

1.6 使用缓存注解完成缓存功能

必须spring缓存使用的组件。

config:为解决序列化的问题

java 复制代码
 @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600)) //缓存过期10分钟 ---- 业务需求。
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))//设置key的序列化方式
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) //设置value的序列化
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }

开启缓存注解

复制代码

使用

java 复制代码
package com.ls.service.impl;

import com.ls.dao.ClazzDao;
import com.ls.entity.Clazz;
import com.ls.service.ClazzService;
import com.ls.vo.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import sun.dc.pr.PRError;

import java.util.ArrayList;
import java.util.List;

/**
 * @program: springboot-redis-cache
 * @description:
 * @author: 1suo
 * @create: 2024-07-24 19:57
 **/
@Service
public class ClazzServiceImpl implements ClazzService {
    @Autowired
    private ClazzDao clazzDao;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Override
    public R save(Clazz clazz) {
        int insert = clazzDao.insert(clazz);
        return new R(200,"保存成功",clazz);
    }

    @CachePut(cacheNames = "clazz", key = "#clazz.cid")
    @Override
    public Clazz update(Clazz clazz) {
        int update = clazzDao.updateById(clazz);
        return clazz;
    }

    @CacheEvict(cacheNames = "clazz", key = "#id")
    @Override
    public R delete(Integer id) {
        int delete = clazzDao.deleteById(id);
        return new R(200,"删除成功",id);
    }

    @Cacheable(cacheNames = "clazz", key = "#id")
    @Override
    public Clazz get(Integer id) {
        Clazz clazz = clazzDao.selectById(id);
        return clazz;
    }

    @Override
    public R getAll() {
        List<Clazz> list = clazzDao.selectList(null);
        return new R(200,"查询成功",list);
    }
}

2. 分布式锁

2.1模拟高并发

使用jmeter压测工具:

第一步:

第二步:

第三步:

通过压测发现库存超卖和重卖了,解决办法使用锁

2.2 使用syn和lock锁

java 复制代码
public String decrement(Integer productid) {
        //根据id查询商品的库存
        int num = stockDao.findById(productid);
        synchronized (this) {

            if (num > 0) {
                //修改库存
                stockDao.update(productid);
                System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");
                return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";
            } else {
                System.out.println("商品编号为:" + productid + "的商品库存不足。");
                return "商品编号为:" + productid + "的商品库存不足。";
            }
        }
    }

上面使用syn和lock虽然解决了并发问题,但是我们未来项目部署时可能要部署集群模式

2.3 模拟多服务器

点击增加一个服务器,同时启动后,实现模拟多服务器。 下面是服务器配置。

启动俩个服务器:

2.4 nginx代理集群

下载window版本的nginx实现代理集群。

nginx.conf配置文件:

通过压测发现本地锁 无效了,使用redis解决分布式锁文件

2.5 使用redis解决分布式锁文件

核心代码:

java 复制代码
@Service
public class StockService {

    @Autowired
    private StockDao stockDao;
    @Autowired
    private RedissonClient redisson;

    //
    public String decrement(Integer productid) {
        RLock lock = redisson.getLock("product::" + productid);
        lock.lock();
        try {
            //根据id查询商品的库存: 提前预热到redis缓存中
            int num = stockDao.findById(productid);
            if (num > 0) {
                //修改库存---incr---定时器[redis  数据库同步]
                stockDao.update(productid);
                System.out.println("商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个");
                return "商品编号为:" + productid + "的商品库存剩余:" + (num - 1) + "个";
            } else {
                System.out.println("商品编号为:" + productid + "的商品库存不足。");
                return "商品编号为:" + productid + "的商品库存不足。";
            }
        }finally {
            lock.unlock();
        }
    }
}
相关推荐
Python私教2 小时前
model中能定义字段声明不存储到数据库吗
数据库·oracle
BestandW1shEs5 小时前
谈谈Mysql的常见基础问题
数据库·mysql
重生之Java开发工程师5 小时前
MySQL中的CAST类型转换函数
数据库·sql·mysql
教练、我想打篮球5 小时前
66 mysql 的 表自增长锁
数据库·mysql
Ljw...5 小时前
表的操作(MySQL)
数据库·mysql·表的操作
哥谭居民00015 小时前
MySQL的权限管理机制--授权表
数据库
wqq_9922502775 小时前
ssm旅游推荐系统的设计与开发
数据库·旅游
难以触及的高度5 小时前
mysql中between and怎么用
数据库·mysql
Jacky(易小天)6 小时前
MongoDB比较查询操作符中英对照表及实例详解
数据库·mongodb·typescript·比较操作符