AICon 上海站|90%日程已就绪,解锁Al未来! 了解详情
写点什么

苏宁 11.11:苏宁物流移动端百万级离线数据处理方案

  • 2018-11-09
  • 本文字数:2950 字

    阅读完需:约 10 分钟

苏宁11.11:苏宁物流移动端百万级离线数据处理方案

本文为“InfoQ x 苏宁 2018 双十一”特别策划系列文章之一。

背景

随着苏宁物流的发展,对网点的整合,很好的强化了苏宁物流最后一公里配送能力,可以在相对短期内整合双方在仓储、干线、末端等方面的快递网络资源,编织一张覆盖最后一公里的密织高效网络,整合完成将极大地强化苏宁物流最后一公里配送能力,提高配送效率,降低运营成本。


其中物流移动端在仓储,干线和末端等各个环节都扮演一个非常重要的角色,然而移动端的用户和我们一般的移动互联网用户有相同的地方也有很大区别,首先物流移动端的用户基本上是我们处在一线的操作人员,界面的交互和服务的响应速度直接影响用户的作业速度。另外移动互联网用户一般是手机,而物流移动端的用户包括手机和手持码枪,这就对我们移动应用提出了更高的要求,不仅仅要具有手机的灵活性还要有手持的实用性。最后随着 3G,4G 的普及移动互联网用户手机联网性普通很好,但是我们的一线作业人员可能处在仓库的角落,信号的盲区,这就要求我们的应用不仅要在有信号的情况下可以作业,在无信号的状态下也可以正常作业。


为了实现各大区网点不能满足实时的网络交互环境的情况下正常作业,这就要求我们操作终端多向前走一步,同时能满足各网点无论在网络环境好坏的情况下都能完成作业。如果满足这些条件就需要本来在服务端处理的数据下载到码枪,在码枪本地本地进行校验处理,服务器的主数据量在都在万级以上,最大的一张表数据在百万级,而正常作业下我们用的码枪都是安卓系统,运行内存在 2G,4G,有的码枪甚至处理内存在 1G。如果通过对码枪的更新换代可以更换性能更好的码枪,但是这种成本是巨大的 ,所以通过对技术上的实现去节省成本和增加用户体验是我们苏宁物流要首当其冲面对的问题。

我们的解决方案

一、数据处理实际逻辑

  1. 通过网络下载 txt 文件

  2. 按行读取每行字符串文本

  3. 采用 String.split 函数按照指定的格式分割字符串为字符数组

  4. 通过字符数组组装 Entity 数据类,讲数据类添加到 List 里缓存

  5. 判断 List 的 Size 大小,达到 3000 条后调用 Greendao 方法启动线程开启事务批量插入,清空 List 继续循环

  6. 未达 3000 条继续循环走 2 流程



图 1、数据处理逻辑流程

二、性能瓶颈分析

使用 TraceView 找到性能的瓶颈,TraceView 是 Android SDK 中内置的一个工具,它可以加载 trace 文件,用图形的形式展示代码的执行时间、次数及调用栈。


生成 trace 文件有三种方法:


  • 使用代码

  • 使用 Android Studio

  • 使用 DDMS



图 2、开始 trace



图 3、写入到 trace 文件



图 4、执行耗时分析



图 5、函数耗时分析


通过 TraceView 分析,另外结合代码分析总结出几个性能瓶颈


  1. String .split 采用正则匹配,大数据量效率低

  2. 读文件比较耗时,如果数据库速度大于文件读取速度,则会等待文件读取

  3. 插入数据库也比较耗时,如果插入速度小于文件读取速度,则文件读取会阻塞等待

  4. 每次使用 GreenDao 开启线程,没有限制最大线程数,开启大量线程,导致数据库连接数超出上限,极易 OOM

  5. 另外,通过代码日志分析得出,Entity 没有重用,每次循环创建新的 Entity,导致内存不足,频繁耗时 GC,界面卡顿。

三、解决方案:

  1. 针对 Spilt 的优化:重写了 String.split 的实现方式,采用效率更高的 index 方式代替原来的正则匹配,依据业务特点固定了分割数目

  2. 针对对象没有重用的优化:采用动态事务,在事务中采用单条连续插入数据库,操作避免创建多余对象,达到指定数量后一起 Commit 到数据库

  3. 单次提交数量优化:经过多次测试,统计出单次提交 12000 条为效率最高的提交量

  4. 针对相互等待问题:采用阻塞队列,对象池方式减少等待


具体流程图如下:



图 6、优化流程图

四、结果对比:

通过测试的对比可以发现,同样的数据,合理匹配文件的读写速度和数据库的读写速度,减少等待间隔。


  • 在单次 8000 量的时候 ,速度可以比原来缩减 3 分 40 秒,

  • 在单次 10000 量的时候可以比原来缩减 3 分 50 秒,

  • 在单量 15000 量的时候可以缩减 3 分 50 秒左右,

  • 在单次 12000 量的时候可以缩减 3 分 57 秒。



图 7、优化对比

五、优化方案+

完成的上步之后,虽然性能上有了很大的提升,但是为了给一线的苏宁物流用户提供更优的用户体验,我们想到了优化方案+。

1. 传统 Rollback Journal

SQLite 实现 原子性提交和回滚操作 的默认方法是 rollback journal。当对 DB 进行写操作的时候,SQLite 首先将准备要修改部分的原始内容(以 Page 为单位)拷贝到“回滚日志”中,用于后续可能的 Rollback 操作以及 Crash、断电等意外造成写操作中断时恢复 DB 的原始状态,回滚日志存放于名为“DB 文件名-journal”的独立文件中。对原始内容做备份后,才能写入修改后的内容到 DB 主文件中,当写入操作完成,用户提交事务后,SQLite 清空 -journal 的内容,至此完成一个完整的写事务。


Rollback 模式中,每次拷贝原始内容或写入新内容后,都需要确保之前写入的数据真正写入到磁盘,而不是缓存在操作系统中,这需要发起一次 fsync 操作,通知并等待操作系统将缓存真正写入磁盘,这个过程十分耗时。


除了耗时的 fsync 操作,写入 -journal 以及 DB 主文件的时候,是需要独占整个 DB 的,否则别的线程/进程可能读取到写到一半的内容。这样的设计使得写操作与读操作是互斥的,并发性很差。

2. WAL 模式的应用

WAL 模式则改变了上述流程,写操作不直接写入 DB 主文件,而是写到“DB 文件名-wal”文件(以下简称“-wal”)的末尾,并且通过 -shm 共享内存文件来实现 -wal 内容的索引。读操作时,将结合 DB 主文件以及 -wal 的内容返回结果。由于读操作只读取 DB 主文件和 -wal 前面没在写的部分,不需要读取写操作正在写到一半的内容,WAL 模式下读与写操作的并发由此实现。


WAL 写操作除了上面的流程,还增加了一步:Checkpoint,即将 -wal 的内容与合并到 DB 主文件。 由于写操作将内容临时写到 -wal 文件,-wal 文件会不断增大且拖慢读操作,因此需要定期进行 Checkpoint 操作将 -wal 文件保持在合理的大小。Checkpoint 操作比较耗时且会阻塞读操作,但由于时效性要求较低,遇到堵塞可以暂时放弃继续 DB 读写操作,不至于太过影响读写性能。SQLite 官方默认的 Checkpoint 阈值是 1000 page,即当 -wal 文件达到 1000 page 大小时,写操作的线程在完成写操作后同步进行 Checkpoint 操作;Android Framework 的 Checkpoint 阈值是 100 page。


基于 WAL 的基本工作方式,做额外的两个优化点:


  1. 写入 -wal 文件时不进行 fsync 操作,因为 -wal 文件损坏只影响新写入的没 Checkpoint 部分数据而非整个数据库损坏,影响相对小;

  2. 将需要进行 fsync 的 Checkpoint 操作放到独立线程执行,让写操作能尽快返回。

结束语

物流行业快速发展的今天移动端在作业中的占比量越来越大,苏宁物流一线的作业人员的手持数量每年也在呈几何倍数上涨,在苏宁物流移动端速度发展的这些年,我们从老的黑白机到现在的智能安卓机,从传统过渡到移动互联网和正在进行的智能化,我们一直坚持用技术为一线作业人员减轻作业负责,提升作业效率,在这种大环境下对我们苏宁物流来说是压力也是机遇,不忘初心,砥砺前行,始终不忘造极,以极客的精神打造极致的用户体验。


作者:李健普,苏宁易购物流研发中心移动端开发工程师,负责物流移动端的开发工作,曾负责开发了快递,分拨,仓库,售后等客户端 ,具有较丰富的移动开发经验,对物流作业相关设备开发经验丰富。


2018-11-09 09:562166
用户头像

发布了 164 篇内容, 共 111.5 次阅读, 收获喜欢 392 次。

关注

评论 11 条评论

发布
用户头像
厉害了,又学到了不少
2018-11-13 16:28
回复
用户头像
牛B
2018-11-13 16:24
回复
用户头像
牛B
2018-11-13 16:24
回复
用户头像
学习了
2018-11-13 16:22
回复
用户头像
牛逼了,受益匪浅,点赞👍
2018-11-13 16:20
回复
用户头像
加个队列也能写一篇。。
2018-11-09 15:52
回复
用户头像
深入浅出,收获很多。感谢楼主分享。
2018-11-09 11:58
回复
用户头像
作者有心了,感谢分享。
2018-11-09 11:05
回复
用户头像
很棒,支持一下。受益匪浅。感谢楼主分享
2018-11-09 10:35
回复
用户头像
很棒,支持一下。受益匪浅。感谢楼主分享
2018-11-09 10:34
回复
用户头像
支持作者,学习到很多,棒棒哒!
2018-11-09 10:32
回复
没有更多了
发现更多内容

kafka面试题阿里,牛客网面试题库java,农民工看完都会了

Java 程序员 后端

linux基础入门知识,kafka入门教程步骤,2021年Java网络编程总结篇

Java 程序员 后端

Mybatis-plus常用API全套教程,springcloud实战视频

Java 程序员 后端

mybatis入门案例,图灵学院第三期视频百度云,阿里中间件

Java 程序员 后端

MyBatis整体架构分析:牛客网字节跳动面试,你连原理都还没弄明白

Java 程序员 后端

netty实战pdf,拉钩教育java就业集训班,Java开发热门前沿知识

Java 程序员 后端

JVM面试题附答案,Dubbo高频面试题+解析

Java 程序员 后端

Kafka必须掌握的核心技术,尚硅谷java入门,Nginx面试

Java 程序员 后端

mybatis总结,java思想编程百度网盘,Java开发工程师常见面试题

Java 程序员 后端

MyBatis整体架构分析:腾讯笔试牛客,阿里程序员的Java之路

Java 程序员 后端

linux内核教程推荐,尚硅谷mysql,一文轻松搞定

Java 程序员 后端

mysql的面试题及答案,尚硅谷与达内哪家好,Java虚拟机学习集锦是我攒来的

Java 程序员 后端

MySQL+Tomcat+JVM,看完还怕面试官,阿里程序员的Java之路

Java 程序员 后端

MySQL优化原理分析及优化方案总结,Java入门

Java 程序员 后端

mysql基础教程视频,牛客网中级项目异步队列,这操作真香

Java 程序员 后端

mysql数据库优化面试,孙鑫java视频教程百度网盘,程序员必学

Java 程序员 后端

linux基础教程试题,linux驱动程序开发视频教程,netty入门与实战

Java 程序员 后端

linux学习笔记,springboot前后端分离教程,我先收藏为敬

Java 程序员 后端

mybatis面试题及答案,尚硅谷java入门,专属于Java程序员的学习福音

Java 程序员 后端

kafka实战最佳经验,牛客网华为机试题答案,Java开发面试问题

Java 程序员 后端

Mybatis-Plus的使用小技巧,linux数据恢复教程,Nginx面试

Java 程序员 后端

kafka入门与实践百度云,牛客网平安科技,和快手大牛的技术面谈

Java 程序员 后端

kafka入门教程,springboot高级教程,聊聊你对分布式锁技术方案的理解

Java 程序员 后端

linux应用开发面试题,java黑马程序员资源,如何保证redis高可用

Java 程序员 后端

Mycat和Mysql搭建高可用企业数据库集群,程序员35岁真的是分水岭吗

Java 程序员 后端

netty框架教程,mysql数据库教程视频指导版,HTTPS面试常问全解析

Java 程序员 后端

kafka入门教程,linux教程第四版pdf下载,mysql基础教程

Java 程序员 后端

kafka教程pdf,spring入门教程,你会的还只有初级工程师的技术吗

Java 程序员 后端

linux内核教程推荐,兄弟连尚硅谷,顺利收获Offer

Java 程序员 后端

linux应用开发面试题,极客时间破解付费课程百度云盘,Java面试官

Java 程序员 后端

netty架构图,linux教程视频,缓存+一致性哈希+分布式面试题

Java 程序员 后端

苏宁11.11:苏宁物流移动端百万级离线数据处理方案_5G/IoT_徐川_InfoQ精选文章