0%

Spring-Cache

Spring缓存

为什么需要缓存

  • 减轻服务器压力
客户端缓存 CDN对象存储
接入服务器 NGIX缓存
应用服务器Tomcat Mybatis && Hibernate缓存 分布式缓存/Redis

Spring Cache

  • Spring Cache 是Spring 提供的一整套的缓存解决方案(JSR- 107),
  • 提供一整套的接口和代码规范、配置、注解等,用于整合各种缓 存方案,如Redis、Caffeine、Guava Cache、Ehcache

不推荐原因

  • 无法测试:无法进行切片测试
  • 不支持复杂结构,只能进行简单对象查询存储

MyBatis缓存

一级缓存和二级缓存

  • 在参数和 SQL 完全一样的情况下,优先命中一级缓存,避免直接对数据库进行查询,提高性能。

  • 把执行的方法和参数通过算法生成缓存的键值, 将键值和结果存放在一 个 Map 中, 如果后续的键值一样, 则直接从 Map 中获取数据;

  • 缓存发霉 解决方案

    任何的 UPDATE, INSERT, DELETE 语句都会清空缓存

一级缓存

  • Mybatis强制开启一级缓存
  • 在一个事务中,若同一个事务中,出现相同的查询SQL,应用服务器则会将第一次查询结果进行本地缓存,后续相同Hash值语句返回缓存的相同结果
  • 不通事务的相同语句查询结果互不干扰
  • 若Database事务隔离级别在 Read Commited或更低时 则会导致数据异常

二级缓存

  • 二级缓存需额外存储介质,查询结果会进入二级缓存,多应用服务会在第三方介质中以不同NameSpace进行隔离

  • 开启二级缓存后,会将一级缓存结果存入二级缓存,后续查询优先查询二级缓存Hash数据,若没有则查询一级缓存数据。

  • 可以用参数控制开启二级缓存,但是多实例应用连接会导致数据异常

    缓存发霉解决方案只能解决当前服务器操作的结果,当A进行修改,B服务器缓存不会被删除,此时Load Balance请求使用B服务器查询到Cache则数值异常

  • 所有当前服务器前端连接 查询相同语句的 结果都会被相互共用

  • 不建议开启二级缓存

Redis缓存

  • Redis 是高性能的 内存 key-value 数据库
    • 支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用(RDB/AOF 在宕机后仍会丢弃数据)
    • 不仅支持简单的key-value类型的数据,同时还提供list,set,zset, hash等数据结构的存储。
    • 支持数据的备份,即master-slave模式的数据备份
    • Redis采用IO多路复用,单线程结构 – 读的速度是110000次/s, 写的速度是81000次/s
    • Redis的所有操作都是原子性的。多个操作也支持事务

Redis的常用数据类型

类型 简介 特性
String(字符串) 是 Redis 最基本的数据类型并且 是二进制安全的 可以包含任何数据,比如jpg图片或者序列 化的对象,一个键最大能存储512M
Hash(字典) 键值对集合 适合存储对象,并且可以像数据库中 update一个属性一样只修改某一项属性值
List(列表) 链表(双向链表),按照插入顺序排 序。 增删快,提供了操作某一段元素的API
Set(集合) 哈希表实现,元素不重复 添加、删除,查找的复杂度都是O(1) 。为 集合提供了求交集、并集、差集等操作
Bitmap 是一种实现对位的操作 借助字符串进行位操作

字符串

  • 可以是字符串、数字、二进制图片、音频等,大小不超过512MB

    1
    2
    3
    4
    5
    6
    7
    8
    //SET key value [ex seconds] [px milliseconds] [nx|xx]
    redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS)
    //GET key
    redisTemplate.opsForValue().get(key)
    //INCRBY key delta
    redisTemplate.opsForValue().increment(key, delta)
    //EXISTS key
    redisTemplate.hasKey(key)

Hash

  • 键值对集合,可以像数据库中update一个属性一样只修改某一项属性值

    1
    2
    3
    4
    //HSET key field
    redisTemplateopsForHash().put(key, field, value)
    //HGET key field
    redisTemplate.opsForHash().get(key, field)

List

  • 链表(双向链表),按照插入顺序排序。

    1
    2
    3
    4
    5
    6
    //LPOP key
    redisTemplate.opsForList().leftPop(key)
    //LLEN key
    redisTemplate.opsForList().size(key)
    //RPUSHX key value
    redisTemplate.opsForList().rightPush(key, value)

Set

  • 为集合提供了求交集、并集、差集等操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //SADD key value1 ... valueN
    redisTemplate..opsForSet().add(key, values)
    //SMEMBERS key
    redisTemplate.opsForSet().members(key)
    //SUNIONSTORE destination key1 key2
    redisTemplate.opsForSet().unionAndStore(key1, key2, destination) //SCARD key
    redisTemplate.opsForSet().size(key)
    //SREM key value1 ... valueN
    redisTemplate.opsForSet().remove(key, value1, ..., valueN)

Bitmap

  • 进行位操作

    1
    2
    3
    4
    5
    6
    //SETBIT key offset value
    redisTemplate.opsForValue().setBit(key, offset, value)
    //GETBIT key offset
    redisTemplate.opsForValue().getBit(key, offset)
    //BITCOUNT key start end
    redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount(key.getBytes(), start, end))

Lua操作

  • Redis内置 Lua 解释器
  • 使用 EVAL 命令对 Lua 脚本进行执行,并获取脚本的返回值
  • Redis是单线程的,脚本也是原子性的,因此只能执行完脚本后才能执行后续命令。因此脚步不能复杂,否则会带来性能问题。
  • 不建议使用
    • 动态语言类型定义问题
    • 无法调试

应用场景

  • 利用缓存存储对象,加快访问的速度(Spring事务结束前,一定要删除一次RedisCache)

  • 利用Set,计算权限的并集

  • 利用Redis实现库存的高并发扣减

    • 将库存量读入Redis中,利用Redis的原子性操作实现库存的高并发扣减
    • 不更新数据库,用带事务的消息队列 (如RocketMQ) 将库存量写回
  • Bloom过滤器

    • 是1970年由布隆提出的,用以判断一个元素是否在一个集合里

    • 用一个固定大小的空间,记录所有的Key

    • 原理

      • 利用多个Hash函数,针对同一个元素进行Hash计算后取模,再根据结果更改位置数值,尽量避免Hash冲突问题
    • 有误判率,当位数容量不足以满足元素空间数量时,会导致误判(K = m/n ln2) ,若所有位都被填充位1时,会导致所有判断失效

    • 使用场景

      • 可以避免恶意访问导致的缓存穿透、缓存雪崩问题(需定期删除避免正常访问数据查询异常)

        缓存穿透
        故意去请求缓存中不存在的数据,导致所有的请求都穿透到数据库访问上,造成数据库的负载增加

        缓存雪崩

        • 缓存同一时间大面积的失效,导致所有请求同时到达数据库,造成数据 库负载增加

内存管理

惰性删除

  • 当访问到该键时,如果该键值已过期,则返回空,并删除该键。

定期删除

  • 定期运行,随机中选取一些键并删除其中过期的键

内存管理机制

  • volatile-lru:利用LRU算法移除有过期时间的key。
    • LRU(Least recently used,最近最少使用) • 随机采样一部分键值,淘汰其中最久没有访问的键
  • volatile-random:随机移除有过期时间的key。
  • volatile-ttl:移除即将过期的key,根据最近过期时间来删除
  • allkeys-lru:利用LRU算法移除任何key。
  • allkeys-random:随机移除任何key。
  • noeviction(默认):不移除任何key,只是返回一个写错误

来源

https://www.icourse163.org/learn/XMU-1462056168#/learn/content