有感于Web应用的缓存设计模式这篇文章里面的缓存使用设计。
其实缓存方面确实可以大做文章,看看这位大牛的理念
- 以减少数据库服务器磁盘IO为最终目的,而不是减少发送到数据库的SQL条数。实际上使用ORM,会显著增加SQL条数,有时候会成倍增加SQL。
- 数据库schema设计的取向是尽量设计 细颗粒度 的表,表和表之间用外键关联,颗粒度越细,缓存对象的单位越小,缓存的应用场景越广泛
- 尽量避免多表关联查询,尽量拆成多个表单独的主键查询,尽量多制造
n + 1
条查询,不要害怕“臭名昭著”的n + 1
问题,实际上n + 1
才能有效利用ORM缓存
拆分n+1条查询的方式,看起来似乎非常违反大家的直觉,但实际上这是真理,我实践经验证明:数据库服务器的瓶颈往往是磁盘IO,而不是SQL并发数量。因此 拆分n+1条查询本质上是以增加n条SQL语句为代价,简化复杂SQL,换取数据库服务器磁盘IO的降低 当然这样做以后,对于ORM来说,有额外的好处,就是可以高效的使用缓存了。
这里我看到了这样的一个新概念,就是“数据库的瓶颈往往是磁盘IO,而不是SQL的并发数量”;这个说法我也不知道是否正确的,没有经历过百万千万数据级别的研究与维护;不过可以学习一下。
确实如同该博主所说,对于一个没有太多访问量的网站有些过度设计,不过如果用户或数据量突然飙升情况下可以以较少的代码更改,实现更高的承载力;当然了,在网站最初设计时,这些因素也应当考虑进去;之前我做的mpf就是没有考虑这些问题,导致高峰时期(1w+ 并发)都访问一个专题页面内容,而次次几乎都会读取数据库服务器,差点导致宕机,后来对于专题内呈现的非动态数据内容都进行了memcached缓存,这样应该好多了。
从数据读取已经ORM有效性来说,最小化schema表设计确实很有效果
1、主键查询mysql表会buffer数据缓存。
2、ORM关系很透明明了
3、ORM model对象可以直接进行数据缓存(目前公司有一个项目就是如此)
如一个微博系统,微博一张表,微博附属信息一张表,微博插入的音乐,图像,视频,评论登录都是独立一张表,全部用id与微博主表关联起来。
按照column拆表实现细粒度对象缓存
数据库的瓶颈往往在磁盘IO上,所以应该尽量避免对大表的扫描。传统的拆表是按照row去拆分,保持表的体积不会过大,但是缺点是造成应用代码复杂度很高;使用ORM缓存的办法,则是按照column进行拆表,原则一般是:
- 将大字段拆分出来,放在一个单独的表里面,表只有主键和大字段,外键放在主表当中
- 将不参与where条件和统计查询的字段拆分出来,放在独立的表中,外键放在主表当中
按照column拆表本质上是一个去关系化的过程。主表只保留参与关系运算的字段,将非关系型的字段剥离到关联表当中,关联表仅允许主键查询,以Key-Value DB的方式来访问。因此这种缓存设计模式本质上是一种SQLDB和NoSQLDB的混合架构设计
数据库设计分表这个应该很常见了,常用字段一个表,非常用字段可以另一个表;同时大数据字段单独一表。
数据一致性问题
使用缓存,必须要考虑数据一致性问题。写入保持同步即可,在读取时需要首先检索cache里面有没有这样的数据,有着读取,无则读取数据并写入缓存;
主要麻烦的地方就是在于update与delete,在原数据信息更新时,缓存数据也必须更新了,否则数据会不一致。不论是内存缓存还是文件脚本缓存或是html页面缓存都是这样的。区别在于是程序主动更新还是用户访问的被动更新。
有些开源cms 采用的就是用户被动访问更新,有人访问;采取cache中去读取更新。不过我更喜欢程序主动更新。如上文博主采用的以文章update更新时间+id作为key来存储缓存信息,在文章原数据update之后,update更新时间一定会被修改,那么读取时cache中就没有了这样的文章内容了,但是原来的文章数据缓存还是存在的,只能等待cache服务器进行自动抛弃更新。这样的话cache中就可能会多很多冗余数据了,对于cache服务器检索查询一定有影响吧。
我的选择就是在文章数据更新时直接update cache或者是不update cache 但是一定把原文章 cache数据清除掉。
在用户删除文章时,先对cache里面的数据进行清除,然后进行数据库的原数据删除。
今天抽空把daynote.sinaapp.com 中的markdown转换后的数据那一层进行缓存,缓存时间是7200s,对于此类非公开的私密日记。用户访问查看后一般都会选择离开,不会时时刻刻进行刷新数据请求,7200s数据缓存搓搓有余了。
原先的xhzd.sinaapp.com也是使用大量的memcache缓存,其中列表都使用了;但是考虑到sae上面的memcache额度,字详细那一块没有缓存。目前几乎没什么访问量,也就没有进行缓存操作了。如果访问量大,那么可以把几乎整个字典都存入了缓存了。
SAE上面使用memcache的方法请看这里