使用redis遇到的问题

最近,在做一个商城项目的过程中使用了redis,也碰到了一些问题,记录下。
1.使用redis的过程中抛出了Could not get a resource from the pool的异常。
此时我修改了redis.conf的内容,变为以下

daemonize no
protected-mode no
#bind 127.0.0.1

但是发现不起作用,原来是redis启动的时候如果不指定配置文件,则还会按照默认的启动方式,此时只需要启动的时候附带上redis.conf就可以了。

./redis-server redis.conf

2、redis用在项目的时候要考虑以下问题:
2.1、缓存雪崩
所有的缓存数据在同一时间过期,导致所有数据都访问数据库,使得数据库崩溃,系统崩溃。
解决:给所有的缓存设置一个随机的过期时间。
2.2、缓存穿透
攻击者请求的数据是数据库和redis中都没有的数据,使得每次都访问数据库,已达到攻击效果。
解决:null或者空字符串值设置给redis。
2.3、缓存击穿
某一个热度高的商品在某一个时间缓存过期了,导致都访问数据库。
解决:使用分布式锁,一次都只能有一个请求访问数据库。
以下是代码,以及一些详细过程。

public PmsSkuInfo getSkuById(String skuId,String ip) {
        System.out.println("ip为"+ip+"的同学:"+Thread.currentThread().getName()+"进入的商品详情的请求");
        PmsSkuInfo pmsSkuInfo = new PmsSkuInfo();
        // 链接缓存
        Jedis jedis = redisUtil.getJedis();
        // 查询缓存
        String skuKey = "sku:"+skuId+":info";
        String skuJson = jedis.get(skuKey);

        if(StringUtils.isNotBlank(skuJson)){//if(skuJson!=null&&!skuJson.equals(""))
            System.out.println("ip为"+ip+"的同学:"+Thread.currentThread().getName()+"从缓存中获取商品详情");

            pmsSkuInfo = JSON.parseObject(skuJson, PmsSkuInfo.class);
        }else{
            // 如果缓存中没有,查询mysql
            System.out.println("ip为"+ip+"的同学:"+Thread.currentThread().getName()+"发现缓存中没有,申请缓存的分布式锁:"+"sku:" + skuId + ":lock");

            // 设置分布式锁,解决缓存击穿问题。使用token防止错误删除别人的分布
            //式锁,不过,即使用了token,也可能删除的时候刚好另外一个创建了,
            //也可能导致删除错误,此时使用lua脚本,直接删除,没有空闲的时间。
            String token = UUID.randomUUID().toString();
            String OK = jedis.set("sku:" + skuId + ":lock", token, "nx", "px", 10*1000);// 拿到锁的线程有10秒的过期时间
            if(StringUtils.isNotBlank(OK)&&OK.equals("OK")){
                // 设置成功,有权在10秒的过期时间内访问数据库
                System.out.println("ip为"+ip+"的同学:"+Thread.currentThread().getName()+"有权在10秒的过期时间内访问数据库:"+"sku:" + skuId + ":lock");

                pmsSkuInfo =  getSkuByIdFromDb(skuId);

                if(pmsSkuInfo!=null){
                    // mysql查询结果存入redis
                    jedis.set("sku:"+skuId+":info",JSON.toJSONString(pmsSkuInfo));
                }else{
                    // 数据库中不存在该sku
                    // 为了防止缓存穿透将,
                    jedis.setex("sku:"+skuId+":info",60*3,JSON.toJSONString(""));
                }null或者空字符串值设置给redis

                // 在访问mysql后,将mysql的分布锁释放
                System.out.println("ip为"+ip+"的同学:"+Thread.currentThread().getName()+"使用完毕,将锁归还:"+"sku:" + skuId + ":lock");
                String lockToken = jedis.get("sku:" + skuId + ":lock");
                if(StringUtils.isNotBlank(lockToken)&&lockToken.equals(token)){
                    //jedis.eval("lua");可与用lua脚本,在查询到key的同时删除该key,防止高并发下的意外的发生
                    jedis.del("sku:" + skuId + ":lock");// 用token确认删除的是自己的sku的锁
                }
            }else{
                // 设置失败,自旋(该线程在睡眠几秒后,重新尝试访问本方法)
                System.out.println("ip为"+ip+"的同学:"+Thread.currentThread().getName()+"没有拿到锁,开始自旋");

                return getSkuById(skuId,ip);
            }
        }
        jedis.close();
        return pmsSkuInfo;
    }
### Spring MVC 中集成和使用 Redis 的教程 #### 1. 配置 Redis 连接参数 在 `application.properties` 文件中配置 Redis 的连接参数,这些参数定义了 Redis 实例的位置以及连接池的行为。 ```properties # Redis settings redis.host=127.0.0.1 redis.port=6379 redis.pass= redis.maxIdle=300 redis.maxActive=600 redis.maxWait=1000 redis.testOnBorrow=true ``` 上述配置指定了 Redis 主机地址、端口以及其他连接池属性[^5]。 --- #### 2. 添加 Maven 或 Gradle 依赖 为了支持 Redis,在项目的构建文件中引入必要的依赖项。以下是 Maven 的示例: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- 如果需要 JSON 序列化 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> ``` 通过添加以上依赖,可以实现对 Redis 数据库的支持并简化序列化的复杂度[^3]。 --- #### 3. 创建 Redis 配置类 创建一个配置类来初始化 `RedisTemplate` 和其他相关组件。 ```java 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.StringRedisTemplate; @Configuration public class RedisConfig { @Bean public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) { return new StringRedisTemplate(redisConnectionFactory); } } ``` 此方法返回了一个 `StringRedisTemplate` 对象,它是一个专门处理字符串键值对的模板工具[^1]。 --- #### 4. 开发服务层逻辑 编写一个简单的服务类来封装 Redis 的基本操作功能。 ```java @Service public class RedisService { @Autowired private RedisTemplate<String, Object> redisTemplate; /** * 设置指定 Key 的 Value 值 */ public void setKey(String key, Object value) { redisTemplate.opsForValue().set(key, value); } /** * 获取指定 Key 的 Value 值 */ public Object getValue(String key) { return redisTemplate.opsForValue().get(key); } /** * 删除指定 Key */ public Boolean deleteKey(String key) { return redisTemplate.delete(key); } } ``` 这段代码展示了如何利用 `RedisTemplate` 来执行常见的增删改查操作[^2]。 --- #### 5. 控制器调用 Redis 功能 最后,在控制器中调用服务层的方法完成业务需求。 ```java @RestController @RequestMapping("/api/redis") public class RedisController { @Autowired private RedisService redisService; @PostMapping("/set") public ResponseEntity<?> setValue(@RequestParam String key, @RequestParam String value) { redisService.setKey(key, value); return ResponseEntity.ok("成功设置:" + key + "=" + value); } @GetMapping("/get/{key}") public ResponseEntity<?> getValue(@PathVariable String key) { Object value = redisService.getValue(key); if (value != null) { return ResponseEntity.ok(value.toString()); } else { return ResponseEntity.status(HttpStatus.NOT_FOUND).body("未找到该 Key"); } } @DeleteMapping("/delete/{key}") public ResponseEntity<?> deleteValue(@PathVariable String key) { boolean result = redisService.deleteKey(key); if (result) { return ResponseEntity.ok("删除成功:" + key); } else { return ResponseEntity.status(HttpStatus.NOT_FOUND).body("删除失败:Key不存在"); } } } ``` 这里实现了 RESTful API 接口,允许客户端通过 HTTP 请求访问 Redis 资源。 --- #### 6. 解决常见问题 针对 Redis 使用过程中可能遇到的一些典型问题及其解决办法包括但不限于以下几点: - **缓存击穿**:可以通过加锁或者布隆过滤器缓解。 - **缓存穿透**:采用预设默认值或 TTL 时间戳机制规避非法查询。 - **缓存雪崩**:合理分配过期时间间隔,避免大量缓存在同一时刻失效[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值