如何通过缓存来提升系统性能

李绮
• 阅读 544

缓存

在系统中最消耗性能的地方就是对数据库的访问了,一般来说,增、删、改操作不会出现什么性能问题,除非索引太多,并且数据量有十分庞大的情况下,这三个操作才会导致性能问题。一般可以限制单表索引的数量来提升性能,比如单表的索引数量不能超过5个。

绝大多数情况下,性能问题都出在查询上,select操作提供了非常丰富的语法,这些语法包括函数,子查询,like子句,where子句等,这些查询都是非常消耗性能的。大部分应用都是读多写少的应用,所以查询慢的问题会被放大了,导致慢查询成为了系统性能的瓶颈,这是就需要用到缓存来提高系统的性能。

缓存为什么能提供系统性能?

缓存通过减少系统对数据库的访问量来提高系统性能。试想一下,如果有100个请求同时请求同一个数据,没有加缓存之前,需要访问100次数据库,而加了缓存之后,可能只需访问1次数据库,剩下的99个请求的数据从缓存中取,大大的较少了数据库的访问量,虽然同样需要访问100次,但数据库的读取性能和缓存的读取性能不在一个级别上,所以对系统性能提升显著。为什么说可能需要访问1次数据库呢,这个和过期有关,后面会讲。

更新模式

既然知道了缓存对系统性能提升显著,那下面先来了解一下缓存如何更新吧。

Cache Aside(推荐)
这应该是最常用的更新模式了,这种模式大致流程如下:

读取

  • 如果缓存中没有,则再从数据库中读取数据,得到数据之后,放入缓存。
  • 如果缓存中有,取到后直接返回。

更新

  • 先更新数据库里的数据,成功后,让缓存失效。

为什么是让缓存失效而不是更新缓存呢?

主要是因为两个并发写操作导致脏数据。试想一下,有两个线程A和B,分别要将资源A的值修改为1和2,线程A先到达数据库把数据更新为1,但还没更新缓存,由于时间片用完了,此时线程B获得了CPU,并在这期间把数据库资源A的值和缓存的值都更新为2,线程B结束后,线程A重新获得CPU,执行更新缓存,把资源A的值改为1,线程A结束。此时,数据库中A的值为2,而缓存中A的值为1,数据不一致。所以让缓存失效就不会有这个问题,保证缓存中的数据和数据库的保持一致。

那是不是Cache Aside模式就不会有并发问题了呢?

不是的。比如,一个读操作,没有命中缓存,就去数据的读取数据(A=1),此时一个写操作,更新数据库数据(A=2)并让缓存失效,此时读操作把读取到的数据(A=1)写到缓存中,导致脏数据。

这种情况理论上会出现,但现实情况中出现的几率极低。要这种情况出现必须在一个读操作发生时,有一个并发写操作,并且既要读操作要于写操作写入前读取,又要后于写操作写入缓存。满足这种条件的概率并不大。

基于出现上面所描述的问题,目前有两种比较合理的解决方案:

  • 通过2PC这种保证数据的一致性(复杂);
  • 通过降低并发时脏数据的概率,并设置合理的过期时间(简单,但存在一定时间内的错误率,一般可以接受)。

Read/Write Through

在这种模式下,对于应用程序来说,所有的读写请求都是直接和缓存打交道,关于数据库的数据完全由缓存服务来更新(更新同步为同步操作)。

这种模式下流程就相当简单了,完全就是对缓存的读写。

缺点:这种模式对缓存服务有强依赖性,要求缓存具备高可用性。所以应有没有上一种普遍。

Write Behind Caching

其实这个模式就是Read/Write Through的一个变种,区别就在于前者是异步更新,后者是同步更新。

既然是异步更新数据库,他的相应速度比Read/Write Through还要高,并且还能合并对同一个数据的多次操作。这个有点像MySQL的buffer pool的刷盘操作。

缺点:异步就代表数据不是强一致性的,还存在数据丢失的风险,实现逻辑也较为复杂。

设计思路

无状态的服务

在分布式系统中,无状态的服务有利于横向扩展,所以缓存也应该独立于业务服务在外,设计成一个独立的服务,使业务服务变成无状态的。很多公司都选择是用Redis来搭建他们的缓存系统,取决于其高速的读写性能。

命中率
一个缓存服务的好坏主要看命中率,一般来说,命中率保存在80%以上已经算很高了,但我们不能为了提高命中率而把数据库的全部数据的写到缓存了,这个是不符合缓存的设计理念,而且需要极大的内存空间。通常来说,应该只有小部分热点数据写到缓存。 缓存是通过牺牲强一致性来换取性能的,并不是所有的业务的适合使用缓存。

有效时间
缓存数据的有效时间不易过短,不易过长,不易过于集中。

  • 过短,会增加数据库访问的次数。
  • 过长容易不使用的数据一直停留在缓存中,浪费空间,并且一旦产生脏数据,过程的有效时间会导致脏数据迟迟无法失效,进而导致影响更多的业务。
  • 过于集中,会导致缓存雪崩。

淘汰策略
当内存不足时,缓存系统就要按照淘汰策略,把不适合留在缓存的数据淘汰掉,腾出位置给新数据。下面就以Redis为例,给出了淘汰策略:

  • noeviction: 不删除策略, 达到最大内存限制时, 如果需要更多内存, 直接返回错误信息(有极少数会例外, 如 DEL )。
  • allkeys-lru: 所有key通用,优先删除最近最少使用(less recently used ,LRU) 的 key。(推荐)
  • volatile-lru: 只限于设置了 expire 的部分,优先删除最近最少使用(less recently used ,LRU) 的 key。
  • allkeys-random: 所有key通用,随机删除一部分 key。
  • volatile-random: 只限于设置了 expire 的部分,随机删除一部分 key。
  • volatile-ttl: 只限于设置了 expire 的部分,优先删除剩余时间(time to live,TTL) 短的key。

可根据项目实际情况进行选择。

小结

缓存是为了加速数据的访问,在数据库之上的一直机制,并非所有业务都适合使用缓存,要根据具体情况选择更新策略和淘汰策略。

来源:blog.csdn.net/qq_36011946/article/details/104164031

点赞
收藏
评论区
推荐文章
Johnny21 Johnny21
4年前
MySQL8.0和MySQL5.7的区别
隐藏索引在8.0中,索引可以隐藏。隐藏索引对性能调试非常重要,索引可以被隐藏和显示,当一个索引隐藏时,不会被查询优化器所使用。隐藏一个索引,然后观察数据库性能是否下降,如果下降,说明该索引有效,否则无效,可以删除。隐藏索引语法:ALTERTABLEtALTERINDEXiINVISIBLE恢复索引语法:AL
Easter64 Easter64
4年前
MySQL语句优化
在MySQL数据库怎么加快查询速度,优化查询效率,主要原则就是应尽量避免全表扫描,应该考虑在where及orderby涉及的列上建立索引。  建立索引不是建的越多越好,原则是:  第一:一个表的索引不是越多越好,也没有一个具体的数字,根据以往的经验,一个表的索引最多不能超过6个,因为索引越多,对update和insert操作也会有性能的影响,涉及
Easter79 Easter79
3年前
TiDB 5.0 RC Release Notes
TiDB5.0.0rc版本是5.0版本的前序版本。在5.0版本中,我们专注于帮助企业基于TiDB数据库快速构建应用程序,使企业在构建过程中无需担心数据库的性能、性能抖动、安全、高可用、容灾、SQL语句的性能问题排查等问题。在TiDB5.0版本中,你可以获得以下关键特性:开启聚簇索引功能,提升数据库的性能。例如:TPC
Wesley13 Wesley13
3年前
MySQL 大表优化方案(长文)
当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化:单表优化除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑、部署、运维的各种复杂度,一般以整型值为主的表在千万级以下,字符串为主的表在五百万以下是没有太大问题的。而事实上很多时候MySQL单表的性能依然有不少优化空间,甚至能正常支撑千万级以上的数据量
Wesley13 Wesley13
3年前
560字带你彻底搞懂:MySQL的索引优化分析
正文一、SQL分析性能下降、SQL慢、执行时间长、等待时间长查询语句写得差索引失效关联查询太多join(设计缺陷)单值索引:在user表中给name属性创建索引,createindexidx\_nameonu
Wesley13 Wesley13
3年前
MySQL · 性能优化 · MySQL常见SQL错误用法
1\.LIMIT语句分页查询是最常用的场景之一,但也通常也是最容易出问题的地方。比如对于下面简单的语句,一般DBA想到的办法是在type,name,create\_time字段上加组合索引。这样条件排序都能有效的利用到索引,性能迅速提升。SELECTFROMoperationWHEREty
Wesley13 Wesley13
3年前
oracle 优化方法总结
分析和优化的基本步骤如下:1、如果是SQL语句的写法问题,我们可以通过在不更改业务逻辑的情况下改写SQL来加以解决;2、如果是不必要的全表扫描/排序而导致了目标SQL的性能问题,我们可以通过建立合适的索引(包括函数索引、位图索引等)来加以解决;3、如果是表或者索引的不良设计导致的目标SQL的性能问题,我们可以通过重新设计表/索引
Wesley13 Wesley13
3年前
mysql中的分区表
分区表的意义:当数据量非常大时(表的容量到达GB或者是TB),如果仍然采用索引的方式来优化查询,由于索引本生的消耗以及大量的索引碎片的产生,查询的过程会导致大量的随机I/O的产生,在这种场景下除非可以很好的利用覆盖索引,否则由于在查询的过程中需要根据索引回数据表查询,会导致性能受到很大的影响,这时可以考虑通过分区表的策略来提高查询的性能。
Wesley13 Wesley13
3年前
mysql学习 索引
  在平时开发过程中写sql时,我们通常都不太关心sql的性能,只有能给查出来数据,sql的执行速度不是太慢就不会去管它了。但是开发时期的数据量往往都不是太大,很多性能问题只有在生产环境中才会发现,如:数据过多、sql关联了太多的表,使用了太多的join、或者建立了索引,但是索引失效的问题。所以要解决这些性能上的难题,就要去研究mysql最为重要的特性索
Stella981 Stella981
3年前
Redis缓存的基本思想
1.缓存的基本思想很多朋友,只知道缓存可以提高系统性能以及减少请求相应时间,但是,不太清楚缓存的本质思想是什么。缓存的基本思想其实很简单,就是我们非常熟悉的空间换时间。不要把缓存想的太高大上,虽然,它的确对系统的性能提升的性价比非常高。其实,我们在学习使用缓存的时候,你会发现缓存的思想实际在操作系统或者其他地方都被大量用到。比如C
Sql优化之回表
前言:MySQL的性能是大家在使用时十分关心的问题,比如在高并发访问时,并且有慢sql存在的情况下,MySQL的性能会明显下降,这会导致数据库响应时间变慢,甚至导致数据库宕机。那么为了避免Mysql性能问题,比较常用的方式创建适当的索引,提升sql语句的执
李绮
李绮
Lv1
思归若汾水,无日不悠悠。
文章
3
粉丝
0
获赞
0