二级缓存详解

提问:怎么使用注解开启二级缓存,注解应该放在那里?

在 MyBatis 中使用注解开启二级缓存,需要在 Mapper 接口上添加 @CacheNamespace 注解。让我详细解释:

  1. 基本用法:

    java

    Apply to TestJwt.java

    @Mapper

    @CacheNamespace  // 开启二级缓存

    public interface UserMapper {

        // Mapper 方法

    }

  1. 完整示例:

    java

    Apply to TestJwt.java

    @Mapper

    @CacheNamespace(

        eviction = LruCache.class,           // 缓存淘汰策略

        flushInterval = 60000,               // 刷新间隔(毫秒)

        size = 1024,                         // 缓存大小

        readWrite = true,                    // 是否读写

        blocking = false                     // 是否阻塞

    )

    public interface UserMapper {

        @Select("SELECT * FROM user WHERE id = #{id}")

        User getUserById(Integer id);

        

        @Insert("INSERT INTO user(name, age) VALUES(#{name}, #{age})")

        int insertUser(User user);

    }

  1. 实体类配置:

    java

    Apply to TestJwt.java

    // 实体类需要实现 Serializable 接口

    public class User implements Serializable {

        private Integer id;

        private String name;

        private Integer age;

        

        // getter 和 setter

    }

  1. 缓存配置选项:

    java

    Apply to TestJwt.java

    @CacheNamespace(

        // 缓存实现类

        implementation = PerpetualCache.class,

        

        // 缓存淘汰策略

        eviction = LruCache.class,

        

        // 刷新间隔(毫秒)

        flushInterval = 60000,

        

        // 缓存大小

        size = 1024,

        

        // 是否读写

        readWrite = true,

        

        // 是否阻塞

        blocking = false

    )

  1. 缓存注解使用场景:

    java

    Apply to TestJwt.java

    @Mapper

    @CacheNamespace

    public interface UserMapper {

        // 查询方法会被缓存

        @Select("SELECT * FROM user WHERE id = #{id}")

        User getUserById(Integer id);

        

        // 更新方法会清除缓存

        @Update("UPDATE user SET name = #{name} WHERE id = #{id}")

        int updateUser(User user);

        

        // 删除方法会清除缓存

        @Delete("DELETE FROM user WHERE id = #{id}")

        int deleteUser(Integer id);

    }

  1. 缓存控制:

    java

    Apply to TestJwt.java

    @Mapper

    @CacheNamespace

    public interface UserMapper {

        // 使用 @Options 控制单个方法的缓存行为

        @Select("SELECT * FROM user WHERE id = #{id}")

        @Options(useCache = true)  // 使用缓存

        User getUserById(Integer id);

        

        // 禁用缓存

        @Select("SELECT * FROM user WHERE id = #{id}")

        @Options(useCache = false)  // 不使用缓存

        User getUserByIdNoCache(Integer id);

    }

  1. 最佳实践:

    java

    Apply to TestJwt.java

    @Mapper

    @CacheNamespace(

        eviction = LruCache.class,

        flushInterval = 60000,

        size = 1024,

        readWrite = true

    )

    public interface UserMapper {

        // 查询方法

        @Select("SELECT * FROM user WHERE id = #{id}")

        User getUserById(Integer id);

        

        // 更新方法

        @Update("UPDATE user SET name = #{name} WHERE id = #{id}")

        int updateUser(User user);

        

        // 删除方法

        @Delete("DELETE FROM user WHERE id = #{id}")

        int deleteUser(Integer id);

    }

  1. 注意事项:
  • 实体类必须实现 Serializable 接口
  • 缓存会占用内存,注意设置合适的缓存大小
  • 更新操作会自动清除缓存
  • 注意缓存一致性问题
  1. 常见问题:

    java

    Apply to TestJwt.java

    // 错误:实体类没有实现 Serializable

    public class User {  // 错误

        private Integer id;

        private String name;

    }

    // 正确:实体类实现 Serializable

    public class User implements Serializable {  // 正确

        private Integer id;

        private String name;

    }

  1. 缓存配置建议:
  • 对于频繁查询但不常更新的数据,可以开启缓存
  • 对于实时性要求高的数据,可以禁用缓存
  • 根据数据量大小设置合适的缓存大小
  • 根据业务需求设置合适的刷新间隔

总结:

  • 在 Mapper 接口上使用 @CacheNamespace 注解开启二级缓存
  • 实体类必须实现 Serializable 接口
  • 可以通过注解参数配置缓存行为
  • 注意缓存一致性和性能问题
  • 根据业务需求选择合适的缓存配置

开启二级缓存的时候,实体类必须实现Serializable吗?

是的,在 MyBatis 中开启二级缓存时,实体类必须实现 Serializable 接口。这是因为:

  1. 为什么需要实现 Serializable:
  • 二级缓存会将对象序列化后存储
  • 序列化是对象持久化的过程
  • 反序列化是对象恢复的过程
  • 没有实现 Serializable 会导致序列化失败
  1. 正确的实体类定义:

    java

    Apply to TestJwt.java

    // 正确:实现 Serializable 接口

    public class User implements Serializable {

        private Integer id;

        private String name;

        private Integer age;

        

        // getter 和 setter

    }

    // 错误:没有实现 Serializable 接口

    public class User {  // 会导致序列化异常

        private Integer id;

        private String name;

        private Integer age;

    }

  1. 序列化版本号:

    java

    Apply to TestJwt.java

    public class User implements Serializable {

        // 建议添加序列化版本号

        private static final long serialVersionUID = 1L;

        

        private Integer id;

        private String name;

        private Integer age;

    }

  1. 常见错误:

    java

    Apply to TestJwt.java

    // 错误:没有实现 Serializable

    @Mapper

    @CacheNamespace

    public interface UserMapper {

        @Select("SELECT * FROM user WHERE id = #{id}")

        User getUserById(Integer id);  // 会抛出序列化异常

    }

    // 正确:实现 Serializable

    @Mapper

    @CacheNamespace

    public interface UserMapper {

        @Select("SELECT * FROM user WHERE id = #{id}")

        User getUserById(Integer id);  // 正常工作

    }

  1. 注意事项:
  • 所有被缓存的实体类都必须实现 Serializable
  • 实体类中的其他对象属性也需要实现 Serializable
  • 建议添加序列化版本号
  • 注意序列化性能影响

总结:

  • 开启二级缓存时,实体类必须实现 Serializable 接口
  • 这是 MyBatis 二级缓存的要求
  • 没有实现 Serializable 会导致序列化异常
  • 建议添加序列化版本号

开启二级缓存有什么好处

开启 MyBatis 二级缓存有以下几个主要好处:

  1. 提高查询性能:
  • 减少数据库访问次数
  • 降低数据库压力
  • 提高响应速度
  • 减少网络开销
  1. 减少数据库负载:

    java

    Apply to TestJwt.java

    @Mapper

    @CacheNamespace

    public interface UserMapper {

        // 第一次查询会访问数据库

        @Select("SELECT * FROM user WHERE id = #{id}")

        User getUserById(Integer id);

        

        // 第二次查询相同数据会直接从缓存获取

        // 不会访问数据库

    }

  1. 适用场景:
  • 频繁查询但不常更新的数据
  • 数据量较小的表
  • 对实时性要求不高的数据
  • 多用户共享的数据
  1. 性能提升示例:

    java

    Apply to TestJwt.java

    // 不使用缓存

    for (int i = 0; i < 1000; i++) {

        userMapper.getUserById(1);  // 每次都会查询数据库

    }

    // 使用缓存

    for (int i = 0; i < 1000; i++) {

        userMapper.getUserById(1);  // 只有第一次查询数据库,后续从缓存获取

    }

  1. 内存使用优化:

    java

    Apply to TestJwt.java

    @CacheNamespace(

        size = 1024,  // 控制缓存大小

        eviction = LruCache.class  // 使用 LRU 淘汰策略

    )

    public interface UserMapper {

        // 缓存配置

    }

  1. 注意事项:
  • 缓存会占用内存
  • 需要注意缓存一致性
  • 不适合频繁更新的数据
  • 实体类需要实现 Serializable
  1. 最佳实践:

    java

    Apply to TestJwt.java

    @Mapper

    @CacheNamespace(

        eviction = LruCache.class,

        flushInterval = 60000,  // 定期刷新缓存

        size = 1024,  // 控制缓存大小

        readWrite = true

    )

    public interface UserMapper {

        // 适合缓存的方法

        @Select("SELECT * FROM user WHERE id = #{id}")

        User getUserById(Integer id);

        

        // 不适合缓存的方法

        @Select("SELECT * FROM user WHERE update_time > #{time}")

        List<User> getRecentUsers(Date time);

    }

  1. 使用建议:
  • 根据业务需求选择合适的缓存策略
  • 设置合适的缓存大小
  • 定期清理缓存
  • 注意缓存一致性
  1. 性能对比:

    text

    Apply to TestJwt.java

    不使用缓存:

    - 每次查询都访问数据库

    - 响应时间较长

    - 数据库压力大

    使用缓存:

    - 首次查询访问数据库

    - 后续查询从缓存获取

    - 响应时间短

    - 数据库压力小

  1. 总结好处:
  • 提高查询性能
  • 减少数据库访问
  • 降低数据库压力
  • 提高响应速度
  • 减少网络开销
  • 优化系统资源使用
  1. 使用场景示例:

    java

    Apply to TestJwt.java

    // 适合缓存的场景

    @Select("SELECT * FROM user WHERE id = #{id}")

    User getUserById(Integer id);  // 用户基本信息

    // 不适合缓存的场景

    @Select("SELECT * FROM order WHERE user_id = #{userId}")

    List<Order> getUserOrders(Integer userId);  // 订单信息经常变化

总结:

  • 二级缓存能显著提高查询性能
  • 减少数据库访问和压力
  • 提高系统响应速度
  • 优化资源使用
  • 适合特定业务场景
  • 需要注意缓存管理

最后我们来测试一下实体类如果没有实现Serializable接口会怎么样?会不会导致序列化异常?

可以看到没有实现Serializable接口导致出现io错误,再来试试实现Serializable接口会不会解决?

可以看到已经不会报错了,看来不实现Serializable接口确实会导致序列化异常。

那么我们再来看看二级缓存对于查询有没有提升?

我们可以看到我第一次查询速度是614ms

再重复查询试试

可以看到确实有所提升

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值