分享 Web 应用的缓存设计模式

huacnlee · 2013年03月08日 · 最后由 hlxwell 回复于 2013年03月13日 · 6311 次阅读
本帖已被管理员设置为精华贴

good,已经收藏

和公司 DBA 经常提醒的基本吻合~有图有真相,看了源码和设计,忍不住想在自己的系统中试试~

文中说到n + 1 才能有效利用ORM缓存,这个真是学习了

到了那个级别数据量,发现关系数据库就是当非关系数据库使的。

不错,已加入阅读列表

文章不错。写了 read-thru, write-thru, write-behind 三种

还有一个策略是提前计算好全部 cache,然后在读之前就更新过期 cache

这篇文章不错,学到了很多,就是没有这么大的数据量可以实践,不过随时可以在新项目中应用这个新思路。

想方设法高效利用内存来减少 IO 的读取。对 n + 1 查询增加上 id 与二级缓存 应用很醒脑啊

好文..收藏

n+1 不是重点吧 我觉得重点是

  • 设计上去关系化
  • 避免多表查询
  • 单表主键查询

rails4 最大量缓存 就没有毕要 n+1 了,同意 n+1 的观点

吐槽无力,完全无力苟同 n+1 有利于缓存的观点。

@bhuztez 我认为他说的 n+1 是指多进行单表主键查询 如果没有二级缓存的情况下 细查询粒度 数据库自身的查询缓存的利用率也会提高

#13 楼 @jasl 基本上每个点都有值得怀疑的地方。就算大体按他这个思路,我们现在来看一下 n+1 那个点。

明明是可以 1+1 的,也不用拆成两张表的。所有 ID 都已知啊,直接一条 SELECT 不就完事了。1 条和 n 条对数据库没有本质区别。且 n 条多浪费 n-1 次 round trip。

一边说要省I/O,拆表增加了I/O负担就不提了。特别是他这个例子里,拆跟没拆没啥区别的。

其实嘛,大谈数据库,还是要抠那么点性能的,连SELECT *出现都不给理由的,这篇文章肯定是没用心写。吐槽真的很无力。

好吧,我还是来恶心他一句,从 2007 年到 2013 年,一点点长进都没有啊,我都不想为他捉急了

#14 楼 @bhuztez

  1. 又扯SELECT *.. http://ruby-china.org/topics/5695
  2. 你说的对, 1 + 1是可以解决问题的,就是 Rails 推荐的标准做法Eager Loading,把另一条转化成一个 IN 查询。但是这种 IN 查询在应用层是不容易缓存的。结果是这种 IN 查询每次都 Hit DB。而N + 1的 N 条查询每条是简单的主键查询,不用任何额外的维护,利用现有的插件就可以维护缓存的一致性。

#16 楼 @hooopo

  1. 反正你的理由不够有说服力。且此处不用SELECT *显然不用拆表...

  2. 有能支持 N+1 不能证明没有能支持 1+1 的啊

#17 楼 @bhuztez

从理论上讲,一个 web 系统可以做缓存优化的地方太多了。而你单揪着SELECT *不放一点意思也没有,况且,那是个费力未必讨好的活儿。

pro tip:

If you really want to make a point about the performance of a particular thing, can you at least pretend you're using science?
19 楼 已删除
20 楼 已删除

N+1 为的是可以直接在 View 里面存取缓存,让缓存的颗粒度更小

#18 楼 @hooopo SELECT *是公认的 bad practise。只要你没理由,是不该用SELECT *的。SELECT *首先是让代码维护变得困难,其次是有性能损失。

难道在这里SELECT *一点问题都没有吗?就是你用了该死的SELECT *你才要拆表。

#21 楼 @huacnlee 不拆表,1+1 也可以达到相同效果。完全不能苟同这种说法

#22 楼 @bhuztez 有几个表就是拆小做成内存表查询的,速度刚刚的。不过 bhuztez 大大是看不起这类庶民方法的啦。

#24 楼 @Rei 不要扯开去啊,就这个例子,把 blog contents 拆表,这拆跟没拆有什么区别么...

#25 楼 @bhuztez 或者你手头有大数据可以测一测。决定数据库模式的不是我,当时领我的组长把那 9000 万数倒来倒去、拆来拆去,后来发现这样弄比较快,就这样做了。

我个人不喜欢因为性能问题拆表,应该按逻辑分,所以我不想再碰大数据了。

数据量上去之后,关系数据库的 join 查询都不能用,热数据要冗余,大部分是主键查询……经历那一次后,我更加坚定用 Mongodb 了。

#26 楼 @Rei 我不是说不该为了性能原因拆表什么的。把拆表这个问题无限制扩大范围是得不出什么结论的,最多就是根据应用类型来决定,那就是一句废话。

我是说 robbin 这个例子拆跟不拆没什么区别,可能还是不拆好....这里根本就没给出任何理由。整篇下来基本就这么个思路。现在采用的这种方式比最烂的好很多了,所以这是一种很好的方式。

#28 楼 @bhuztez 我也没测过 select col, col2 是不是跟拆分后的 select * 性能一样,按 robbin 的说法应该后面的好。这就留给有心人啦,我用 Mongodb。

再说一个,为什么要用 select * 呢,一条数据在不同的位置可能要调用不同的字段,全部取出来就可以缓存在内存,不同地方 select 不同的 col 缓存效率就低。当然也可以通过页面片段缓存,实际哪个好要看场合了。

#27 楼 @Rei

首先是一句废话,用什么数据库取决于你的应用类型。

为什么要用 RDBMS,是因为数据本身之间存在关系啊。数据关系处理起来是很难的。要应用自己保证 ForeignKey Constraint 还是太麻烦了,所以才会出现各种 RDBMS。这也是为什么你总是要先 normalize 找出所有关系。

数据量上去之后,关系数据库的 join 查询都不能用

不是的。假如你知道你需要哪些 JOIN。Oracle 的 Table Cluster 可以把相关数据放在一起,这样即便是一个很大的 cluster,也是可以 JOIN 的。Google 开发的 F1 也是这么干的。

我更加坚定用 Mongodb 了

MongoDB 解决的完全不是同一类问题。你用 MongoDB,你对数据的假设是,对于任意一条记录,任何地点都可能对它发起写请求,且不存在某个地点发起写请求的次数特别多,此时你想要保证多数地点要可写。对,默认你已经跨地理分布的多个数据中心了。

#30 楼 @Rei

  1. 不是只有select *才能把所有字段取出来。
  2. 这里就是少取一个字段即可避免拆表

robbin 的这篇文章很赞,将 Rails 中各种 cache 的最佳实践都很详细地总结了一次,强烈推荐。

另外,我在微博上提到过:Rails 的 ActiveRecord 在 3.1 以后,对于 n+1 有更先进的处理,通过 Eager Loading http://t.cn/zYnMxad 会转化成 in 查询,变成 1+1,而且 in 查询可以通过 cache server 的 multiget 方式来批量命中。

fork 了那个 cache,正在看如何改进这个。

#31 楼 @bhuztez 这里说的应用类型就是完整性要求不严格的 Web 应用,Startup 也买不起 Oracle,所以我也只说我的选择罢了。

36 楼 已删除

#36 楼 @zhulinpinyu 不懂 rspec。在原贴 @ 就行了不要窜主题。

我表示觉得 robbin 这篇文章很强大,同时盼望 @bhuztez 写出同样强大文章,但具有不同的观点

#3 楼 @ywjno n+1 有效使用缓存,记得几年前,robinfan 在 KongfuRails 里就讲过了

需要 登录 后方可回复, 如果你还没有账号请 注册新账号