查看: 2874|回复: 2
打印 上一主题 下一主题

[技术方案] Yupoo(又拍网)的系统架构

[复制链接]

1313

主题

1337

帖子

5648

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
5648
跳转到指定楼层
楼主
发表于 2014-9-28 16:31:09 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Yupoo!(又拍网) 是目前国内最大的图片服务提供商,整个网站构建于大量的开源软件之上。以下为其使用到的开源软件信息:
(信息来源:http://www.yupoo.com/info/about/)。
一、Yupoo的整体架构
三、服务器的选用
选用Squid的原因是“目前暂时还没找到效率比 Squid 高的缓存系统,原来命中率的确很差,后来在 Squid 前又装了层 Lighttpd, 基于 url 做 hash, 同一个图片始终会到同一台 squid 去,所以命中率彻底提高了。”
五、数据库的设计
数据库一向是网站架构中最具挑战性的,瓶颈通常出现在这里。又拍网的照片数据量很大,数据库也几度出现严重的压力问题。和很多使用MySQL的2.0站点一样,又拍网的MySQL集群经历了从最初的一个主库一个从库、到一个主库多个从库、 然后到多个主库多个从库的一个发展过程。
一般都会先进行垂直拆分,因为这种方式拆分方式实现起来比较简单,根据表名访问不同的数据库就可以了。但是垂直拆分方式并不能彻底解决所有压力问题,另外,也要看应用类型是否合适这种拆分方式。如果合适的话,也能很好的起到分散数据库压力的作用。比如对于豆瓣我比较适合采用垂直拆分, 因为豆瓣的各核心业务/模块(书籍、电影、音乐)相对独立,数据的增加速度也比较平稳。不同的是,又拍网的核心业务对象是用户上传的照片,而照片数据的增加速度随着用户量的增加越来越快。压力基本上都在照片表上,显然垂直拆分并不能从根本上解决我们的问题,所以,Yupoo采用水平拆分的方式。
索引表的方式能够比较方便地添加数据库节点,在增加节点时,只要将其添加到可用数据库列表里即可。 当然如果需要平衡各个节点的压力的话,还是需要进行数据的迁移,但是这个时候的迁移是少量的,可以逐步进行。要迁移用户A的数据,首先要将其状态置为迁移数据中,这个状态的用户不能进行写操作,并在页面上进行提示。 然后将用户A的数据全部复制到新增加的节点上后,更新映射表,然后将用户A的状态置为正常,最后将原来对应的数据库上的数据删除。这个过程通常会在临晨进行,所以,所以很少会有用户碰到迁移数据中的情况。当然,有些数据是不属于某个用户的,比如系统消息、配置等等,把这些数据保存在一个全局库中。
分库带来的问题如何解决?
分库会给在应用的开发和部署上都带来很多麻烦。
1、不能执行跨库的关联查询
如果我们需要查询的数据分布于不同的数据库,没办法通过JOIN的方式查询获得。比如要获得好友的最新照片,不能保证所有好友的数据都在同一个数据库里。一个解决办法是通过多次查询,再进行聚合的方式。所以需要尽量避免类似的需求。有些需求可以通过保存多份数据来解决,比如User-A和User-B的数据库分别是DB-1和DB-2, 当User-A评论了User-B的照片时,我们会同时在DB-1和DB-2中保存这条评论信息,我们首先在DB-2中的photo_comments表中插入一条新的记录,然后在DB-1中的user_comments表中插入一条新的记录。这两个表的结构如下图所示。这样我们可以通过查询photo_comments表得到User-B的某张照片的所有评论, 也可以通过查询user_comments表获得User-A的所有评论。另外可以考虑使用全文检索工具来解决某些需求, 使用Solr来提供全站标签检索和照片搜索服务。
如果要在节点数据库上使用自增字段,那么我们就不能保证全局唯一。这倒不是很严重的问题,但是当节点之间的数据发生关系时,就会使得问题变得比较麻烦。再来看看上面提到的评论的例子。如果photo_comments表中的comment_id的自增字段,当我们在DB-2.photo_comments表插入新的评论时, 得到一个新的comment_id,假如值为101,而User-A的ID为1,那么我们还需要在DB-1.user_comments表中插入(1, 101 …)。 User-A是个很活跃的用户,他又评论了User-C的照片,而User-C的数据库是DB-3。 很巧的是这条新评论的ID也是101,这种情况很用可能发生。那么我们又在DB-1.user_comments表中插入一行像这样(1, 101 …)的数据。 那么我们要怎么设置user_comments表的主键呢(标识一行数据)?可以不设啊,不幸的是有的时候(框架、缓存等原因)必需设置。那么可以以user_id、 comment_id和photo_id为组合主键,但是photo_id也有可能一样(的确很巧)。看来只能再加上photo_owner_id了, 但是这个结果又让我们实在有点无法接受,太复杂的组合键在写入时会带来一定的性能影响,这样的自然键看起来也很不自然。所以,Yupoo放弃了在节点上使用自增字段,想办法让这些ID变成全局唯一。为此增加了一个专门用来生成ID的数据库,这个库中的表结构都很简单,只有一个自增字段id。 当我们要插入新的评论时,我们先在ID库的photo_comments表里插入一条空的记录,以获得一个唯一的评论ID。 当然这些逻辑都已经封装在我们的框架里了,对于开发人员是透明的。 为什么不用其它方案呢,比如一些支持incr操作的Key-Value数据库。Yupoo还是比较放心把数据放在MySQL里。 另外,Yupoo会定期清理ID库的数据,以保证获取新ID的效率。
前面提到的一个数据库节点为Shard,一个Shard由两个台物理服务器组成, 可以理解为Node-A和Node-B,Node-A和Node-B之间是配置成Master-Master相互复制的。 虽然是Master-Master的部署方式,但是同一时间还是只使用其中一个,原因是复制的延迟问题, 当然在Web应用里,可以在用户会话里放置一个A或B来保证同一用户一次会话里只访问一个数据库, 这样可以避免一些延迟问题。但是Python任务是没有任何状态的,不能保证和PHP应用读写相同的数据库。那么为什么不配置成Master-Slave呢?Yupoo觉得只用一台太浪费了,所以在每台服务器上都创建多个逻辑数据库。 如下图所示,在Node-A和Node-B上我们都建立了shard_001和shard_002两个逻辑数据库, Node-A上的shard_001和Node-B上的shard_001组成一个Shard,而同一时间只有一个逻辑数据库处于Active状态。 这个时候如果需要访问Shard-001的数据时,我们连接的是Node-A上的shard_001, 而访问Shard-002的数据则是连接Node-B上的shard_002。以这种交叉的方式将压力分散到每台物理服务器上。 以Master-Master方式部署的另一个好处是,可以不停止服务的情况下进行表结构升级, 升级前先停止复制,升级Inactive的库,然后升级应用,再将已经升级好的数据库切换成Active状态, 原来的Active数据库切换成Inactive状态,然后升级它的表结构,最后恢复复制。 当然这个步骤不一定适合所有升级过程,如果表结构的更改会导致数据复制失败,那么还是需要停止服务再升级的。
前面提到过添加服务器时,为了保证负载的平衡,需要迁移一部分数据到新的服务器上。为了避免短期内迁移的必要,在实际部署的时候,每台机器上部署了8个逻辑数据库, 添加服务器后,只要将这些逻辑数据库迁移到新服务器就可以了。最好是每次添加一倍的服务器, 然后将每台的1/2逻辑数据迁移到一台新服务器上,这样能很好的平衡负载。当然,最后到了每台上只有一个逻辑库时,迁移就无法避免了,不过那应该是比较久远的事情了。
Yupoo把分库逻辑都封装在我们的PHP框架里了,开发人员基本上不需要被这些繁琐的事情困扰。下面是使用框架进行照片数据的读写的一些例子:


1

[color=rgb(49, 124, 197) !important]2

3

[color=rgb(49, 124, 197) !important]4

5

[color=rgb(49, 124, 197) !important]6

7

[color=rgb(49, 124, 197) !important]8

9

[color=rgb(49, 124, 197) !important]10

11

[color=rgb(49, 124, 197) !important]12

13

[color=rgb(49, 124, 197) !important]14

15

[color=rgb(49, 124, 197) !important]16

17

[color=rgb(49, 124, 197) !important]18

19

[color=rgb(49, 124, 197) !important]20

21

[color=rgb(49, 124, 197) !important]22

23

[color=rgb(49, 124, 197) !important]24

25

[color=rgb(49, 124, 197) !important]26

27

[color=rgb(49, 124, 197) !important]28

29

[color=rgb(49, 124, 197) !important]30

31

[color=rgb(49, 124, 197) !important]32

33

[color=rgb(49, 124, 197) !important]34

35

[color=rgb(49, 124, 197) !important]36

37


[color=rgb(0, 111, 224) !important]&[color=rgb(0, 45, 122) !important]lt[color=rgb(51, 51, 51) !important];[color=rgb(51, 51, 51) !important]?php

[color=rgb(0, 111, 224) !important]    [color=rgb(51, 51, 51) !important]$[color=rgb(0, 45, 122) !important]Photos[color=rgb(0, 111, 224) !important] [color=rgb(0, 111, 224) !important]=[color=rgb(0, 111, 224) !important] [color=rgb(128, 0, 128) !important]new[color=rgb(0, 111, 224) !important] [color=rgb(0, 78, 208) !important]ShardedDBTable[color=rgb(51, 51, 51) !important]([color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039hotos', 'yp_photos', 'user_id', array(

[color=rgb(0, 111, 224) !important]                [color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039;photo_id'    => array('type' => 'long', 'primary' => true, 'global_auto_increment' => true),

[color=rgb(0, 111, 224) !important]                [color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039;user_id'     => array('type' => 'long'),

[color=rgb(0, 111, 224) !important]                [color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039;title'       => array('type' => 'string'),

[color=rgb(0, 111, 224) !important]                [color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039;posted_date' => array('type' => 'date'),

[color=rgb(0, 111, 224) !important]            [color=rgb(51, 51, 51) !important])[color=rgb(51, 51, 51) !important])[color=rgb(51, 51, 51) !important];

[color=rgb(0, 111, 224) !important]    [color=rgb(51, 51, 51) !important]$[color=rgb(0, 45, 122) !important]photo[color=rgb(0, 111, 224) !important] [color=rgb(0, 111, 224) !important]=[color=rgb(0, 111, 224) !important] [color=rgb(51, 51, 51) !important]$[color=rgb(0, 45, 122) !important]Photos[color=rgb(0, 111, 224) !important]-[color=rgb(0, 111, 224) !important]&[color=rgb(0, 45, 122) !important]gt[color=rgb(51, 51, 51) !important];[color=rgb(0, 78, 208) !important]new_object[color=rgb(51, 51, 51) !important]([color=rgb(128, 0, 128) !important]array[color=rgb(51, 51, 51) !important]([color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039;user_id' => 1, 'title' => 'Workforme'));

[color=rgb(0, 111, 224) !important]    [color=rgb(51, 51, 51) !important]$[color=rgb(0, 45, 122) !important]photo[color=rgb(0, 111, 224) !important]-[color=rgb(0, 111, 224) !important]&[color=rgb(0, 45, 122) !important]gt[color=rgb(51, 51, 51) !important];[color=rgb(0, 78, 208) !important]insert[color=rgb(51, 51, 51) !important]([color=rgb(51, 51, 51) !important])[color=rgb(51, 51, 51) !important];

[color=rgb(0, 111, 224) !important]    [color=rgb(255, 128, 0) !important]// 加载ID为10001的照片,注意第一个参数为用户ID

[color=rgb(0, 111, 224) !important]    [color=rgb(51, 51, 51) !important]$[color=rgb(0, 45, 122) !important]photo[color=rgb(0, 111, 224) !important] [color=rgb(0, 111, 224) !important]=[color=rgb(0, 111, 224) !important] [color=rgb(51, 51, 51) !important]$[color=rgb(0, 45, 122) !important]Photos[color=rgb(0, 111, 224) !important]-[color=rgb(0, 111, 224) !important]&[color=rgb(0, 45, 122) !important]gt[color=rgb(51, 51, 51) !important];[color=rgb(0, 78, 208) !important]load[color=rgb(51, 51, 51) !important]([color=rgb(206, 0, 0) !important]1[color=rgb(51, 51, 51) !important],[color=rgb(0, 111, 224) !important] [color=rgb(206, 0, 0) !important]10001[color=rgb(51, 51, 51) !important])[color=rgb(51, 51, 51) !important];

[color=rgb(0, 111, 224) !important]    [color=rgb(255, 128, 0) !important]// 更改照片属性

[color=rgb(0, 111, 224) !important]    [color=rgb(51, 51, 51) !important]$[color=rgb(0, 45, 122) !important]photo[color=rgb(0, 111, 224) !important]-[color=rgb(0, 111, 224) !important]&[color=rgb(0, 45, 122) !important]gt[color=rgb(51, 51, 51) !important];[color=rgb(0, 45, 122) !important]title[color=rgb(0, 111, 224) !important] [color=rgb(0, 111, 224) !important]=[color=rgb(0, 111, 224) !important] [color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039;Database Sharding';

[color=rgb(0, 111, 224) !important]    [color=rgb(51, 51, 51) !important]$[color=rgb(0, 45, 122) !important]photo[color=rgb(0, 111, 224) !important]-[color=rgb(0, 111, 224) !important]&[color=rgb(0, 45, 122) !important]gt[color=rgb(51, 51, 51) !important];[color=rgb(0, 78, 208) !important]update[color=rgb(51, 51, 51) !important]([color=rgb(51, 51, 51) !important])[color=rgb(51, 51, 51) !important];

[color=rgb(0, 111, 224) !important]    [color=rgb(255, 128, 0) !important]// 删除照片

[color=rgb(0, 111, 224) !important]    [color=rgb(51, 51, 51) !important]$[color=rgb(0, 45, 122) !important]photo[color=rgb(0, 111, 224) !important]-[color=rgb(0, 111, 224) !important]&[color=rgb(0, 45, 122) !important]gt[color=rgb(51, 51, 51) !important];[color=rgb(0, 78, 208) !important]delete[color=rgb(51, 51, 51) !important]([color=rgb(51, 51, 51) !important])[color=rgb(51, 51, 51) !important];

[color=rgb(0, 111, 224) !important]    [color=rgb(255, 128, 0) !important]// 获取ID为1的用户在2010-06-01之后上传的照片

[color=rgb(0, 111, 224) !important]    [color=rgb(51, 51, 51) !important]$[color=rgb(0, 45, 122) !important]photos[color=rgb(0, 111, 224) !important] [color=rgb(0, 111, 224) !important]=[color=rgb(0, 111, 224) !important] [color=rgb(51, 51, 51) !important]$[color=rgb(0, 45, 122) !important]Photos[color=rgb(0, 111, 224) !important]-[color=rgb(0, 111, 224) !important]&[color=rgb(0, 45, 122) !important]gt[color=rgb(51, 51, 51) !important];[color=rgb(0, 78, 208) !important]fetch[color=rgb(51, 51, 51) !important]([color=rgb(128, 0, 128) !important]array[color=rgb(51, 51, 51) !important]([color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039;user_id' => 1, 'posted_date__gt' => '2010-06-01'));

[color=rgb(51, 51, 51) !important]?[color=rgb(0, 111, 224) !important]&[color=rgb(0, 45, 122) !important]gt[color=rgb(51, 51, 51) !important];




首先要定义一个ShardedDBTable对象,所有的API都是通过这个对象开放。第一个参数是对象类型名称, 如果这个名称已经存在,那么将返回之前定义的对象。你也可以通过get_table(‘Photos’)这个函数来获取之前定义的Table对象。 第二个参数是对应的数据库表名,而第三个参数是数据库线索字段,你会发现在后面的所有API中全部需要指定这个字段的值。 第四个参数是字段定义,其中photo_id字段的global_auto_increment属性被置为true,这就是前面所说的全局自增ID, 只要指定了这个属性,框架会处理好ID的事情。
如果我们要访问全局库中的数据,我们需要定义一个DBTable对象。


1

[color=rgb(49, 124, 197) !important]2

3

[color=rgb(49, 124, 197) !important]4

5

[color=rgb(49, 124, 197) !important]6

7

[color=rgb(49, 124, 197) !important]8

9

[color=rgb(49, 124, 197) !important]10

11


[color=rgb(0, 111, 224) !important]&[color=rgb(0, 45, 122) !important]lt[color=rgb(51, 51, 51) !important];[color=rgb(51, 51, 51) !important]?php

[color=rgb(0, 111, 224) !important]    [color=rgb(51, 51, 51) !important]$[color=rgb(0, 45, 122) !important]Users[color=rgb(0, 111, 224) !important] [color=rgb(0, 111, 224) !important]=[color=rgb(0, 111, 224) !important] [color=rgb(128, 0, 128) !important]new[color=rgb(0, 111, 224) !important] [color=rgb(0, 78, 208) !important]DBTable[color=rgb(51, 51, 51) !important]([color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039;Users', 'yp_users', array(

[color=rgb(0, 111, 224) !important]                [color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039;user_id'  => array('type' => 'long', 'primary' => true, 'auto_increment' => true),

[color=rgb(0, 111, 224) !important]                [color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039;username' => array('type' => 'string'),

[color=rgb(0, 111, 224) !important]            [color=rgb(51, 51, 51) !important])[color=rgb(51, 51, 51) !important])[color=rgb(51, 51, 51) !important];

[color=rgb(51, 51, 51) !important]?[color=rgb(0, 111, 224) !important]&[color=rgb(0, 45, 122) !important]gt[color=rgb(51, 51, 51) !important];




DBTable是ShardedDBTable的父类,除了定义时参数有些不同(DBTable不需要指定数据库线索字段),它们提供一样的API。
六、缓存方案的选择
Yupoo使用的框架自带缓存功能,对开发人员是透明的。


1

[color=rgb(49, 124, 197) !important]2

3

[color=rgb(49, 124, 197) !important]4

5


[color=rgb(0, 111, 224) !important]&[color=rgb(0, 45, 122) !important]lt[color=rgb(51, 51, 51) !important];[color=rgb(51, 51, 51) !important]?php

[color=rgb(0, 111, 224) !important]    [color=rgb(51, 51, 51) !important]$[color=rgb(0, 45, 122) !important]photo[color=rgb(0, 111, 224) !important] [color=rgb(0, 111, 224) !important]=[color=rgb(0, 111, 224) !important] [color=rgb(51, 51, 51) !important]$[color=rgb(0, 45, 122) !important]Photos[color=rgb(0, 111, 224) !important]-[color=rgb(0, 111, 224) !important]&[color=rgb(0, 45, 122) !important]gt[color=rgb(51, 51, 51) !important];[color=rgb(0, 78, 208) !important]load[color=rgb(51, 51, 51) !important]([color=rgb(206, 0, 0) !important]1[color=rgb(51, 51, 51) !important],[color=rgb(0, 111, 224) !important] [color=rgb(206, 0, 0) !important]10001[color=rgb(51, 51, 51) !important])[color=rgb(51, 51, 51) !important];

[color=rgb(51, 51, 51) !important]?[color=rgb(0, 111, 224) !important]&[color=rgb(0, 45, 122) !important]gt[color=rgb(51, 51, 51) !important];




比如上面的方法调用,框架先尝试以Photos-1-10001为Key在缓存中查找,未找到的话再执行数据库查询并放入缓存。当更改照片属性或删除照片时,框架负责从缓存中删除该照片。这种单个对象的缓存实现起来比较简单。稍微麻烦的是像下面这样的列表查询结果的缓存。


1

[color=rgb(49, 124, 197) !important]2

3

[color=rgb(49, 124, 197) !important]4

5


[color=rgb(0, 111, 224) !important]&[color=rgb(0, 45, 122) !important]lt[color=rgb(51, 51, 51) !important];[color=rgb(51, 51, 51) !important]?php

[color=rgb(0, 111, 224) !important]    [color=rgb(51, 51, 51) !important]$[color=rgb(0, 45, 122) !important]photos[color=rgb(0, 111, 224) !important] [color=rgb(0, 111, 224) !important]=[color=rgb(0, 111, 224) !important] [color=rgb(51, 51, 51) !important]$[color=rgb(0, 45, 122) !important]Photos[color=rgb(0, 111, 224) !important]-[color=rgb(0, 111, 224) !important]&[color=rgb(0, 45, 122) !important]gt[color=rgb(51, 51, 51) !important];[color=rgb(0, 78, 208) !important]fetch[color=rgb(51, 51, 51) !important]([color=rgb(128, 0, 128) !important]array[color=rgb(51, 51, 51) !important]([color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039;user_id' => 1, 'posted_date__gt' => '2010-06-01'));

[color=rgb(51, 51, 51) !important]?[color=rgb(0, 111, 224) !important]&[color=rgb(0, 45, 122) !important]gt[color=rgb(51, 51, 51) !important];




Yupoo把这个查询分成两步,第一步先查出符合条件的照片ID,然后再根据照片ID分别查找具体的照片信息。 这么做可以更好的利用缓存。第一个查询的缓存Key为Photos-list-{shard_key}-{md5(查询条件SQL语句)}, Value是照片ID列表(逗号间隔)。其中shard_key为user_id的值1。目前来看,列表缓存也不麻烦。 但是如果用户修改了某张照片的上传时间呢,这个时候缓存中的数据就不一定符合条件了。所以,需要一个机制来保证我们不会从缓存中得到过期的列表数据。我们为每张表设置了一个revision,当该表的数据发生变化时(调用insert/update/delete方法), 我们就更新它的revision,所以我们把列表的缓存Key改为Photos-list-{shard_key}-{md5(查询条件SQL语句)}-{revision}, 这样我们就不会再得到过期列表了。
revision信息也是存放在缓存里的,Key为Photos-revision。这样做看起来不错,但是好像列表缓存的利用率不会太高。因为我们是以整个数据类型的revision为缓存Key的后缀,显然这个revision更新的非常频繁,任何一个用户修改或上传了照片都会导致它的更新,哪怕那个用户根本不在我们要查询的Shard里。要隔离用户的动作对其他用户的影响,我们可以通过缩小revision的作用范围来达到这个目的。 所以revision的缓存Key变成Photos-{shard_key}-revision,这样的话当ID为1的用户修改了他的照片信息时, 只会更新Photos-1-revision这个Key所对应的revision。
因为全局库没有shard_key,所以修改了全局库中的表的一行数据,还是会导致整个表的缓存失效。 但是大部分情况下,数据都是有区域范围的,比如帮助论坛的主题帖子, 帖子属于主题。修改了其中一个主题的一个帖子,没必要使所有主题的帖子缓存都失效。 所以在DBTable上增加了一个叫isolate_key的属性。


1

[color=rgb(49, 124, 197) !important]2

3

[color=rgb(49, 124, 197) !important]4

5

[color=rgb(49, 124, 197) !important]6

7

[color=rgb(49, 124, 197) !important]8

9

[color=rgb(49, 124, 197) !important]10

11

[color=rgb(49, 124, 197) !important]12

13

[color=rgb(49, 124, 197) !important]14

15

[color=rgb(49, 124, 197) !important]16

17

[color=rgb(49, 124, 197) !important]18

19

[color=rgb(49, 124, 197) !important]20

21


[color=rgb(0, 111, 224) !important]&[color=rgb(0, 45, 122) !important]lt[color=rgb(51, 51, 51) !important];[color=rgb(51, 51, 51) !important]?php

[color=rgb(51, 51, 51) !important]$[color=rgb(0, 45, 122) !important]GLOBALS[color=rgb(51, 51, 51) !important][[color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039osts'] = new DBTable('Posts', 'yp_posts', array(

[color=rgb(0, 111, 224) !important]        [color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039;topic_id'    => array('type' => 'long', 'primary' => true),

[color=rgb(0, 111, 224) !important]        [color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039;post_id'     => array('type' => 'long', 'primary' => true, 'auto_increment' => true),

[color=rgb(0, 111, 224) !important]        [color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039;author_id'   => array('type' => 'long'),

[color=rgb(0, 111, 224) !important]        [color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039;content'     => array('type' => 'string'),

[color=rgb(0, 111, 224) !important]        [color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039;posted_at'   => array('type' => 'datetime'),

[color=rgb(0, 111, 224) !important]        [color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039;modified_at' => array('type' => 'datetime'),

[color=rgb(0, 111, 224) !important]        [color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039;modified_by' => array('type' => 'long'),

[color=rgb(0, 111, 224) !important]    [color=rgb(51, 51, 51) !important])[color=rgb(51, 51, 51) !important],[color=rgb(0, 111, 224) !important] [color=rgb(0, 111, 224) !important]&[color=rgb(184, 92, 0) !important]#039;topic_id');

[color=rgb(51, 51, 51) !important]?[color=rgb(0, 111, 224) !important]&[color=rgb(0, 45, 122) !important]gt[color=rgb(51, 51, 51) !important];




注意构造函数的最后一个参数topic_id就是指以字段topic_id作为isolate_key,它的作用和shard_key一样用于隔离revision的作用范围。
ShardedDBTable继承自DBTable,所以也可以指定isolate_key。 ShardedDBTable指定了isolate_key的话,能够更大幅度缩小revision的作用范围。 比如相册和照片的关联表yp_album_photos,当用户往他的其中一个相册里添加了新的照片时, 会导致其它相册的照片列表缓存也失效。如果指定这张表的isolate_key为album_id的话, 我们就把这种影响限制在了本相册内。
缓存分为两级,第一级只是一个PHP数组,有效范围是Request。而第二级是memcached。这么做的原因是,很多数据在一个Request周期内需要加载多次,这样可以减少memcached的网络请求。另外Yupoo的框架也会尽可能的发送memcached的gets命令来获取数据, 从而减少网络请求。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?马上注册

x
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏
回复

使用道具 举报

0

主题

67

帖子

80

积分

新人

Rank: 1

积分
80
沙发
发表于 2015-8-8 11:49:52 | 只看该作者
11111111111111111
回复 支持 反对

使用道具 举报

0

主题

67

帖子

80

积分

新人

Rank: 1

积分
80
板凳
发表于 2015-8-8 11:50:19 | 只看该作者
1111111111111111111111111
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 马上注册

本版积分规则

毒镜头:老镜头、摄影器材资料库、老镜头样片、摄影
爱评测 aipingce.com  
快速回复 返回顶部 返回列表