• A+
所属分类:未分类

SpringBoot整合redis缓存使用

carson0408

于 2020-03-05 00:05:33 发布

12311

 已收藏 98

分类专栏: Redis 文章标签: redis spring boot

版权

Redis

专栏收录该内容

7 篇文章2 订阅

订阅专栏

        一开始写增删改查基本都是直接对库操作,接口调用量少的时候,性能问题几乎不存在,但是数据量级上升之后,这些增删改查接口的压力也会大大上升,甚至会出现慢查询的情况,出现较大的延迟。这时候机智的小伙伴会使用索引,没错,索引可以解决一部分查询造成的性能问题。那么如何才能进一步提升查询的性能呢?对于读多写少的表可以使用缓存,那么将大大减少读取数据库的次数。

 

 

一.Redis(缓存)常见的问题

1.缓存穿透

        缓存穿透指查询一个一定不存在的数据,由于缓存不命中就会从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据都要到数据库查询,造成缓存穿透。这种情况其实就是因为数据库不存在的数据无法写入缓存,解决这个问题的方法,就是把这种数据库查不到值的情况也考虑进去。

解决方案:

缓存空值,将数据库查询不到的则作为空值存入,那么下次可以从缓存中获取key值是空值可以判断出数据库不存在这个值。

使用布隆过滤器,布隆过滤器能判断一个 key 一定不存在(不保证一定存在,因为布隆过滤器结构原因,不能删除,但是旧值可能被新值替换,而将旧值删除后它可能依旧判断其可能存在),在缓存的基础上,构建布隆过滤器数据结构,在布隆过滤器中存储对应的 key,如果存在,则说明 key 对应的值为空。

2.缓存击穿

       缓存击穿针对某些高频访问的key,当这个key失效的瞬间,大量的请求击穿了缓存,直接请求数据库。

解决方案:

设置二级缓存

设置高频key缓存永不过期

使用互斥锁,在执行过程中,如果缓存过期,那么先获取分布式锁,在执行从数据库中加载数据,如果找到数据就加入缓存,没有就继续该有的动作,在这个过程中能保证只有一个线程操作数据库,避免了对数据库的大量请求。

3.缓存雪崩

        缓存雪崩指缓存服务器重启、或者大量缓存集中在某一个时间段失效,这样失效的时候,会给后端系统带来很大压力,造成数据库的故障。

解决方法:

缓存高可用设计,Redis sentinel和Redis Cluster等

请求限流与服务熔断降级机制,限制服务请求次数,当服务不可用时快速熔断降级。

设置缓存过期时间一定的随机分布,避免集中在同一时间缓存失效,可以在设计时将时间定为一个固定值+随机值。

定时更新缓存策略,对于实时性要求不高的数据,定时进行更新。

4.分布式锁

    我们知道目前常见的分布式锁有基于zookeeper和基于redis的,基于zookeeper是一个持久节点下的临时顺序节点的抢占,是一个队列机制。而基于redis则是对某个redis缓存key的抢占。两者优缺点如下:

  优点 缺点

zookeeper

1.有封装好的框架,容易实现

2.有等待锁的队列,大大提升抢锁效率。

添加和删除节点性能较低

redis Set和Del指令性能较高

1.实现复杂,需要考虑超时,原子性,误删等情形。

2.没有等待锁的队列,只能在客户端自旋来等待,效率低下。

    可以看出,redis实现分布式锁需要设置超时时间,如果不设置超时时间会出现什么问题呢?如果获取锁之后在解锁过程中出现宕机,则会导致死锁现象。因此需要设置超时时间来避免死锁现象。在redis2.8版本之前获取锁及设置超时时间分为setnx和expire两个步骤完成,如果这两个步骤之间出现宕机现象,依然会存在死锁现象。因此,redis2.8版本做了修改,将setnx和expire两个作为一个方法实现,避免了宕机引起的死锁现象。

 

二.缓存的使用(springboot整合redis)

1.引入依赖:

<dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-data-redis</artifactId>

        </dependency>

 

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-cache</artifactId>

        </dependency>

2.配置

application.yml文件

spring:

   redis:

    ## Redis数据库索引(默认为0)

    database: 0

    ## Redis服务器地址

    host: 127.0.0.1

    ## Redis服务器连接端口

    port: 6379

    ## Redis服务器连接密码(默认为空)

    password: root

    jedis:

      pool:

        ## 连接池最大连接数(使用负值表示没有限制)

        #spring.redis.pool.max-active=8

        max-active: 8

        ## 连接池最大阻塞等待时间(使用负值表示没有限制)

        #spring.redis.pool.max-wait=-1

        max-wait: -1

        ## 连接池中的最大空闲连接

        #spring.redis.pool.max-idle=8

        max-idle: 8

        ## 连接池中的最小空闲连接

        #spring.redis.pool.min-idle=0

        min-idle: 0

    ## 连接超时时间(毫秒)

    timeout: 1200

Application类----@EnableCaching启动缓存注解

@SpringBootApplication

@EnableTransactionManagement

@MapperScan("com.carson.cachedemo.mapper")

@EnableScheduling

@EnableCaching

public class CachedemoApplication {

 

    public static void main(String[] args) {

        SpringApplication.run(CachedemoApplication.class, args);

    }

 

}

提供使用缓存的接口类,该类对user对象做了缓存,即user表中的每一条记录做了缓存,缓存key值是userid,缓存value值为user对象。其中常用的缓存注解

其中获取记录需要添加注解:@Cache(value="xxx",key="xxxx",unless="#result==null")

删除则用@CacheEvict(value="xxx",key="xxxxx")

更新则用 @CachePut(value="xxx",key="xxxx")

@Cacheable 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存

@CacheEvict 清空缓存

@CachePut 保证方法被调用,又希望结果被缓存。

@Service

public class UserManageServiceImpl implements UserManageService{

 

    @Autowired

    UserManageMapper userManageMapper;

 

 

    /**

     * 根据用户id获取用户信息

     * @param userid

     * @return

     */

    @Override

    @Transactional(propagation = Propagation.REQUIRED,readOnly = false)

    @Cacheable(value = "user",key = "#userid",unless = "#result==null")

    public User findUser(int userid) {

        return userManageMapper.findByUserid(userid);

    }

 

    /**

     * 删除的时候需要把好友关系表中的关系也删除。

     * @param userid

     * @return

     */

    @Override

    @Transactional(propagation = Propagation.REQUIRED,readOnly = false)

    @CacheEvict(value = "user",key = "#userid")

    public int deleteUser(int userid) {

 

        int res2 = userManageMapper.deleteFriendByUserid(userid);

        int res3= userManageMapper.deleteFriendByFriendid(userid);

        int res1= userManageMapper.deleteByUserid(userid);

        return res1;

    }

 

    /**

     *  查询所有好友

     * @return

     */

    @Override

    @Transactional(propagation = Propagation.REQUIRED,readOnly = false)

    public List<User> findAllUser() {

        return userManageMapper.findAllUser();

    }

 

    /**

     * 保存用户

     * @param user

     * @return

     */

    @Override

    @Transactional(propagation = Propagation.REQUIRED,readOnly = false)

    public int saveUser(User user) {

        return userManageMapper.insertUser(user);

    }

 

    /**

     * 更新用户

     * @param user

     * @return

     */

    @Override

    @Transactional(propagation = Propagation.REQUIRED,readOnly = false)

    @CachePut(value = "user",key = "#userid")

    public int updateUser(User user) {

        return userManageMapper.updateUser(user);

    }

 

    /**

     * 获取所有好友信息

     * @return

     */

    @Override

    @Transactional(propagation = Propagation.REQUIRED,readOnly = false)

    public List<Friends> getFriends() {

        return userManageMapper.getFriends();

    }

 

    /**

     * 根据userid获取好友信息

     * @param userid

     * @return

     */

    @Override

    @Transactional(propagation = Propagation.REQUIRED,readOnly = false)

    public Friends getFriendsByUserid(int userid) {

        return userManageMapper.getFriendsByUserid(userid);

    }

 

    /**

     * 先对friendid进行查询,如果用户表中没有friendid,以防安全,禁止添加,成对添加

     *

     * @return

     */

    @Override

    @Transactional(propagation = Propagation.REQUIRED,readOnly = false)

    public int addFriend(Relation relation) {

 

       Relation search=userManageMapper.getRelation(relation);

       if(search!=null){

           throw new OperationException(Result.FRIENDS_EXIST);

       }

       Relation newRelation = new Relation();

       newRelation.setUserid(relation.getFriendid());

       newRelation.setFriendid(relation.getUserid());

       int res1 = userManageMapper.insertFriend(relation);

       int res2 = userManageMapper.insertFriend(newRelation);

       return res1*res2;

    }

 

    @Override

    @Transactional(propagation = Propagation.REQUIRED,readOnly = false)

    public User findByFriendid(int friendid) {

        return userManageMapper.findByFriendid(friendid);

    }

 

    /**

     * 删除好友关系,逻辑是成对删除

     * @param relation

     * @return

     */

    @Override

    @Transactional(propagation = Propagation.REQUIRED,readOnly = false)

    public int deleteFriend(Relation relation) {

        Relation search=userManageMapper.getRelation(relation);

        if(search==null){

            throw new OperationException(Result.FRIENDS_NOT_EXIST);

        }

        Relation newRelation = new Relation();

        newRelation.setUserid(relation.getFriendid());

        newRelation.setFriendid(relation.getUserid());

        int res1 = userManageMapper.deleteFriend(relation);

        int res2 = userManageMapper.deleteFriend(newRelation);

        return res1*res2;

 

    }

 

    /**

     * 用于查询是否存在该用户

     * @param login

     * @return

     */

    @Override

    public User search(Login login) {

        return userManageMapper.searchForLogin(login);

    }

 

 

}

1.数据库中记录如下:

此时缓存的记录如下:

然后调用http://localhost:8081/user/get/1016,得到如下结果。

此时查看缓存记录:

则发现缓存出现。

2.调用http://localhost:8081/user/delete/1016 删除数据库记录

此时数据库记录

缓存记录如下,说明删除操作会清空对应的缓存。

————————————————

版权声明:本文为CSDN博主「carson0408」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/carson0408/article/details/104664552/

  • 版权声明:本站原创文章,于2022年3月26日18:23:49,由 发表,共 6574 字。
  • 转载请注明: | 程序员小羊

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: