存储高性能--缓存

缓存的引入,解决的问题一般分为以下两大类场景:

  • 读取的数据需要进行复杂运算,使用持久化的存储方式无法满足性能要求(例如当前在线人数)
  • 对于数据的读取多而写入少的情况,重复地读取相同的数据也会造成相当大的性能压力(例如朋友圈、淘宝商品等,一次发布大量读取)

引入了缓存后,可以大大缓解上述场景下的性能问题,当然与此同时,也会为系统引入额外的复杂性。

缓存穿透

缓存穿透是指访问了不存在的数据,造成业务系统直接访问数据库,在访问量大的时候回拖垮性能。

要解决这个问题,首先可以在接口层做访问校验,只允许合法的访问通过,比如:某表的 id 从 0 开始自增,而用户访问了 id 为-1 的数据,则缓存中不可能存在这样的数据,就一定会产生数据库查询。我们在接口层加入了访问 id 的校验,对于不合法的 id 直接返回默认值/空值,则避免了这一种情况。

其次是当合法访问了不存在的数据时,在首次查询该数据不存在时,为缓存赋值为默认值,下次再访问此数据时则直接从缓存返回默认值(或者错误信息等特殊值)。

缓存击穿

缓存击穿是指缓存在失效瞬间,对系统某些数据进行大量并发请求,此时就会造成对缓存击穿,造成大量的数据库访问。

要应对缓存击穿的问题,一般的做法是在查询时使用互斥锁,这样在大量并发时,针对某个数据,只会产生一个查询,其他线程在访问时只会等待,当第一个查询完成后,更新缓存,解锁。后面的请求就能从缓存中获得数据了。

缓存雪崩

缓存雪崩与缓存击穿类似,指的是缓存过期时,重建缓存时造成的性能急剧下降,并且在旧缓存失效、新缓存还未生成的时候,大量的并发访问又会加剧系统的性能压力。 要解决缓存雪崩的问题,也可以通过加锁的方式,通过加入更新锁,这样在同一时间只有只会有一个查询线程,其他线程只能等待或者先返回默认值/空值。另外对于分布式的系统,则需要用到分布式锁。

除了使用更新锁,还可以考虑定时地后台更新。区别于业务系统访问时才更新缓存(按需缓存)。后台更新一般比较消耗内存,有一定的可能性会出现内存不够的情况,这时候就会丢弃一部分缓存。

对此,可以使用消息队列实现一个按需的后台更新:后台接收更新消息后,检查相应的缓存是否有效,若无效则执行更新操作,否则什么都不做。

另外,还可以对于每次更新的缓存数据,针对每个数据的过期时间添加一定范围的随机值,使数据不至于在同一时间大规模失效。

缓存热点

缓存热点指的是某些热点数据访问量特别大,以至于缓存服务器的压力也特别大,比如明星热搜,短时间内有上千万的访问量。这时候单台的缓存服务器就不够了,需要引入分布式的缓存,然后针对热点缓存进行分布式存储,读取时随机分配缓存服务器以缓解访问压力。

总结

缓存穿透——业务查询了数据库中没有的数据,造成每次查询该数据都会直接访问数据库

缓存击穿——对于某一个失效的缓存数据,在它重建完成之前以大量的并发访问他,造成了大量的直接查询数据库

缓存雪崩——当缓存数据大规模失效时,大部分业务的访问都会直接访问数据库

本文总阅读量