Java Redis使用场景实例分析
1.作为缓存
1.1 为何使用
数据存储在内存中,数据查询速度快。可以分摊数据库压力。
1.2 什么样的数据适合放入缓存
查询频率比较高,修改频率比较低。
安全系数低的数据
1.3 使用redis作为缓存
1.3.1 未使用配置类
注意要将实体类实现序列化:
@Data @AllArgsConstructor @NoArgsConstructor @TableName(value = "tb_dept") public class Dept implements Serializable { @TableId(value = "id",type = IdType.AUTO) private Integer id; private String name; private String realname; }
对应依赖:
<dependencies> <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>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--mp的依赖--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.2</version> </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> </dependencies>
controller层对应代码:
@RestController @RequestMapping("order") public class DeptController { @Resource private DeptService deptService; @GetMapping("getById/{id}") //order/getById/1 //{}可以放多个,由下面的传参函数对应 //@PathVariable:获取请求映射中{}的值 public Dept getById(@PathVariable Integer id){ return deptService.findById(id); } @GetMapping("deleteById/{id}") public String deleteById(@PathVariable Integer id){ int i = deptService.deleteById(id); return i>0?"删除成功":"删除失败"; } @GetMapping("insert") public Dept insert(Dept dept){ Dept insert = deptService.insert(dept); return insert; } @GetMapping("update") public Dept update(Dept dept){ Dept update = deptService.update(dept); return update; } }
service层对应代码:
@Service public class DeptService { @Resource private DeptMapper deptMapper; //当存储的value类型为对象类型使用redisTemplate //存储的value类型为字符串。StringRedisTemplate @Autowired private RedisTemplate redisTemplate; //业务代码 public Dept findById(Integer id){ ValueOperations forValue = redisTemplate.opsForValue(); //查询缓存 Object o = forValue.get("dept::" + id); //缓存命中 if(o!=null){ return (Dept) o; } Dept dept = deptMapper.selectById(id); if(dept!=null){ //存入缓存中 forValue.set("dept::"+id,dept,24, TimeUnit.HOURS); } return dept; } public int deleteById(Integer id){ redisTemplate.delete("dept::"+id); int i = deptMapper.deleteById(id); return i; } public Dept insert(Dept dept){ int insert = deptMapper.insert(dept); return dept; } public Dept update(Dept dept){ redisTemplate.delete("dept::"+dept.getId()); int i = deptMapper.updateById(dept); return dept; } }
配置源:
# 配置数据源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mydb?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
#sql日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#连接redis
spring.redis.host=192.168.22*.1**
spring.redis.port=6379
查看的缓存: 前部分代码相同@before通知,后部分代码也相同后置通知。 我们可以AOP完成缓存代码和业务代码分离。
spring框架它应该也能想到。--使用注解即可完成。解析该注解。
1.3.2 使用配置类
(1)把缓存的配置类加入
@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;
(2) 使用开启缓存注解
(3)使用注解
//业务代码 //使用查询注解:cacheNames表示缓存的名称 key:唯一标志---dept::key //先从缓存中查看key为(cacheNames::key)是否存在,如果存在则不会执行方法体,如果不存在则执行方法体并把方法的返回值存入缓存中 @Cacheable(cacheNames = {"dept"},key="#id") public Dept findById(Integer id){ Dept dept = deptMapper.selectById(id); return dept; } //先删除缓存在执行方法体。 @CacheEvict(cacheNames = {"dept"},key = "#id") public int deleteById(Integer id){ int row = deptMapper.deleteById(id); return row; } //这个注释可以确保方法被执行,同时方法的返回值也被记录到缓存中,实现缓存与数据库的同步更新。 @CachePut(cacheNames = "dept",key="#dept.id") public Dept update(Dept dept){ int insert = deptMapper.updateById(dept); return dept; }
2.分布式锁
使用压测工具测试高并发下带来线程安全问题
2.1 压测工具的使用
内部配置:
2.2 库存项目
2.2.1 controller层
@RestController @RequestMapping("bucket") public class BucketController { @Autowired private BucketService bucketService; @GetMapping("update/{productId}") public String testUpdate(@PathVariable Integer productId){ String s = bucketService.updateById(productId); return s; } }
2.2.2 dao层
//此处写就不需要在启动类使用注解 @Mapper public interface BucketMapper extends BaseMapper<Bucket> { public Integer updateBucketById(Integer productId); }
2.2.3 entity层
@Data @AllArgsConstructor @NoArgsConstructor public class Bucket { @TableId(value = "productId",type = IdType.AUTO) private Integer productId; private Integer num; }
2.2.4 service层
@Service public class BucketService { @Resource private BucketMapper bucketMapper; public String updateById(Integer productId){ //查看该商品的库存数量 Bucket bucket = bucketMapper.selectById(productId); if(bucket.getNum()>0){ //修改库存每次减1 Integer integer = bucketMapper.updateBucketById(productId); System.out.println("扣减成功!剩余库存数:"+(bucket.getNum()-1)); return "success"; }else { System.out.println("扣减失败!库存数不足"); return "fail"; } } }
2.2.5 mapper
<?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.qy151wd.dao.BucketMapper"> <update id="updateBucketById" parameterType="int"> update bucket set num=num-1 where productId=#{productId} </update> </mapper>
2.2.6 依赖
<dependencies> <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>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--mp的依赖--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.2</version> </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> </dependencies>
2.2.7 测试结果
我们看到同一个库存被使用了n次。以及数据库中库存为负数。 线程安全问题导致。
2.3 解决方案
2.3.1 使用 synchronized 或者lock锁
对应的service层修改为
@Service public class BucketService { @Resource private BucketMapper bucketMapper; public String updateById(Integer productId){ //加自动锁 synchronized (this){ //查看该商品的库存数量 Bucket bucket = bucketMapper.selectById(productId); if(bucket.getNum()>0){ //修改库存每次减1 Integer integer = bucketMapper.updateBucketById(productId); System.out.println("扣减成功!剩余库存数:"+(bucket.getNum()-1)); return "success"; }else { System.out.println("扣减失败!库存数不足"); return "fail"; } } } }
如果搭建了项目集群,那么该锁无效 。
2.3.2 使用redisTemplate
(1)使用idea开集群项目
(2)使用nginx
(3)测试结果
发现又出现: 重复数字以及库存为负数。
(4)解决方法
service对应代码修改
@Service public class BucketService { @Resource private BucketMapper bucketMapper; @Autowired private RedisTemplate redisTemplate; public String updateById(Integer productId){ ValueOperations<String,String> forValue = redisTemplate.opsForValue(); Boolean flag = forValue.setIfAbsent("aaa::" + productId, "-----------------"); if(flag){ try{ //查看该商品的库存数量 Bucket bucket = bucketMapper.selectById(productId); if(bucket.getNum()>0){ //修改库存每次减1 Integer integer = bucketMapper.updateBucketById(productId); System.out.println("扣减成功!剩余库存数:"+(bucket.getNum()-1)); return "success"; }else { System.out.println("扣减失败!库存数不足"); return "fail"; } }finally { redisTemplate.delete("aaa::"+productId); } } return "服务器正忙,请稍后再试......."; } }
注意此处的测压速度不易太快(推荐使用5秒100个线程)
经过测压测试后,结果为:
以上是Java Redis使用场景实例分析的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

Redis是一种内存数据结构存储系统,主要用作数据库、缓存和消息代理。它的核心特点包括单线程模型、I/O多路复用、持久化机制、复制与集群功能。 Redis在实际应用中常用于缓存、会话存储和消息队列,通过选择合适的数据结构、使用管道和事务、以及进行监控和调优,可以显着提升其性能。

AI可以帮助优化Composer的使用,具体方法包括:1.依赖管理优化:AI分析依赖关系,建议最佳版本组合,减少冲突。2.自动化代码生成:AI生成符合最佳实践的composer.json文件。3.代码质量提升:AI检测潜在问题,提供优化建议,提高代码质量。这些方法通过机器学习和自然语言处理技术实现,帮助开发者提高效率和代码质量。

HTML5带来了五个关键改进:1.语义化标签提升了代码清晰度和SEO效果;2.多媒体支持简化了视频和音频嵌入;3.表单增强简化了验证;4.离线与本地存储提高了用户体验;5.画布与图形功能增强了网页的可视化效果。

MySQL函数可用于数据处理和计算。1.基本用法包括字符串处理、日期计算和数学运算。2.高级用法涉及结合多个函数实现复杂操作。3.性能优化需避免在WHERE子句中使用函数,并使用GROUPBY和临时表。

2024年必备的Laravel扩展包包括:1.LaravelDebugbar,用于监控和调试代码;2.LaravelTelescope,提供详细的应用监控;3.LaravelHorizon,管理Redis队列任务。这些扩展包能提升开发效率和应用性能。

Redis是NoSQL数据库,提供高性能和灵活性。1)通过键值对存储数据,适合处理大规模数据和高并发。2)内存存储和单线程模型确保快速读写和原子性。3)使用RDB和AOF机制进行数据持久化,支持高可用性和横向扩展。

typetraits在C 中用于编译时类型检查和操作,提升代码的灵活性和类型安全性。1)通过std::is_integral和std::is_floating_point等进行类型判断,实现高效的类型检查和输出。2)使用std::is_trivially_copyable优化vector拷贝,根据类型选择不同的拷贝策略。3)注意编译时决策、类型安全、性能优化和代码复杂性,合理使用typetraits可以大大提升代码质量。

在MySQL中配置字符集和排序规则的方法包括:1.设置服务器级别的字符集和排序规则:SETNAMES'utf8';SETCHARACTERSETutf8;SETCOLLATION_CONNECTION='utf8_general_ci';2.创建使用特定字符集和排序规则的数据库:CREATEDATABASEexample_dbCHARACTERSETutf8COLLATEutf8_general_ci;3.创建表时指定字符集和排序规则:CREATETABLEexample_table(idINT
