MySQL 之外的优化

对于整个应用来说,数据库的优化只是其中一部分,还有很多与数据相关,但与 MySQL 无关的优化。

常见问题

  • 获取过量的数据:例如获取 1000 行数据,但只显示 10 行
  • 让数据库或应用处理不擅长的工作:例如获取所有的数据,然后在应用中统计行数;或者在数据库中执行复杂的字符串操作
  • 执行太多次查询:例如不采用关联查询,而是在应用中通过层层嵌套来获取相关内容。
  • 没必要的 MySQL 连接:如果可以从 MySQL 之外的缓存获取数据,就不要连接 MySQL 服务器了
  • 连接池和长连接是否正确使用?连接池一般是较好的方式,但一般不建议设置为连接池可以自动扩展,而是应当在连接池满的时候,让连接请求进行排队,这样就能避免连接数太多而过载。

缓存

对于高负载的应用,会有很多层的缓存,这包括 MySQL 服务器中、应用服务器中、Web 服务器中、浏览器中等。缓存越接近客户端,就越节省资源并且效率更高。

缓存一般可以分为两类:被动缓存和主动缓存。

  • 被动缓存在未命中的情况下直接返回 null,典型例子是 memcached
  • 主动缓存在未命中的情况下会请求结果,然后缓存和返回该结果

通常,我们希望缓存是主动的,这样对于应用来说就是「透明」的,因为这样应用就不需要亲自去处理缓存未命中的情况。

应用缓存有许多种:

  • 本地缓存:例如缓存某个用户的 ID,这个时候缓存仅仅就是个局部变量
  • 本地共享内存缓存:例如对象字段中的某个 LinkedHashMap,用来作为 LRU 来缓存最近使用的数据,这个缺点就是不适用于有多个实例的分布式应用中。
  • 分布式内存缓存:例如 memcached,多个服务器实例只使用同一份缓存,不存在数据冗余、同步更新等问题。
  • 磁盘上的缓存:适用于像图片、文档这一类很难全部装进内存的对象或者静态内容

对于缓存,我们需要设计缓存控制策略,以下是常见的策略:

  • TTL(time to live):也就是设置过期时间
  • 写失效或者写更新:也就是在写操作的时候,标记缓存失效或者直接更新缓存,这种方式在批量写入的时候会导致大量的缓存更新操作
  • 读失效:这种方式是在读的时候用一些额外的信息来判断缓存是否实现,在写的时候则什么都不做,最简单的实现方式是采用对象版本控制,也就是用自增的 version 来记录改动,读的时候判断 version 是否改变。

除此之外,我们还可以使用预生成内容来代替缓存,常见的就是网站首页生成静态页面。

对于 memcached 这类作为基础组件的缓存,我们要特别注意它的高可用性,因为一旦该组件失效,会导致「雪崩」,这个时候大量缓存失效,需要更新缓存,就会瞬时对数据库服务器造成很大的压力。

MySQL 的替代品

MySQL 不是数据存储的万金油,在某些领域,我们应当选择合适的替代品:

  • 图像文件或其他大型二进制文件:虽然我们可以把它们放到 BLOB 列,但是还是更适合放在文件系统中,数据库中只存文件名,然后在 MySQL 之外进行存取文件
  • 全文检索:MySQL 在这方面不如 Solr 和 Lucene 这类搜索引擎
  • 简单的键值存储以及队列:Redis 是很好的替代品
  • 大数据:Hadoop 等

参考资料:「高性能 MySQL」