赞
踩
springboot对redis的操作封装了两个StringRedisTemplate和RedisTemplate类,StringRedisTemplate是RedisTemplate的子类,StringRedisTemplate它只能存储字符串类型,无法存储对象类型。要想用StringRedisTemplate存储对象必须把对象转为json字符串。
(1) 引入相关的依赖
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
(2)注入StringRedisTemplate该类对象
- @Autowired
- private StringRedisTemplate redisTemplate;
(3)使用StringRedisTemplate
该类把对每种数据类型的操作,单独封了相应的内部类。
对String类型的数据操作
- //对String类型的操作
- @Test
- void test01() {
-
- ValueOperations<String, String> forValue = stringRedisTemplate.opsForValue();
- //时间存储 时间结束后销毁
- forValue.set("k1","李四",30l, TimeUnit.SECONDS);
- //获取k1的value值
- String s = forValue.get("k1");
- System.out.println(s);
-
- //若存在,不存入,不存在则存入 返回布尔值
- Boolean aBoolean = forValue.setIfAbsent("k1", "张三", 30l, TimeUnit.SECONDS);
- System.out.println(aBoolean);
-
- //追加
- Integer i = forValue.append("k1", "是个人");
- System.out.println(i);
-
- }
对Hash类型的数据操作
- //对Hash类型的操作
- @Test
- void test02(){
- HashOperations<String, Object, Object> forHash = stringRedisTemplate.opsForHash();
- forHash.put("k1","name","张三");
- //必须都是字符串类型,虽然上面泛型是Object 但使用的是spring的序列化 Integer 无法转为String
- forHash.put("k1","age","18");
-
- Map<String,String> map=new HashMap<>();
- map.put("name","李四");
- map.put("age","25");
- forHash.putAll("k2",map);
-
- Object o = forHash.get("k1", "name");
- System.out.println(o);
-
- Set<Object> s= forHash.keys("k1");
- System.out.println(s);
- List<Object> l = forHash.values("k1");
- System.out.println(l);
-
- //获取k1对于的所有的field和value
- Map<Object, Object> k12 = forHash.entries("k1");
- System.out.println(k12);
-
- }
注意:
使用RedisTemplate 必须要指定序列化方式,默认使用jdk序列化方式。但会引起乱码,而且占用内存大。
- @Autowired
- private RedisTemplate redisTemplate;
-
- @Test
- void test01(){
- //指定key的序列化方式
- redisTemplate.setKeySerializer(new StringRedisSerializer());
- //指定value的序列化方式 GenericJackson2JsonRedisSerializer()/Jackson2JsonRedisSerializer<Object>(Object.class)
- redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
-
- ValueOperations forValue = redisTemplate.opsForValue();
-
- forValue.set("k1","张三",60l,TimeUnit.SECONDS);
-
- //value默认采用jdk,类必须实现序列化接口
- forValue.set("k2",new User(1,"李四","123456"));
- }
上面的RedisTemplate需要每次都指定key value以及field的序列化方式,可以创建一个配置类,为RedisTemplate指定好序列化。以后再用就无需指定。
- @Configuration
- public class RedisConfig {
-
- @Bean
- public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
- RedisTemplate<String, Object> template = new RedisTemplate<>();
- 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);
- template.setConnectionFactory(factory);
- //key序列化方式
- template.setKeySerializer(redisSerializer);
- //value序列化
- template.setValueSerializer(jackson2JsonRedisSerializer);
- //value hashmap序列化 filed value
- template.setHashValueSerializer(jackson2JsonRedisSerializer);
- template.setHashKeySerializer(redisSerializer);
- return template;
- }
- }
(1)数据存储在内存中,数据查询速度快。可以分摊数据库压力。
(2)什么样的数据适合放入缓存
查询频率比较高,修改频率比较低。
安全系数低的数据
(3)使用redis作为缓存
- @Autowired
- private UserMapper userMapper;
-
- @Autowired
- private RedisTemplate redisTemplate;
-
- //根据id查询
- public User findById(Integer id){
- ValueOperations forValue = redisTemplate.opsForValue();
- //查询缓存
- Object o = forValue.get("user::" + id);
- //缓存命中
- if(o!=null){
- return (User) o;
- }
- User user = userMapper.selectById(id);
- if(user!=null){
- //存入缓存
- forValue.set("user::"+id,user,2, TimeUnit.HOURS);
- }
- return user;
- }
-
- //根据id删除
- public int delete(Integer id){
- //先删除缓存再删除数据库中的数据
- redisTemplate.delete("user::"+id);
- int i = userMapper.deleteById(id);
- return i;
- }
-
- //添加
- public User insert(User user){
- int i = userMapper.insert(user);
- return user;
- }
-
- //根据id修改
- //先删掉缓存,再修改数据库
- public User update(User user){
- Integer id = user.getId();
- redisTemplate.delete("user::"+id);
- int i = userMapper.updateById(user);
- return user;
- }
查看的缓存: 前部分代码相同@before通知,后部分代码也相同后置通知。 我们可以AOP完成缓存代码和业务代码分离。
spring框架它应该也能想到。--使用注解即可完成。解析该注解。
(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;
- }
使用压测工具测试高并发下带来线程安全问题
同一个库存被使用了n次。以及数据库中库存为负数。 线程安全问题导致。
1. 解决方案: 使用 synchronized 或者lock锁
2.使用redis作为锁
nginx(windows系统下)
配置nginx文件 nginx.conf
开启nginx
注意nginx包的目录必须没有中文,否则无法开启
准备数据库文件
开启idea集群
测试代码:
controller层
- @RestController
- @RequestMapping("productStock")
- public class ProductStockController {
- @Autowired
- private ProductStockService productStockService;
- //减库存
- @RequestMapping("decreaseStock/{productId}")
- public String decreaseStock(@PathVariable("productId") Integer productId){
- return productStockService.decreaseStock(productId);
- }
- }
service层
- @Service
- public class ProductStockServiceImpl2 implements ProductStockService {
- @Autowired
- private ProductStockDao productStockDao;
-
- @Autowired
- private StringRedisTemplate stringRedisTemplate;
-
- @Override
- public String decreaseStock(Integer productId) {
- ValueOperations<String,String> forValue = stringRedisTemplate.opsForValue();
- Boolean flag = forValue.setIfAbsent("dis::" + productId, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
- if(flag) {
- try{
- //查看该商品的库存数量
- Integer stock = productStockDao.findStockByProductId(productId);
- if (stock > 0) {
- //修改库存每次-1
- productStockDao.updateStockByProductId(productId);
- System.out.println("扣减成功!剩余库存数:" + (stock - 1));
- return "success";
- } else {
- System.out.println("扣减失败!库存不足!");
- return "fail";
- }
- }finally {
- stringRedisTemplate.delete("dis::" + productId);
- }
- }
- return "服务忙,请稍后..........";
-
- }
- }
sql语句
- <select id="findStockByProductId" resultType="integer">
- select num from tbl_stock where productId=#{productId}
- </select>
-
- <update id="updateStockByProductId">
- update tbl_stock set num=num-1 where productId=#{productId}
- </update>
可以使用:redission依赖,redission解决redis超时问题的原理。
为持有锁的线程开启一个守护线程,守护线程会每隔10秒检查当前线程是否还持有锁,如果持有则延迟生存时间。
使用redis破解版(可以在windows中使用的),开启redis
- <dependency>
- <groupId>org.redisson</groupId>
- <artifactId>redisson</artifactId>
- <version>3.13.4</version>
- </dependency>
- //获取redisson对象并交于spring容器管理
- @Bean
- public Redisson redisson(){
- Config config =new Config();
- config.useSingleServer().
- setAddress("redis://192.168.226.234:6379").
- //redis默认有16个数据库
- setDatabase(0);
- return (Redisson) Redisson.create(config);
- }
测试代码
- @Service
- public class ProductStockServiceImpl2 implements ProductStockService {
- @Autowired
- private ProductStockDao productStockDao;
-
- @Autowired
- private Redisson redisson;
-
- @Override
- public String decreaseStock(Integer productId) {
- //获取锁对象
- RLock rlock = redisson.getLock("dis::"+productId);
- try{
- rlock.lock(30, TimeUnit.SECONDS);
- //查看该商品的库存数量
- Integer stock = productStockDao.findStockByProductId(productId);
- if (stock > 0) {
- //修改库存每次-1
- productStockDao.updateStockByProductId(productId);
- System.out.println("扣减成功!剩余库存数:" + (stock - 1));
- return "success";
- } else {
- System.out.println("扣减失败!库存不足!");
- return "fail";
- }
- }finally {
- rlock.unlock();
- }
- }
-
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。