最近在写一个工具类应用。需要支持离线功能,所以本地需要一份的拷贝,这样就涉及到移动端和服务器端数据库的同步问题。
在设计时我要满足以下几个需求:
1. 同步时双向传输数据最小化。双向即,服务器端更新同步到移动端,和移动端更新同步到服务器。每次只传输两端差异数据。
2. 支持离线。支持离线本身是一种好的用户体验,而它带来的一个其他的好处是每次移动端数据库查询仅需查询本地数据库,这样就避免了过多的服务器端查询。本地数据库减少了很多服务器的压力,当然也给用户省了流量。数据库更新操作也是如此,仅更新本地数据库,然后在适当的时机与服务器端进行同步。更进一步的说,移动端查询和更新数据只跟本地数据库打交道。
3. 冲突解决。如果一个用户帐号在多个移动端进行离线使用,势必会产生数据冲突。
设计的关键在于数据模型的设计,和同步算法。以下是我的想法。
下面是对象类代码,对应数据库的表字段。
服务器端设计:
public abstract class serverbasemodel { public long userid; /* global unique user id */ public long id; /* model id. unique for user */ public long lastmodified; /* last modified 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
1. 使用auto increment主键?不行!根据前面支持离线的需求,id应该在移动端就已经生成。如果使用auto increment在同一个用户帐号的情况下只可以做到单个移动端的唯一性,无法保证多个移动端的唯一性,更加不能保证服务器端全局的唯一性。
2. 使用uuid作为主键?可行!每一条数据在移动端创建时即为之生成uuid。这样基本可以保证服务器端全局的唯一性。对于使用uuid作为主键好不好的讨论很多,大家可以另行参考。
3. 我的方案。使用userid和一个用户唯一的model id作为联合主键。model id需要保证在同一userid下唯一,这样再加上userid使得数据全局唯一。问题是如何选择model id?一个比较可行但是不能保证完全没有重复的是时间戳。
4. 还有其他更好的主键方案吗?
接下来是如何判断服务器端数据已经更新
每一条数据存储一个last modified时间戳。这个时间戳是服务器端的时间。同一条数据如果移动端的lastmodified小于服务器端的lastmodified就可以判断数据已经更新。
移动端数据更新
移动端数据库增加一个dirty标志,dirty标志表示本地新增或者修改的数据,这些数据会在下一次同步时上传至服务器。
如何处理数据删除
根据前面last modified和dirty字段的设计,整个数据模型是一个增量式的。数据只允许新增和更新,所以这里增加一个deleted标志表示数据是否已经被删除。
以上介绍完我的移动端和服务器端数据库同步的数据模型设计,接下来讲讲详细同步算法。
不过。。。等等。。。公司年会的节奏,等有时间继续写。
同步算法:
1. 服务器端向移动端同步
2. 移动端向服务器端同步
android帐号验证框架