在新浪微博的app中,从别的页面进入主页,在没有网络的情况下,首页中的已经收到的微博还是能显示的,这显然是把相关的数据存储在app本地。

使用数据的app本地存储,能减少网络的流量,同时极大提高了用户的体验(想想,很多数据都能在app本地获取,显示的速度当然快)。使用了本地存储后,需要考虑的是数据的增量更新方案。

什么是数据的增量更新?假设,用户A的首页在数据表中是有40条数据,id1-40,app每次获取10条数据。第一次运行,app从数据表获取了id1-10条数据同时存储在本地。假设用户离开了这个页面再回到首页,这时app需要再次从数据库中获取数据,由于之前已经有10条数据(id1-10)存储在app本地了,那么现在需要从数据库中获取的10条数据就是从剩余的30条中数据获取(id11-40)后并保存在app本地。这个就是增量更新的典型例子。

增量更新的原理是在数据库中,每条数据都必须有update_time这个值,记录数据最后更新的时间,当app从服务器获取了一次数据后(返回的数据必须按时间排序,update_time最近的在第一条),记录下第一条数据的update_time,当再次获取数据就只需要获取上个时间点到访问服务器这刻为止所更新的数据即可。

因为分页机制的存在,这个算法实现起来是挺多需要注意的地方,下面我举一个简化的例子详细说明:

一些假设:

1. app每次请求都带4个参数

http://test/api/timeline?count=3&page=1&since=1100&max=1200

count: 每页的显示条数(默认为3)

page: 当前页码(默认为1)

since: 时间戳,若指定此参数,则返回时间戳大于等于since的结果(应该是上次获取的最新数据的update_time)

max: 时间戳,若指定此参数,则返回时间戳少于等于max的结果(应该是发送时的时间)

在sql的查询时,使用条件 since<=update_time<= max

2. api 返回的数据包含

{

"size": 10,   //实际返回的数据量(因为分页获取的缘故,所以经常少于total值)

"total": 284,  //应该返回的总数据量

"page": 1,

"count": 3,

"max": 0, //max为获取的最后一条数据的update_time

"since": 0

},

{ //返回的数据实体

data:.......

}

3. app存储的本地数据中的update_time是指服务器中的这条数据的更新时间,不是指app中这条数据的更新时间。

现在开始讨论:

(1)当app安装完毕后还没启动,服务器的数据表中的数据为3条,app存储的本地数据为空

服务器的数据表的数据

id

update_time

1

1100

2

1101

3

1101

app存储的本地数据

id

update_time

(2)当app第一次运行(时间为11:05),因为是第一次运行,since为0,max为现在的时间点1105,在服务器的数据表中获取所有数据。

发送的请求为:http://test/api/timeline?count=3&page=1&since=0&max=1105

(3)从(2)中发送请求后,api的返回数据,服务器的数据表中的数据,app存储的本地数据如下:

api返回的数据

{

"size": 3,   //实际返回的数据量

"total": 3,  //应该返回的总数据量

"page": 1,

"count": 3,

"max": 1101,

"since":0

},

{ //返回的数据实体

data:.......

}

服务器的数据表的数据

id

update_time

1

1100

2

1101

3

1101

app存储的本地数据

id

update_time

1

1100

2

1101

3

1101

这里是策略的重点(1)api返回数据中的max必须为最后一条数据的update_time

(4)现在的时间是11:20,用户点击了页面中“获取更多”的按钮,app应该从服务器的数据表中拉取数据,在发送请求前,服务器的数据表中的数据如下:

服务器的数据表的数据

id

update_time

1

1100

2

1101

3

1101

4

1118

5

1118

6

1119

7

1119

可看到,比起上次拉取数据的时候,服务器的数据表多了id为4,5,6,7的数据。

这时发送api请求,策略的重点(2):当api的返回数据size=total时,since值比上次获取大一点,因为这时数据已经获取完整了,没必要重复获取数据上次已经获取的数据(记得条件since<=update_time<= max 吗?)所以since值设置为1101+1=1102,max为现在的时间点:1120,请求的url如下:

http://test/api/timeline?count=3&page=1&since=1102&max=1120

发送请求后api的返回数据和app存储的本地数据如下:

api返回的数据

{

"size": 3,   //实际返回的数据量(因为分页获取的缘故,所以经常少于total值)

"total": 4,  //应该返回的总数据量

"page": 1,

"count": 3,

"max": 1119,

"since":1102

},

{ //返回的数据实体

data:.......

}

app的数据:

id

update_time

1

1100

2

1101

3

1101

4

1118

5

1118

6

1119

这里是策略的重点(3)在数据库中,update_time为1101~1120的数据有4条,但由于分页的缘故,只获取了3条(从size和total参数可以判定),这意味着1101~1120这段时间的数据没有获取完整,app所获取的最后一条数据的update_time是1119,服务器的数据表中剩下的没有被app获取的数据有两种情况:

a.update_time刚好是1119

b.update_time大于1119

 

   由于我们没法判断属于哪种种情况,如果我们下次拉数据的时候 since大于1119,服务器的数据表中id为7的数据不会再获取,那么会造成app中丢失了id为7的数据,所以针对上次数据获取不完整的情况,下次获取数据时since必须是等于1119,虽然有可能会获取重复的数据。

(5)现在的时间是11:30,用户点击了页面中“获取更多”的按钮,app应该从服务器的数据表中拉取数据,在发送请求前,服务器的数据表中的数据如下:

服务器的数据表的数据

id

update_time

1

1100

2

1101

3

1101

4

1118

5

1118

6

1119

7

1119

8

1120

这时发送api请求,这里是策略的重点(4):当api的返回数据size少于total,为了避免有数据丢失,since为上次收到api的返回数据的max值:1119,max为现在的时间点:1130。关于策略重点(4),请结合策略的重点(3)一起理解。

 

请求的url如下:

http://test/api/timeline?count=3&page=1&since=1119&max=1130

发送请求后api的返回数据和app存储的本地数据如下:

api返回的数据

{

"size": 3,   //实际返回的数据量(因为分页获取的缘故,所以经常少于total值)

"total": 3,  //应该返回的总数据量

"page": 1,

"count": 3,

"max": 1120,

"since":1119

},

{ //返回的数据实体

data:.......

}

这是策略的重点(5):api中返回数据中id为6的数据,在app的本地数据中已经存在,对于这条数据,app端应该放弃重复插入。

最后app存储的本地数据如下:

app的数据:

id

update_time

1

1100

2

1101

3

1101

4

1118

5

1118

6

1119

7

1119

8

1120

ok,整个增量更新的策略已经分析完毕了。在这个策略中,page参数几乎没用,之所以要保留,是为了兼容分页不带since,max的情况。对于这个增量更新的策略,请仔细理解策略的重点(1)(2)(3)(4)(5)的分析。

增量更新的策略,还要处理一个删除数据的同步问题。假设,在服务器的数据表要删除一条数据,怎么通知app本地也删除这条数据。我们的解决方案是服务器的服务器的数据表中增加一个标识is_delete,当需要在业务逻辑上删除的时候,把这条数据的is_delete设为1,同时更新update_time。当app增量更新检测到这条is_delete为1的数据,就在app本地数据中把这条数据删除。为了避免在服务器保存太多的数据,在服务器设置一个crontab,定期把那些已经标识is_delete设为1已经一段时间的数据删除。

这个增量更新的策略,适用于需要分页显示的app页面。

app后端系列文章总目录

如果您觉得这系列的文章对你有所帮助,欢迎打赏。

支付宝账号:190678908@qq.com 收款人:曾健生

[文章作者]曾健生

[作者邮箱]h6k65@126.com

[作者QQ]190678908

[新浪微博] @newjueqi

[博客]http://blog.csdn.net/newjueqi

http://blog.sina.com.cn/h6k65

版权声明:本文为博主原创文章,未经博主允许不得转载。

app后端设计(10)--数据增量更新的更多相关文章

  1. **app后端设计(10)--数据增量更新(省流量)

    在新浪微博的app中,从别的页面进入主页,在没有网络的情况下,首页中的已经收到的微博还是能显示的,这显然是把相关的数据存储在app本地. 使用数据的app本地存储,能减少网络的流量,同时极大提高了用户 ...

  2. app后端设计--总目录 (转)

    特此说明,我转载的!!! app后端设计(1)--api app后端设计(2)--xmpp的使用 app后端设计(3)--短信,邮件,推送服务 app后端设计(4)-- 通讯的安全性 app后端设计( ...

  3. app后端设计--总目录

    做了3年app相关的系统架构,api设计,先后在3个创业公司中工作,经历过手机网页端,android客户端,iphone客户端,现就职于app云后端平台bmob(想了解bmob点击这里).其中的乐与苦 ...

  4. app后端设计(0)--总文件夹

    原文:http://blog.csdn.net/newjueqi/article/details/19003775 做了接近两年app相关的系统架构,api设计,先后在两个创业公司中工作,经历过手机网 ...

  5. [置顶] app后端设计--总目录

    版权声明:本文为博主原创文章,未经博主允许不得转载. 做了3年app相关的系统架构,api设计,先后在3个创业公司中工作,经历过手机网页端,Android客户端,iphone客户端,现就职于app云后 ...

  6. app后端设计(0)--总目录(转)

    原文:http://blog.csdn.net/newjueqi/article/details/19003775 做了接近两年app相关的系统架构,api设计,先后在两个创业公司中工作,经历过手机网 ...

  7. app后端设计(php)

    来源:http://blog.csdn.net/column/details/mobilebackend.html?page=1 做了3年app相关的系统架构,api设计,先后在3个创业公司中工作,经 ...

  8. Package设计2:增量更新

    SSIS 设计系列: Package设计1:选择数据类型.暂存数据和并发 Package设计2:增量更新 Package 设计3:数据源的提取和使用暂存 一般来说,ETL实现增量更新的方式有两种,第一 ...

  9. Kettle中通过触发器方式实现数据 增量更新

    在使用Kettle进行数据同步的时候, 共有 1.使用时间戳进行数据增量更新 2.使用数据库日志进行数据增量更新 3.使用触发器+快照表 进行数据增量更新 今天要介绍的是第3中方法. 实验的思路是这样 ...

随机推荐

  1. 部署与管理ZooKeepe

    1.部署 本章节主要讲述如何部署ZooKeeper,包括以下三部分的内容: 1. 系统环境 2. 集群模式的配置 3. 单机模式的配置 系统环境和集群模式配置这两节内容大体讲述了如何部署一个能够用于生 ...

  2. linux下64位汇编的系统调用(5)

    看到这里大家都基本知道了如何进行linux下的汇编系统调用:不过有些童鞋可能会问:那些C库中函数里为我们解决的额外汇编代码你是怎么知道的? 好吧,我承认:我是通过逆向知道的,这貌似有点犯规的嫌疑- 比 ...

  3. iOS中tableView组头部或尾部标题的设置

    解决在tableView返回组标题直接返回字符串,带来的不便设置组标题样式的问题解决办法,设置尾部标题和此类似  // 返回组头部view的高度 - (CGFloat)tableView:(UITab ...

  4. javascript学习(三)——常用方法(2)

    一.兼容性较高的浏览器页面关闭 //关闭网页,不支持火狐(火狐返回上次浏览页面)   //FireFox非window.open()等弹出页面,需要在地址栏中输入about:config, 然后将do ...

  5. JDK内置工具之一——JMap(java memory map)

    1.介绍 打印出某个java进程(使用pid)内存内的,所有‘对象’的情况(如:产生那些对象,及其数量). 可以输出所有内存中对象的工具,甚至可以将VM 中的heap,以二进制输出成文本.使用方法 j ...

  6. 深入理解SpringCloud之配置刷新

    我们知道在SpringCloud中,当配置变更时,我们通过访问http://xxxx/refresh,可以在不启动服务的情况下获取最新的配置,那么它是如何做到的呢,当我们更改数据库配置并刷新后,如何能 ...

  7. myBatis之入门示例

    1. myBatis目录结构: --src ---entity [POJO类] ---mappers [映射类] ----*Mapper.java [方法接口,相当于Dao] ----*Mapper. ...

  8. Python爬虫 - 爬取百度html代码前200行

    Python爬虫 - 爬取百度html代码前200行 - 改进版,  增加了对字符串的.strip()处理 源代码如下: # 改进版, 增加了 .strip()方法的使用 # coding=utf-8 ...

  9. Java多线程:synchronized的可重入性

    从Java多线程:线程间通信之volatile与sychronized这篇文章中我们了解了synchronized的基本特性,知道了一旦有一个线程访问某个对象的synchronized修饰的方法或代码 ...

  10. 连接到放置本地yum源服务器之前的注意事项

    1.确认系统防火墙关闭 2.启动httpd服务 service httpd start 如果提示没有httpd服务: 安装httpd服务 yum install -y httpd 作者:Daley Z ...