移动端与服务器端数据库同步
本文为转载文章,查看原文请点击以下链接。
-
移动端与服务器端数据库同步
以下是简单理解:
本文的策略可实现简单的数据同步。
主要的关键点是:
- 时间戳
- 更新状态(本文未提及)
更新状态包括添加,修改,删除等等,明确告诉另一端具体是做哪一种更新。 - 记录数据更改记录的表(本文未提及)
按我理解,时间戳与更新状态这两个字段可以放在需要同步的目标表里,也可新建一个更改记录
表,主要字段包括时间戳,更新状态,待同步数据的主键(如guid).
不知这种理解是否正确。
以下是正文:
数据库同步满足以下几个需求:
-
同步时双向传输数据最小化。双向即,服务器端更新同步到移动端,和移动端更新同步到服务器。每次只传输两端差异数据。
-
支持离线。支持离线本身是一种好的用户体验,而它带来的一个其他的好处是每次移动端数据库查询仅需查询本地数据库,这样就避免了过多的服务器端查询。本地数据库减少了很多服务器的压力,当然也给用户省了流量。数据库更新操作也是如此,仅更新本地数据库,然后在适当的时机与服务器端进行同步。更进一步的说,移动端查询和更新数据只跟本地数据库打交道。
-
冲突解决。如果一个用户帐号在多个移动端进行离线使用,势必会产生数据冲突。
设计的关键在于数据模型的设计,和同步算法。以下是我的想法。
下面是对象类代码,对应数据库的表字段。
服务器端设计:
public abstract class ServerBaseModel {
public long userId; /* Global unique user id */
public long id; /* Model id. Unique for user */
public long lastmodified; /* Last modified server time stamp */
public boolean deleted; /* delete flag */
}
移动端设计:
public abstract class ClientBaseModel {
public long userId; /* Global unique user id */
public long id; /* Model id. Unique for user */
public long lastmodified; /* Last modified server time stamp */
public boolean deleted; /* delete flag */
public boolean dirty; /* Local dirty flag */
}
首先是如何选择表的主键id
-
使用auto increment主键?不行!根据前面支持离线的需求,id应该在移动端就已经生成。如果使用auto increment在同一个用户帐号的情况下只可以做到单个移动端的唯一性,无法保证多个移动端的唯一性,更加不能保证服务器端全局的唯一性。
-
使用UUID作为主键?可行!每一条数据在移动端创建时即为之生成UUID。这样基本可以保证服务器端全局的唯一性。对于使用UUID作为主键好不好的讨论很多,大家可以另行参考。
-
我的方案。使用userId和一个用户唯一的model id作为联合主键。model id需要保证在同一userId下唯一,这样再加上userId使得数据全局唯一。问题是如何选择model id?一个比较可行但是不能保证完全没有重复的是时间戳。
-
还有其他更好的主键方案吗?
接下来是如何判断服务器端数据已经更新
每一条数据存储一个last modified时间戳。这个时间戳是服务器端的时间。同一条数据如果移动端的lastmodified小于服务器端的lastmodified就可以判断数据已经更新。
移动端数据更新
移动端数据库增加一个dirty标志,dirty标志表示本地新增或者修改的数据,这些数据会在下一次同步时上传至服务器。
如何处理数据删除
根据前面last modified和dirty字段的设计,整个数据模型是一个增量式的。数据只允许新增和更新,所以这里增加一个deleted标志表示数据是否已经被删除。
以上介绍完我的移动端和服务器端数据库同步的数据模型设计,接下来讲讲同步算法。
同步算法:
服务器端向移动端同步
- 获得移动端最大的last modified,发送至服务器端。
- 服务器端查询所有last modified值比移动端最大last modified的数据,返回至移动端。
- 移动端更新本地数据库
移动端向服务器端同步
- 获得所有的dirty数据,发送至服务器端。
- 服务器端处理dirty数据,返回更新的数据的新的last modified。
- 移动端更新last modified,并且清楚dirty标志。