大约8年前,我为一个应用程序构建了这样的系统,我可以分享随着应用程序使用率的增长而演变的几种方式。
我首先将任何设备的所有更改(插入,更新或删除)记录到“历史记录”表中。因此,例如,如果有人在“联系人”表中更改了他们的电话号码,系统将编辑contact.phone字段,并添加一条历史记录,其中包含action = update,field = phone,record = [contact ID],值= [新电话号码]。然后,每当设备同步时,它都会下载自上次同步以来的历史记录项,并将其应用于本地数据库。这听起来像上述的“事务复制”模式。
一个问题是,当可以在不同设备上创建项目时,保持ID唯一。我刚开始时并不了解UUID,所以我使用了自动递增的ID,并编写了一些复杂的代码,这些代码在中央服务器上运行,以检查从设备上传的新ID,如果发生冲突,将其更改为唯一ID,以及告诉源设备更改其本地数据库中的ID。只需更改新记录的ID并没有那么糟,但是,例如,如果我在联系人表中创建了一个新项目,然后在事件表中创建了一个新的相关项目,那么现在我也需要使用外键检查并更新。
最终,我了解到UUID可以避免这种情况,但是到那时我的数据库已经变得很大,而且我担心完整的UUID实施会造成性能问题。因此,我没有使用完整的UUID,而是开始使用随机生成的8个字符的字母数字键作为ID,并且将现有代码保留在适当的位置以处理冲突。在我当前的8个字符的键和UUID的36个字符之间的某个位置,必须有一个甜点,可以消除冲突而不会造成不必要的膨胀,但是由于我已经有了冲突解决代码,因此尝试该冲突并不是优先考虑的事情。 。
下一个问题是,历史表的大小大约是数据库其余部分的10倍。这使存储变得昂贵,并且对历史表的任何维护都可能很麻烦。保留整个表可以使用户回滚以前的所有更改,但是开始感觉有些过时了。因此,我在同步过程中添加了一个例程,该例程中,如果历史记录表中不再存在设备上次下载的历史记录项,则服务器不会为它提供最近的历史记录项,而是为它提供一个包含所有数据的文件该帐户。然后,我添加了一个cronjob来删除90天以上的历史记录项。这意味着用户仍可以回滚少于90天的更改,并且如果他们每90天至少同步一次,则更新将像以前一样进行增量。但是如果他们等待超过90天,
所做的更改使历史记录表的大小减少了近90%,因此现在维护历史记录表仅使数据库的大小是原来的两倍,而不是原来的十倍。该系统的另一个好处是,如果需要,即使没有历史记录表,同步仍然可以进行-就像我需要进行一些维护以使其暂时脱机一样。或者,我可以为不同价格的帐户提供不同的回滚时间段。如果要下载的更改超过90天,则完整文件通常比增量格式更有效。
如果我今天从头开始,我将跳过ID冲突检查,而只瞄准足以消除冲突的密钥长度,并进行某种错误检查,以防万一。但是历史记录表以及最近更新的增量下载或需要时进行的完整下载的组合一直运行良好。