略谈服务端缓存设计

原文发表于2015-10-14。

大略谈一下,各位轻拍哦!

服务端性能优化,除了重构,最常用的手段就是缓存。缓存主要分为本地缓存和分布式缓存两种。

根据我们每日千万次访问的经验来看,缓存不是必须的。优化充足的情况下,SQL平均耗时1ms。这是因为命中了索引,并且命中了MySQL缓冲池(内存中)。如果命中索引但不命中缓冲池,且查询数据量不大,磁盘并发量不高,则大约耗时10ms(磁盘寻道)。如果这些都不满足,耗时就大了。

所以首先要建设的是索引,一切供查询的字段能索引就索引。有的字段取值多样性很小,比如布尔值、枚举值,就不适合索引(就算命中索引也可能要全盘扫描),查询时要联合其他字段过滤下,才会快。Replication, Sharding也是好办法,能用就用上。

本地缓存是应用程序在同一JVM内的缓存,通常是ConcurrentHashMap或Guava Cache。优点是耗时低得忽略不计,缺点是占用本地内存、多机会冗余、数据不同步。当集群里每个App Server都有个缓存,会有很多数据是重复的,而且某台机器更新了一条数据,别的机器不知道啊,只能等缓存过期。

  • 同步问题可以用消息队列来解决,一台有更新,广播消息给其他机器,准实时同步
  • Guava Cache有expire或refresh两种过期方式,expire是过期就丢弃,refresh是过期先留着旧值,取到新值再更新。expire会有点性能波动,为了响应性可以选择refresh方式。另外建议设个上限,量太多会影响GC的。
  • 根据Jim Gray的经验数字,过期时间通常以5分钟为宜(主要还是看你业务哦)。

分布式缓存是Memcached/Couchbase, Redis这类,访问要经过网络,多少会有一点点开销。Memcached或Redis本身就要有集群,否则一旦扛不住并发,还不如数据库呢。某台App Server更新了一条数据,就通知缓存把这条更新或丢弃,确保其他Server能拿到最新结果。

  • 查询操作要有超时设置。注意耗时,如果超过10ms就有点不好了。
  • 缓存服务用来存复杂查询的结果倒是极合适的,这时候比数据库快得多。

缓存除了加快访问以外,也能提高负载能力。因为数据库连接是有限的资源——不支持NIO,每个连接同时只能服务一个线程,有多少连接就有多少并发。如果有慢查询占住了连接,系统性能会急剧下降!缓存就能减轻对数据库连接的依赖。

本地缓存和分布式缓存各有千秋,一般建议用一致性更高的分布式缓存,当性能需要极端调优时,使用本地缓存。

posted @ 2020-12-25 17:36  计算法  阅读(40)  评论(0编辑  收藏  举报