超高速缓存的最佳实践

标签: Web开发 缓存 | 发表时间:2014-03-25 00:00 | 作者:踏雁寻花
出处:http://www.importnew.com

Performance Zone这个社区是由 New RelicAppDynamics来维护的。这两个人都是APM社区的用户,APM社区有很多的知名的用户,并且能够为这些用户减少很多的成本。

定制高速缓存解决方案是一件非常有趣的事情,它似乎是改善应用程序整体性能的最简单的方式。然而,超高速缓存是一项很大的技术难题,在实践之前需要注意几个事项。

最佳范例

1、key/value集合并不是缓存

几乎我做过的所有项目都用到了一些定制高速缓存解决方案,这些方案都是使用的Java Maps。然而Map并不是缓存的解决方案,因为可能缓存超出了一个key/value的存储容量。缓存还需要满足以下特点:

  • 驱逐策略(eviction policies)
  • 最大容量限制(max size limit)
  • 持久性存储(persistent store)
  • 弱引用建(weak references keys)
  • 统计(statistics)

Java Map并不能提供上述的特点,你也不应该花费你客户的钱去定制缓存方案。你应该选择一个更好的缓存技术,比如 EHCacheGuava Cache,这两种缓存技术都是非常强大的,而且用起来也非常的简单。这些工具经常被一些项目用来测试,所以,代码质量相比与其他的定制方案更加优秀。

2、使用一个缓存抽象层

Spring提供的缓存抽象层是一套非常灵活的方案。 @Cacheable注解可以将业务逻辑层的代码从缓存横切关注点分离开来。缓存解决方案是可以通过配置文件进行配置的,所以它不会破坏业务层的方法。

3、谨防缓存的开销

每一个api接口都是需要计算成本的,而缓存也不例外。如果你缓存一个web服务或者是一个开销比较大的数据库操作,那么这种开销可以忽略不计。如果在一个递归算法中使用本地缓存,那么就需要考虑缓存解决方案的开销了。甚至Spring的缓存抽象层都是有开销的,所以一定要确保收益大于成本。

4、如果数据库查询操作非常慢,那么缓存可能是最后的解决方案了。

如果使用类似Hibernate的 ORM工具,那么它就是首先要考虑进行优化的位置。确保抓取策略( fetching strategy)被正确的设计,这样才不会面临N+1的查询问题( N+1 query problems),你同样需要对SQL语句数进行断言操作( assert the SQL statement count,译者译:包括对增删改查操作的断言),以验证ORM生成的查询语句是否存在问题。

当你对ORM生成的SQL语句进行优化之后,你需要再一次检查数据库查询速度是否还是那么慢。同时要确保所有的索引都用上了,这样你的SQL查询才会非常高效。索引必须要全部都放在内存中,不然就会浪费更多的SSD或者HDD硬盘空间了。

你的数据库是可以缓存查询结果的,一定要利用好这个特点。

如果数据集是很大的,并且数据增长的速度也是非常快的,那么你就需要按比例将这些数据分配到多个数据库碎片( shards)中。

如果这样都不能够解决你的问题,那么就考虑换一个更加优秀的缓存解决方案吧,比如 Memcached

5、是否会影响到数据一致性呢?

当你在业务层之前使用缓存,数据一致性的约束是非常难做到的。如果缓存不能同步到数据库,那么 ACID的特性就会受到影响。如果一个根实体改变了数据,那么将会影响到很大一部分的缓存。如果你抛弃缓存实体,那么所有由缓存带来的效益将会失去。如果你异步更新了缓存实体,就会影响到数据的一致性, 最终一致)的数据模型也就不存在了。

让我们来看看实例吧

受关于Java 8 computeIfAbsent Map这篇 文章的启发,我写了一个 Guava Cache缓存方案,有下面几个优点:

  1. 有一个固定大小的缓存(2条记录)
  2. 在jdk1.6下运行
    private LoadingCache<Integer, Integer> fibonacciCache = CacheBuilder.newBuilder()
            .maximumSize(2)
            .build(new CacheLoader<Integer, Integer>() {
                public Integer load(Integer i) {
                    if (i == 0)
                        return i;
                    if (i == 1)
                        return 1;
                    LOGGER.info("Calculating f(" + i + ")");
                    return fibonacciCache.getUnchecked(i - 2) + fibonacciCache.getUnchecked(i - 1);
                }
            });
     
    @Test
    public void test() {
        for (int i = 0; i < 10; i++) {
            LOGGER.info("f(" + i + ") = " + fibonacciCache.getUnchecked(i));
        }
    }

输出为:

    INFO  [main]: FibonacciGuavaCacheTest - f(0) = 0
    INFO  [main]: FibonacciGuavaCacheTest - f(1) = 1
    INFO  [main]: FibonacciGuavaCacheTest - Calculating f(2)
    INFO  [main]: FibonacciGuavaCacheTest - f(2) = 1
    INFO  [main]: FibonacciGuavaCacheTest - Calculating f(3)
    INFO  [main]: FibonacciGuavaCacheTest - f(3) = 2
    INFO  [main]: FibonacciGuavaCacheTest - Calculating f(4)
    INFO  [main]: FibonacciGuavaCacheTest - f(4) = 3
    INFO  [main]: FibonacciGuavaCacheTest - Calculating f(5)
    INFO  [main]: FibonacciGuavaCacheTest - f(5) = 5
    INFO  [main]: FibonacciGuavaCacheTest - Calculating f(6)
    INFO  [main]: FibonacciGuavaCacheTest - f(6) = 8
    INFO  [main]: FibonacciGuavaCacheTest - Calculating f(7)
    INFO  [main]: FibonacciGuavaCacheTest - f(7) = 13
    INFO  [main]: FibonacciGuavaCacheTest - Calculating f(8)
    INFO  [main]: FibonacciGuavaCacheTest - f(8) = 21
    INFO  [main]: FibonacciGuavaCacheTest - Calculating f(9)
    INFO  [main]: FibonacciGuavaCacheTest - f(9) = 34

完整代码可以在GitHub上获得。

相关文章

相关 [高速缓存 最佳实践] 推荐:

超高速缓存的最佳实践

- - ImportNew
Performance Zone这个社区是由 New Relic 和 AppDynamics来维护的. 这两个人都是APM社区的用户,APM社区有很多的知名的用户,并且能够为这些用户减少很多的成本. 定制高速缓存解决方案是一件非常有趣的事情,它似乎是改善应用程序整体性能的最简单的方式. 然而,超高速缓存是一项很大的技术难题,在实践之前需要注意几个事项.

jQuery最佳实践

- andi - 阮一峰的网络日志
上周,我整理了《jQuery设计思想》. 那篇文章是一篇入门教程,从设计思想的角度,讲解"怎么使用jQuery". 今天的文章则是更进一步,讲解"如何用好jQuery". 我主要参考了Addy Osmani的PPT《提高jQuery性能的诀窍》(jQuery Proven Performance Tips And Tricks).

PHP最佳实践

- xiangqian - 阮一峰的网络日志
虽然名字叫《PHP最佳实践》,但是它主要谈的不是编程规则,而是PHP应用程序的合理架构. 它提供了一种逻辑和数据分离的架构模式,属于MVC模式的一种实践. 我觉得,这是很有参考价值的学习资料,类似的文章网上并不多,所以一边学习,一边就把它翻译了出来. 根据自己的理解,我总结了它的MVC模式的实现方式(详细解释见译文):.

MongoDB最佳实践

- - NoSQLFan
将 MongoDB加入到我们的服务支持列表中,是整个团队年初工作计划中的首要任务. 但我们感觉如果先添加一项对NoSQL存储的支持,而不是先升级已支持的关系型数据库,可能对用户不太好,毕竟目前的用户都使用关系型数据库. 所以我们决定将引入MongoDB这项工作放到升级MySQL和PostgreSQL之后来做.

Dockerfile 最佳实践

- - DockOne.io
在容器领域,Docker 公司提出的容器镜像已经成为目前容器打包交付的事实标准. 构建镜像需要编写 Dockerfile,如何编写一个优雅的 Dockerfile 呢. 在 Docker 公司的官方文档中给出了一篇:《 Best practices for writing Dockerfiles》.

文章: Grails最佳实践

- - InfoQ cn
我在IntelliGrape工作,这是一家专门使用Groovy & Grails进行开发的公司. 本文是我们Grails项目遵循的最佳实践的基本清单,收集自邮件列表、Stack Overflow、博文, 播客和 IntelliGrape的内部讨论. 它们分为控制器、服务、Domain、视图、TagLib、测试和其他.

PHP最佳实践(译)

- - CSDN博客Web前端推荐文章
原文:  PHP Best Practices-A short, practical guide for common and confusing PHP tasks. 译者: youngsterxyf. 本文档最后审阅于2013年3月8日. 由我, Alex Cabal,维护该文档. 我编写PHP程序已有很长一段时间了,当前我 经营着 Scribophile,由认真作家组成的一个在线写作团体,  Writerfolio,为自由职业者提供的一个易用写作工具集,以及  Standard Ebooks,一个图文并茂、无数字版权管理的公共领域电子书出版商.

Log4j最佳实践(原) - Mainz

- - 博客园_Mainz's Blog
本文是结合项目中使用 Log4j总结的最佳实践,非转载. 网上可以找到的是这一篇《 Log4j最佳实践》. 本来 Log4j使用是非常简单的,无需多介绍其用法,这只是在小型项目中;但在 大型的项目中使用 log4j不太一样. 大型项目非常依赖日志,因为解决线上问题必须依靠log,依靠大量的日志.

再谈RestAPI最佳实践

- - 企业架构 - ITeye博客
http://www.javacodegeeks.com/2014/05/rest-api-best-practices-reloaded.html ,仅供学习和参考,转载请注明出处. 近一年半,我参与了2到3个项目的工作,这些项目涉及到大量供“外部”使用的Rest API,稍后我们再来解释为什么要将“外部”这个词放在引号之中.

一些 REST 最佳实践

- - 鸟窝
原文: Some REST best practices, 作者: Pierre-Olivier Bourgeois. 译文: 一些REST最佳实践, 译者: yongx. 如今,REST APIs 已经非常普遍,几乎所有WEB应用都用到了它们. 提供简单,一致,实用的API是种义务,方便其它人很容易的使用.