NingG +

实践系列:高并发的缓存实践

0.背景

在沟通「商品首页」展示时,如何保证高性能、高可用,具体来说,3 个方面需要注意:

  1. 本地缓存
  2. 分布式缓存
  3. 冷数据存储方案:未命中缓存的冷数据,数据库并发压力

1.高并发的缓存实践

具体来说,围绕下面 3 项,逐个讨论:

  1. 本地缓存
  2. 分布式缓存
  3. 冷数据存储方案:未命中缓存的冷数据,数据库并发压力

1.1.本地缓存

本地缓存,一般采用 Guava,此时,需要考虑 2 个问题:

  1. 数据一致性:本地缓存,会导致数据一致性减弱
  2. 线上服务的影响:大量本地缓存,会导致「堆内存占用」过多,并且导致 gc,会影响线上服务的执行

业界解决方案:

Think:

  1. JVM 上,本地内存的缓存,分级?
  2. JVM 上,如何使用「直接内存」?直接内存是否存在安全性问题?(多进程都访问)

关于堆外内存:

  1. JDK 1.8 默认的「堆外内存大小」跟「堆大小」一致,如果没设置-XX:MaxDirectMemorySize,则默认与-Xmx参数值相同

  2. 堆外内存:

    1. 是什么?JVM 进程占用的「直接内存」,非堆内存
    2. 疑问:这个内存,是 JVM 进程独占的吗?是否有其他进程访问的风险?
    3. 确定:这个内存本质是受 OS 管理的,其他进程可能会访问到
    4. 「堆外内存」:JVM 在直接内存中,开启的一块内存区域
      1. 利用 unsafe 包内工具,直接对物理内存进行的读写
      2. 是「byte 数组」
      3. Java 对象不能直接保存在里面,需要经过「序列化/反序列化」实现存取
    5. 有什么用?
      1. 减少 GC 次数:「堆外内存」的占用,不需要
      2. 减少复制次数:「堆内数据」发送到远端时(网络 IO or 文件 IO),会先复制到「直接内存」再发送,使用「堆外内存」节省了这一步
    6. 适用场景
      1. 长期存在和能复用的场景:「堆外内存」的分配和回收,也是需要成本的
      2. 注重稳定性的场景:「堆外内存」能有效避免因 GC 导致的暂停
      3. 适合简单对象存储:内部存储的都是「byte 数组」,Java 对象的读写,需要经过「序列化/反序列化」,复杂对象的「序列化/反序列化」需要特别注意
      4. 适合注重 IO 效率的场景:用「堆外内存」读写文件,效率高
    7. 有什么问题?
      1. 内存泄露:对外内存如果泄露,很难排查
      2. 不适合存储复杂对象:内部存储的都是「byte 数组」,Java 对象的读写,需要经过「序列化/反序列化」,复杂对象的「序列化/反序列化」需要特别注意
    8. 如何分配对外内存?
      1. 分配:本质 unsafe 包,直接操作物理内存,都要转换为「字节数组」
      2. 复杂的 Java 对象,都需要手动进行「序列化/反序列化」,因此,复杂对象不建议存储在「对外内存」
    9. 如何实现「堆外内存」的读写?
      1. 有开源方案,实现「堆外内存」的读写

1.2.分布式缓存

分布式缓存,存在几个问题:

  1. 分布式缓存存储不足时,如何处理?只有 LRU 清理缓存这一种策略?
  2. 分布式缓存,Redis 单实例,能支持最大多少并发连接数?QPS 又是多大?

实践中,典型问题

关联资料

分布式缓存的:雪崩、击穿、并发(并发控制)

Note:

1.3.冷热库存储

采用 MySQL 存储数据时,在请求量过大,并且缓存空间有限的情况下,如何考虑「冷数据」的处理:

可以使用 NoSQL 方案,例如阿里的 Tair,内存存储,并发效率更高,具体还需要进一步实践。

2.参考资料

同类文章:

微信搜索: 公众号 ningg ,即可联系我

Top