天天看点

多核计算与并发编程(四) 缓存数据同步的设计

   这次讲一个完整的例子,我们就做一个打折加油站信息一览,功能是浏览油价打折的加油站,以及当前的汽油价格。

   我们这个网站的访问量非常大,一台服务器是不够的(否则我们就不需要在多台服务器间同步数据了不是吗),甚至于给我们提供加油站信息的人也非常多,录入数据也需要多台服务器,于是我们有了下面的服务器的架构

<a href="http://blog.51cto.com/attachment/201312/213034761.jpg"></a>

   我们有一台数据库服务器,也许将来是多台,但是不在本文的讨论范围。

   我们有两台用于数据维护(录入、修改)的服务器,我们称之为“写服务”。我们有三台缓存服务器,把数据库里面的数据全部加载到内存中,提高服务响应的速度,给网站和手机端使用,我这里称为“读服务”。

我们思考以下问题:

   1.“写服务”使用中,如果用户录入数据时连接到不同的服务器(A和B),行为和结果会不同吗?

   2.“写服务”引起的数据改变,怎么通知到“读服务”去更新缓存中的数据?

   3.“读服务”使用中,如果用户录入数据时连接到不同的服务器(C、D、E),行为和结果会不同吗?

现在回答上面的问题

   1.“写服务”没有数据缓存,用户所有的操作都是直接和数据库打交道,所以,所有服务器的行为都是一致的,用户连接到服务器A或者B,没有任何差别。

   2.这里我使用一个简单的方法来实现,到“写服务”引起数据变化后,把数据变化的条目id,追加到一个记录数据变化的队列中。“读服务”的服务器各自定时查询数据变化的队列,从数据库读取对应的记录来更新自身的缓存。

   3.“读服务”的服务器C、D、E各自定期更新缓存,会有数据不一致的情况,取决于“定期”更新的间隔,用户连接上不同的服务器,可能会查询到不同的数据,在大多数应用中,这是可以容忍的。

现在讲一讲数据同步的具体实现

1.消息约定

   在我们的应用中,只有一种消息,就是“数据变化”,参数是数据库中变化数据的id,“写服务”在任何操作后,都发送“数据变化”消息。

   “读服务”收到消息后,去数据库查询对应的记录,有三种情况。

   a.数据库查到记录,缓存中没有,可能是“写服务”进行了添加操作。“读服务”把记录添加到缓存中

   b.数据库查到记录,缓存中也有,可能是“写服务”进行了更新操作。“读服务”更新缓存中的记录

   c.数据库没有,缓存中存在,可能是“写服务”进行了删除操作。“读服务”删除缓存中的记录

这一消息约定的优点是,即是消息重复执行,也不会造成数据错误

2.消息队列

   我使用数据库的一个表,来作为消息队列,每个消息有自动增加的id,和创建时间。自动增加id是给消费者(“读服务”)避免重复消费已经消费过的消息。创建时间用于定时批量删除过期的消息

3.消费消息

   “读服务”定时查询消息表,消费消息(更新缓存)之后,记录最后的id,避免下次重复获取

4.“读服务”重启

   当服务重启的时候,所有的数据都是从数据库读取的最新数据,但也有可能在加载数据的过程中,有新的数据变化。解决办法是,

   在启动的最初,记录消息队列最大的消息id,在启动完成后,已刚才获取的最大id,作为首次获取消息的起始值(但不包括那个最大id),如果在启动过程中,消息队列有了新id,将不会错过。

总结一下,这个方案

   优点:实现简单,读服务器之间不需要有数据交换,所有的服务都各自访问消息队列。

   缺点:使用消息轮询,数据加载会有延后,延后的时间取决于轮询的间隔。

本文转自 ponpon_ 51CTO博客,原文链接:http://blog.51cto.com/liuxp0827/1344918,如需转载请自行联系原作者