Android高性能日志写入方案的实现-创新互联
前言
在西双版纳等地区,都构建了全面的区域性战略布局,加强发展的系统性、市场前瞻性、产品创新能力,以专注、极致的服务理念,为客户提供网站设计、成都网站建设 网站设计制作按需制作,公司网站建设,企业网站建设,品牌网站建设,成都营销网站建设,成都外贸网站建设,西双版纳网站建设费用合理。公司目前在做一款企业级智能客服系统,对于系统稳定性要求很高,不过难保用户在使用中不会出现问题,而 Android SDK 集成在客户的 APP 中,同时由于 Android 碎片化的问题,对于 SDK 的问题排查就显得尤为困难,因此记录下用户的操作日志就显得极为重要。
初始方案
一开始,SDK 记录日志的方式是直接通过写文件,当有一条日志要写入的时候,首先,打开文件,然后写入日志,最后关闭文件。这样做的问题就在于频繁的IO操作,影响程序的性能,而且 SDK 为了保证消息的及时性,还维护了一个后台进程,当其中一个进程进行日志写入时,另一个就会被锁在门外等着,问题就愈发严重。使用这种方案虽然当前看上去对程序的影响不大,但是随着日志量的增加,更多的IO操作,一定会造成性能瓶颈。
下面我们来分析下直接写入文件的流程:
- 用户发起 write 操作
- 操作系统查找页缓存
a.若未命中,则产生缺页异常,然后创建页缓存,将用户传入的内容写入页缓存
b.若命中,则直接将用户传入的内容写入页缓存 - 用户 write 调用完成
- 页被修改后成为脏页,操作系统有两种机制将脏页写回磁盘
a.用户手动调用 fsync()
b.由 pdflush 进程定时将脏页写回磁盘
可以看出,数据从程序写入到磁盘的过程中,其实牵涉到两次数据拷贝:一次是用户空间内存拷贝到内核空间的缓存,一次是回写时内核空间的缓存到硬盘的拷贝。当发生回写时也涉及到了内核空间和用户空间频繁切换。
而且相对于机械硬盘,SSD 存储还有一个“写入放大”的问题。这个问题主要和 SSD 存储的物理结构有关。当 SSD 被全部写过一遍之后,再写入的数据是不可以直接更新,只可以通过覆盖重写,在覆盖之前需要先擦除数据。但写入的最小单位是 Page,擦除的最小单位是 Block,而 Block 远大于 Page,所以在写入新数据时就需要先把 Block 上的数据读出来和要写入的数据合并在一起,再把 Block 擦除,最后把读出来的数据重新写入到存储上,这样导致实际写入的数据可能远远大于最开始需要写入的数据。
没想到简单的写文件竟然涉及了这么多操作,只是对于应用层透明而已。
既然每写一次文件会执行这么多次操作,那么我们能不能将日志缓存起来,当达到一定的数量后再一次性的写入磁盘中呢?
这样确实能够大量减少 IO 次数,但是却会引发另一个更严重的问题——丢日志
把日志缓存在内存中,当程序发生 Crash 或进程被杀后就无法保证日志的完整性,而且由于 SDK 存在多进程,也无法保证多进程下日志的顺序。
一个完善的日志方案,需要满足
- 高效,不能影响系统性能,不能因为引入了日志模块而造成应用卡顿
- 保证日志的完整性,如果不能保证日志完整,那么日志收集就没有意义了
- 对于多进程应用,要保证最终看到的日志顺序的准确性
高性能方案
既然无法减少写入次数,那么我们能不能在写文件的过程中去优化呢?
答案是可以的,使用 mmap
mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系,函数原型如下
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
当前标题:Android高性能日志写入方案的实现-创新互联
本文网址:http://ybzwz.com/article/jcioh.html