适用于服务器和客户端之间数据库同步的解决方案

前言 最近的做的东西中有些比较有意思,其中一个就是在服务器和移动端本地有一个一样的数据库,我们会对服务器的数据库进行维护(增、删、改),这样我们必须做一个同步服务器数据库和移动端本地数据库数据的解决方案,在之前未开始做的时候,心里有一个大概的想法,但是在没有实践之前,这个想法始终是不成熟的。终于在和小伙伴进行了讨论之后,收获了一个比较可行并且细化的解决方案。

话不多说,进入主题。

需要解决的问题:
  • 移动端怎么获知有数据更改
  • 服务器怎么告诉移动端需要更新那些数据
  • 移动端怎么去更新数据比较合适
  • 不同时间更新的服务器数据怎么一次性让客户端进行更新

服务器设计
日志表
  • 操作类型

    对数据表进行的是增、删、改、还是其它操作的类型标识。
  • 更新的表名字

    操作的数据表的名字。
  • 更新的数据ID(标识)

    操作的数据表的这条数据的ID。
  • 更新时间

    更新这条数据的时间,用于生成更新数据版本号。
  • 更新数据所属版本号

    更新这条数据所对应的版本号,根据日志表中的最大一个版本号的最后一个操作的时间来判断,如果是同一天为同一个版本,如果当前操作的时间比数据库的最大一个版本号的最后一个操作时间大并且不是同一天,那么则增加一个新版本。

    【更新:】后台管理用户手动为之前的操作添加版本号,版本号为之前最大版本+1
  • 操作人

    操作这次更新的用户。

服务器在后台有用户对数据库数据进行更新时,都需要向日志表中插入一条数据,记录更新这条数据的操作类型、ID、更新时间,并根据日志表中的数据生成更新版本号
【更新:】这个时候的版本号是空的

例子
  • 第一次上线之后:

    服务器和移动端的数据库在此时都是最新的。后来某一个时间里对服务器数据库数据进行了添加,新增了1条数据,在操作完成后就需要向日志表添加一条记录日志,更新版本号因为日志表中还没有数据,所以生成版本号为:【1】。
    【更新:】服务器和移动端的数据库在此时都是最新的。后来某一个时间里对服务器数据库数据进行了添加,新增了1条数据,在操作完成后就需要向日志表添加一条记录日志,更新版本号"",当后台管理用户需要让客户端和服务端的数据进行同步的时候,点击同步数据按钮,将操作记录表中版本号为空的记录版本号更改为【1】。

  • 第二次:

    再次对服务器数据库某表的数据进行了维护更新,修改了一条数据的值,在修改完成后,向日志表中添加一条记录,这个时候操作的时间已经是在上次操作的5天后(假设我们的更新版本号设定节点的时间为1天),那么此时我们需要增加一个版本号,这时候这条日志的版本号为【2】。
    【更新:】第二次对服务器数据库某表的数据进行了维护更新,修改了一条数据的值,在修改完成后,向日志表中添加一条记录,数据版本号为"",当后台管理用户需要让客户端和服务端的数据进行同步的时候,点击同步数据按钮,将操作记录表中版本号为空的记录版本号更改为【2】。

  • 第三次:

    再次对服务器数据库某表的数据进行了维护更新,删除了一条数据的值,在删除完成后,向日志表中添加一条记录,这个时候操作的时间和第二次的时间为同一天(假设我们的更新版本号设定节点的时间为1天),那么此时我们不需要增加一个版本号,这时候这条日志的版本号依然为【2】

  • 第四次:

    根据上面的条件以此类推...

这个时间我们服务器对这个数据库的操作都会记录下来,什么时候让客户端与服务器的数据库进行同步,由后台管理用户说的算。

更新接口
  • 请求参数:
{
  version:""
}
  • 返回数据:
//无数据更新时
{
  version:"",//服务器最新的版本号
  update:[],//需要更新的数据数组
  insert:[],//需要插入的数据数组
  delete:[],//需要删除id数组
  execsql:"",//需要直接执行的sql语句,辅助更新数据时用
  execsqllevel:""//执行sql的优先级(1执行在insert完后、2执行在update后、3执行在delete后)
}

//有数据更新时
{
  version:"2",
  update:[{json数据1},{json数据2},{json数据3},{json数据4}],
  insert:[{json数据1},{json数据2},{json数据3},{json数据4}],
  delete:[数据ID1,数据ID2,数据ID3,数据ID4],
  execsql:"",
  execsqllevel:""
}

服务器在获取到请求的移动端本地版本号后,查询日志表是否有比这个版本大的数据版本,如果没有,直接返回这个版本号;如果有,先将比这个版本大一个版本的更新版本号对应的操作全部查询出来,并且根据操作类型进行分类,分别放入 update:[],insert:[],delete:[]三个数组中,并且和这个版本号返回,如果可以不担心多次操作的数据进行分类集中的进行增删改的话,也可以将比移动端本地的数据库版本号大的数据版本的操作日志对于的数据全部查询出来,并且进行分类分别放入 update:[],insert:[],delete:[]三个数组中,和最大的一个版本号进行返回。

移动端更新数据
  • 请求更新数据接口的时间:

    可以在每次打开APP的时候自动进行请求,也可以每天自动请求一次,但是建议再做一个检测更新版本的功能。

  • 请求数据

//参数
{
  version:"0"//当前的本地版本号(第一次上线后默认为0)
}

//返回数据
{
  version:"1",
  update:[{json数据1},{json数据2},{json数据3},{json数据4}],
  insert:[{json数据1},{json数据2},{json数据3},{json数据4}],
  delete:[数据ID1,数据ID2,数据ID3,数据ID4],
  execsql:""
}

在请求完这个接口后,先对数据库的版本进行校验,如果已是最新版本,则无需进行后继的操作,如果不是最新版本,按照顺序

  • 遍历insert数组的数据插入到本地
  • 遍历update数组的数据将本地的数据进行更新
  • 遍历delete数组的数据将本地的数据进行删除

如果担心会出现数据错乱的问题,那么在请求更新完一个版本的数据之后应该再请求一次数据更新接口,一直请求到没有可更新数据为止,版本【1】更新到版本【99】则可能需要请求98次,虽然这样看起来很麻烦,但在我看来却很安全;如果不用担心数据错乱的问题,那么接口返回数据的方法可以一次性从版本【1】更新到版本【99】;

最后

其它小伙伴都是刚刚来公司不久的新同事,接手这个项目也就几天的时间,对项目中的东西并不是特别了解,但我们在讨论时都非常投入的参与到其中,这对于一个刚刚更换了新鲜血液的团队来说是非常好的事情,至少我们在认知上没有太大的差异,在交流上不会存在着障碍,能够在这么快的时间让他们融入团队,并切身的投入到其中,对他们来说也是认可。


2017-04-06更改

今天上午又跟小伙伴讨论了关于数据库同步的问题,发现之前的版本控制其实存在着问题,如果在一个时间节点内(这个时间节点还没有结束的时候),有用户请求了服务器的更新数据接口,那么只能更新到这个时候之前的数据,如果后继服务器还更新过数据,在这期间请求的移动端就无法再获取到后面更新的数据了,所以我们最后讨论的结果是让后台的管理用户去手动的添加版本,也就是说在所有添加操作记录是是没有数据库版本的,更新了一次数据后(跟时间节点没有关系,随便多长时间),只有当后台管理用户手动点击同步数据到客户端的时候,才将操作记录中没有版本的数据更新上当前的操作版本,这样就可以保证数据是完整更新的,不会出现之前时间节点出现的问题,并且这样的自由度比较大,可以随时让后台管理用户来控制什么时候让客户端和服务器进行数据同步更新。

2017-04-10更改

适用于数据库数据还原。之前后台的数据库操作日志就是为了能让数据库有版本控制,并且能随时进行还原,如果服务器的数据库可以依据操作日志进行还原,那么反之客户端的数据库也可以根据后台的操作日志进行还原。这个过程应该就需要将版本一个个的回退,所以客户端在请求完更新数据的接口后,如果有可更新的数据,应该在更新数据完毕后再请求一次这个接口(或者在后台的返回状态中配置客户端这么做也可以),这样客户端就有条件可以实现一步步的版本回退。

END(如果写的不好请指出,我会及时改正^_^)