当前位置: 首页 > news >正文

MySQL之应用层优化(三)

应用层优化

应用层缓存

  • 2.本地共享内存缓存
    这种缓存一般是中等大小(几个GB),快速,难以在多台机器间同步。它们对小型的半静态位数据比较合适。例如每个州的城市列表,分片数据存储的分区函数(映射表),或者使用存活时间(TTL)策略进行失效的数据等。共享内存最大的好处是访问非常快——通常比其他任何远程缓存访问都要快不少
  • 3.分布式内存缓存
    最常见的分布式内存缓存的例子是memcached.分布式缓存比本地共享内存缓存要大得多,增长也容易。缓存中创建的数据每一个比特都只有一份副本,这样既不会浪费内存,也不会因为相同的数据存在不同的地方而引入一致性问题。分布式内存非常适合存储共享对象,例如用户资料,评论,以及HTML片段。分布式缓存比本地共享缓存的延时要高得多,所以最高效的使用方法是批量进行多个获取操作(例如,在一次循环中获取多个对象)。分布式缓存还需要考虑怎么增加更多的节点,以及某个节点崩溃了怎么处理。对于这两个场景,应用程序必需决定在节点间怎么分布或重分布缓存对象。当缓存集群增加或减少一台服务器时,一致性缓存对避免性能问题而言是非常重要的。
  • 4.磁盘上的缓存
    磁盘是很慢的,所以缓存在磁盘上的最好是持久化对象,很难全部装进内存的对象,或者静态内容(例如预处理的自定义图片)。对于磁盘上的缓存和Web服务器,一个非常有用的技巧是使用404错误处理机制来捕捉缓存未命中的情况。假设Web应用要在头部展示一张基于用于名(“欢迎回来,John”)的自定义图片。并且通过/images/welcomeback/john.jpg这样的访问路径引用此图片。如果图片不存在,将会导致一个404错误,并且触发上述错误处理。这个错误处理可以生成图片,在磁盘上存储它,然后发出一个重定向或者将该图片传回浏览器。后续的请求只需要从文件中直接返回图片。有很多类型的内容可以使用这种技巧。例如,不用再将最近的标题作为HTML部分进行缓存,可以在Javascript文件中存储这些东西,然后在网页头重引用这个文件:latest_headlines.js.缓存失效很简单:删除文件即可。可以通过执行一个删除N分钟前所创建的文件的定时任务,来实现TTL失效。如果想要限制缓存大小,也可以通过按最近访问时间排序来删除文件,从而实现最近最少使用(LRU)失效算法。如果失效策略是基于最近访问时间,则必须在文件系统挂载参数中打开访问时间记录(忽略noatime选项即可),如果这么做,应该使用内存文件系统来避免大量磁盘操作。

缓存控制策略

缓存也有像反范式化数据库设计一样的问题:重复数据,也就是说有多个地方需要更新数据,所以需要想办法避免读到脏数据。下面是一些最常见的缓存控制策略:

  • 1.TTL(time to live,存活时间)
    缓存对象存储时设置一个过期时间;可以通过清理进程在达到过期时间后删掉最想,或者先留着直到下次访问时再清理(清理后需要使用新的版本替换)。对于数据很少变更或者没有新数据的情况,这是最好的失效策略
  • 2.显式失效
    如果不能接受脏数据,那么进程在更新原始数据时需要同时使缓存失效。这种策略有两个写——失效和写——更新。写——失效策略很简单:只需要标记缓存数据已经过期(是否清理缓存数据是可选的)。写——更新策略需要多做一些工作,因为在更新数据时就需要替换掉缓存项。无论如何,这都是非常有益的,特别是当生成缓存数据代价很昂贵时(写线程也许已经做了)。如果更新缓存数据,后续的请求将不在需要等待应用来生成。如果在后台做失效处理,例如基于TTL的失效,就可以在一个从用户请求完全分离出来的进程中生成失效数据的新版本
  • 3.读时失效
    在更改旧数据时,为了避免要同时失效派生出来的脏数据,可以在缓存中保存一些信息,当从缓存读数据时可以利用这些信息判断数据是否已经失效。和显式失效策略相比,这样做有很大的优势:成本固定且可以分散在不同时间内。假设要失效一个有一百万缓存对象依赖的对象,如果采用写时失效,需要一次在缓存中失效一百万个对象,即使有高效的方法来找到这些对象,也可能需要很长的时间才能完成。如果采用读时失效,写操作可以立即完成,但后续这一百万对象的读操作可能会有略微的延迟。这样就把失效一百万对象的开销分散了,并且可以帮助避免出现负载冲高和延迟增大的峰值。

一种最简单的读时失效的办法时采用对象版本控制。使用这种方法,在缓存中存储一个对象时,也可以存储对象所依赖的数据的当前版本号或者时间戳。例如,假设要缓存用户博客日志的统计信息,包括用户所发表的博客数。当缓存blog_stats对象时,也可以同一时间存储用户的当前版本号,因为该统计信息是依赖于用户的。
不管什么时候更新依赖于用户的数据,都需要更新用户的版本号,假设用户的版本号初始为0,并且由你来生成和缓存统计信息。当用户发表了一篇博客,就增加用户的版本好到1(当然也要同时存储这篇博客,尽管在这个例子并没有用到博客数据)。然后当需要显示统计数据的时候,可以对缓存中blog_stats对象的版本与缓存的用户版本进行比较。因为用户的版本比对象的版本高,所以可以直到缓存的统计信息已经过期了,需要重新计算。
这是一个非常粗糙的内容失效方式,因为它假设依赖于用户的每一个比特的数据与所有其他数据都有交互。但这个假设并不总是成立的。举个例子,如果一个用户对一篇博客做了编辑,你也需要增加用户的版本号,着就会导致存储的统计信息失效,而实际上统计信息(发表的博客数)并没真的改变。这个取舍是很简单的。一个简单的缓存失效策略不只是更容易创建,也可能更加高效。
对象版本控制是一种简单的标记缓存方法,它可以处理更复杂的依赖关系。一个标记的缓存可以识别不同类型的依赖,并且分别跟踪每个依赖的版本。在前面的图书俱乐部的例子中,你可以通过下面的版本好标记评论,使缓存的评论依赖于用户的版本和书的版本:user_ver=1234和book_ver=5678.任一版本号变了,都应该刷新缓存的评论

缓存对象分层

分层缓存对象对检索、失效和内存利用都有帮助。相对于只缓存对象,也可以缓存对象的ID、对象的ID组等通常需要一起检索的数据。电子商务网站的搜索结果是这种技术很好的例子。一次搜索可能返回一个匹配产品的列表,包括名称、描述、缩略图,以及价格。缓存整个列表的效率很低:其他的搜索也可能会包含一些相同的产品,这就会导致数据重复,并且浪费内存。这种策略也使得当一个产品价格变动时,找出并失效搜索结果变得很困难,因为你必须查看每个列表,找到哪些列表包含了更新过的产品。
可以缓存关于搜索的最小信息,而不必缓存整个列表,例如返回结果的数量以及列表中的产品ID。然后可以再单独缓存每个产品。这样做可以解决 两个问题:不会重复存放任何结果数据,也更容易在失效产品的粒度上去失效缓存。缺点则是,相对于一次性获得整个搜索结果,必须在缓存中检索多个对象。然而不管怎么说,为搜索结果缓存产品ID的列表都是更有效的做法。先在一个缓存命中返回ID的列表,再使用这些ID去请求缓存获得产品信息。如果缓存允许在一次调用里返回多个结果,第二次请求就可以返回多个产品(memcached通过mget()调用来支持)
如果使用不当,这种方法可能会导致奇怪的结果。假设使用TTL策略来失效搜索结果,并且当产品变更时显式地区失效单个产品。现在想象以下,一个产品地描述发生了变化,不再包含搜索中匹配地关键字,但是搜索结果地缓存还没有过期失效,此时用户就会看到错误地搜索结果,因为缓存的搜索结果将会引用这个变化了的产品,即使它不再包含匹配搜索的关键字。
对于大多数应用程序来说,这不是问题。如果应用程序不能容忍这种情况,可以使用基于版本的缓存,并在执行搜索时在结果中存储产品的版本好。当发现搜索结果在缓存中时,可以将当前搜索结果的版本号和搜索结果中的每个产品的版本号做比较。如果发现任何一个产品的版本数据不一致,可以重新搜索并且重新缓存结果。这对理解远程缓存访问的花销是多么昂贵非常重要。虽然缓存很快,也可以避免很多工作,但在LAN环境下网络往返缓存服务器通常也需要0.3ms左右。我们见过很多案例,复杂的网页需要一千次左右的缓存访问来组合页面结果,这将会耗费3s左右的网络延时,意味着你的页面可能慢得不可接受,即使它甚至不需要访问数据库!因此,在这种情况下对缓存使用批量获取调用是非常重要的。对缓存分层,采用小一些的本地缓存,也可能获得很大的收益

预生成内容

除了在应用程序几倍缓存位数据,也可以在后台预先请求一些页面,并且将结果存为静态页面。如果页面是动态的,也可以预先生成页面的部分内容,然后使用像服务端包含(SSI)这样的技术创建最终页面。这有助于减小生成预生成内容的大小和开销,否则可能在将不同部分拼装到最终页面的时候,由于微小的变化产生大量的重复内容。几乎可以对任何类型的缓存使用预生成策略,包括memcached.预生成内容有几个重要的好处。

  • 1.应用代码没有复杂的命中和未命中处理路径
  • 2.当未命中的处理路径慢得不可接受时,这种方案可以很好地工作,因为它保证了未命中的情况永远不会发生。实际上,在任何时候设计任何类型的缓存系统,总是应该考虑未命中的路径有多曼。如果平均性能提升很大,但是因为要预生成缓存内容,偶尔有一些请求变得非常缓慢,这时可能比不用缓存还糟糕。性能的持续稳定通常跟高性能一样重要
  • 3.预生成可以避免在缓存未命中时异常的雪崩效应
    缓存预生成号的内容可能占用大量空间,并且并不总能预生成所有东西。无论是哪种形式的缓存,需要预生成的内容中最重要的部分是哪些最经常被请求,或者生成的成本最高的,所以可以通过404错误处理机制来按需生成。预生成的内容有时候也可以从内存文件系统中获益,因为可以避免磁盘IO

相关文章:

  • 基于Android Studio外卖系统,订餐系统
  • 【python】OpenCV—QR Code
  • jenkins部署和使用
  • IT行业入门,如何假期逆袭,实现抢跑
  • 基于redisson实现tomcat集群session共享
  • 几种linux开机自启脚本的方法
  • c语言回顾-数组(全网最详细,哈哈哈)
  • Vue 3中 <script setup> 与生命周期钩子函数的详细解析
  • 软考-软件设计师
  • 海云安参编《数字安全蓝皮书 》正式发布并入选《2024中国数字安全新质百强》荣膺“先行者”
  • 深⼊理解MySQL Innodb存储引擎的缓冲池、事务、索引底层工作原理,掌握 MySQL 主从同步,读写分离技术以及集群的搭建,具备分库分表,SQL调优经验
  • Objective-C使用块枚举的细节
  • 如何构建智能聊天系统
  • Zigbee智能家居数据中心:微信小程序实时掌控家居传感器信息
  • Java面试题-锁
  • Service Worker
  • 第十八天-企业应用架构模式-基本模式
  • 码农张的Bug人生 - 初来乍到
  • 前端学习笔记之观察者模式
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用(一)
  • 实现简单的正则表达式引擎
  • 为视图添加丝滑的水波纹
  • 新手搭建网站的主要流程
  • 怎么将电脑中的声音录制成WAV格式
  • #【QT 5 调试软件后,发布相关:软件生成exe文件 + 文件打包】
  • #LLM入门|Prompt#1.7_文本拓展_Expanding
  • (4)通过调用hadoop的java api实现本地文件上传到hadoop文件系统上
  • (6)STL算法之转换
  • (el-Transfer)操作(不使用 ts):Element-plus 中 Select 组件动态设置 options 值需求的解决过程
  • (ZT)出版业改革:该死的死,该生的生
  • (二)c52学习之旅-简单了解单片机
  • (附源码)springboot 校园学生兼职系统 毕业设计 742122
  • (考研湖科大教书匠计算机网络)第一章概述-第五节1:计算机网络体系结构之分层思想和举例
  • (三)Kafka 监控之 Streams 监控(Streams Monitoring)和其他
  • (原創) 未来三学期想要修的课 (日記)
  • (原創) 系統分析和系統設計有什麼差別? (OO)
  • (转)es进行聚合操作时提示Fielddata is disabled on text fields by default
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • .net core 实现redis分片_基于 Redis 的分布式任务调度框架 earth-frost
  • .netcore 如何获取系统中所有session_如何把百度推广中获取的线索(基木鱼,电话,百度商桥等)同步到企业微信或者企业CRM等企业营销系统中...
  • .NET中的Event与Delegates,从Publisher到Subscriber的衔接!
  • @AutoConfigurationPackage的使用
  • [ 代码审计篇 ] 代码审计案例详解(一) SQL注入代码审计案例
  • [ 隧道技术 ] 反弹shell的集中常见方式(四)python反弹shell
  • [.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序...
  • [100天算法】-x 的平方根(day 61)
  • [C#]手把手教你打造Socket的TCP通讯连接(一)
  • [DAU-FI Net开源 | Dual Attention UNet+特征融合+Sobel和Canny等算子解决语义分割痛点]
  • [GYCTF2020]Ez_Express
  • [Hive] 常见函数
  • [Intel Edison开发板] 05、Edison开发基于MRAA实现IO控制,特别是UART通信
  • [noip2015 d1t2] 信息传递
  • [python]tkinker的GUI应用执行耗时长的任务
  • [Salesforce组件开发][LWC] 弹出框开发
  • [TLSR8266] 1、搭建tlsr8266编译框架在win服务器中