是当系统处理大量磁盘 IO 操作的时候,由于 CPU 和内存的速度远高于磁盘,可能导致 CPU 耗费太多时间等待磁盘返回处理的结果。对于这部分 CPU 在 IO 上的开销,我们称为 “iowait”。

iowait 怎么查看呢?

如果你用的是 Linux 系统或者 Mac 系统,当你在执行一项很耗费磁盘 IO 的操作时,比如读写大文件,通过 top 命令便可以看到。如下图所示:

CPU 开销示意图

其中的 2.6 wa 便是 iowait 占用了 2.6% CPU。

那么,这种 CPU 开销对性能会有什么影响呢?特别是像秒杀这样的高并发系统,当秒杀服务运行的时候,会输出大量信息到日志文件,比如程序报错信息、请求参数的调试信息等,而这些写日志文件无疑会给磁盘带来更大的压力,导致更多的 CPU 开销。所以,这一讲我们就主要来探讨下这个问题。

秒杀日志面临的问题
对于并发不高的服务,我们可以把所有需要的日志写入到磁盘上的日志文件里。但是,在高峰期间,秒杀服务单节点需要处理的请求 QPS 可能达到 10 万以上。一个请求从进入秒杀服务到处理失败或者成功,至少会产生两条日志。也就是说,高峰期间,一个秒杀节点每秒产生的日志可能达到 30 万条以上。

这是什么概念?

磁盘有个性能指标:IOPS,即每秒读写次数。一块性能比较好的固态硬盘,IOPS 大概在 3 万左右。也就是说,一个秒杀节点的每秒日志条数是固态硬盘 IOPS 的 10 倍!如果这些日志每次请求时都立即写入磁盘,磁盘根本扛不住,更别说通过网络写入到监控系统中。

所以,秒杀日志会面临的第一个问题是,每秒日志量远高于磁盘 IOPS,直接写磁盘会影响服务性能和稳定性

另外,服务在输出日志前,需要先分配内存对日志信息进行拼接。日志输出完,还需要释放该日志的内存。这将会导致什么问题呢?

对于那些有内存垃圾回收器的语言,如 Java 和 Golang ,频繁分配和释放内存,可能会导致内存垃圾回收器频繁回收内存,而回收内存的时候又会导致 CPU 占用率大幅升高,进而影响服务性能和稳定性。

那些没有内存垃圾回收器的语言,如 C++ ,又会受什么影响呢?它们通常是从堆内存中分配内存,而大量的分配、释放堆内存可能会导致内存碎片,影响服务性能。

所以,秒杀日志会面临的第二个问题是,大量日志导致服务频繁分配,频繁释放内存,影响服务性能

最后,秒杀日志还会面临服务异常退出丢失大量日志的问题

我们知道,由于秒杀服务处理的请求量太大,每秒都会有很多请求的日志未写入磁盘。如果秒杀服务突然出问题挂掉了,那这批日志可能就会丢失。

对于高并发系统,这在所难免,问题是如何把控好写入日志的时间窗口,将丢失的日志条数控制在一个很小的可接受范围内。

这就是秒杀日志面临的第三个问题。通过上面的介绍,想必你也明白了,像秒杀这种大流量业务场景下,日志收集是个大难题,也是个必须要解决的性能问题。

如何优化秒杀日志性能?
前面我们了解到,秒杀日志面临着磁盘 IO 高、内存压力大、大量丢失等风险,归根结底,还是因为日志量太大,常规日志保存手段已经无法发挥作用。怎么办呢?接下来我就对这几个问题一一介绍下。

磁盘 IO 性能优化
首先,我们来看下秒杀日志量超过磁盘 IOPS 的问题。

上一讲我给你介绍了多级缓存,你是否还记得内存性能和磁盘性能的差别呢?没错,内存性能远高于磁盘性能。那我们能否利用内存来降低磁盘压力,提升写日志的性能呢?答案是可以。

Linux 有一种特殊的文件系统:tmpfs,即临时文件系统,它是一种基于内存的文件系统。当使用临时文件系统时,你以为在程序中写文件是写入到磁盘,实际上是写入到了内存中。临时文件系统中的文件虽然在内存中,但不会随着应用程序退出而丢失,因为它是由操作系统管理的。

由于云架构保障了云主机的高可用,只要操作系统正常运行,也没有人删除文件,临时文件系统中的文件就不会丢失。所以,我们可以将秒杀服务写日志的文件放在临时文件系统中。相比直接写磁盘,在临时文件系统中写日志的性能至少能提升 100 倍

当然,临时文件系统中的日志文件也不能无限制地写,否则临时文件系统的内存迟早被占满。那该怎么办呢?可以这样处理,比如,每当日志文件达到 20MB 的时候,就将日志文件转移到磁盘上,并将临时文件系统中的日志文件清空。 相比频繁的小数据写入,磁盘在顺序写入大文件的时候性能更高,也就降低了写入压力。

内存分配性能优化
不知道你学过 C 语言没?如果学过的话,你应该对 malloc 函数和 free 函数不陌生。malloc 函数主要用于从堆内存中分配内存,而 free 函数则是将使用完的内存归还到堆内存中。堆内存是由系统管理的,当堆内存中有大量碎片时,为了找到合适大小的存储空间,可能需要比对多次才能找到,这无疑让程序性能大打折扣。

而秒杀服务在输出大量日志的时候会存在频繁的内存分配和归还,如果使用常规方式分配内存,会导致高并发下性能下降。所以,我们需要使用高效的内存管理,既能快速分配内存,又能避免频繁触发垃圾回收器回收内存。

怎么做呢?

我们可以参考共享单车运营方的做法。像摩拜、哈罗、青桔等,单车的起步和归还都在人流量大的投放点,而不是运营方仓库。假如在程序中,我们也能像共享单车一样,根据实际业务自己管理内存的分配和归还,就能避免譬如内存碎片和内存垃圾回收,导致性能降低的问题。

具体怎么实现?

对于秒杀系统来说,它的日志里需要附加一些信息,以便后面排查问题或者数据统计,这些附加信息有用户 ID、来源 IP、抢购的商品 ID、时间等。但日志文件是纯文本的,而附加信息中有的是整数,有的是字符串,这就需要统一拼接成字符串才能输出到文本文件中。然而,在像 Java、Golang 这类高级语言中,字符串是一个经过封装的对象,底层是字符数组。直接用字符串拼接的话,会导致程序分配新的字符串对象来保存拼接后的结果。

比如下面的代码就会触发内存分配。

str := "hello " + userName

如何避免字符串内存分配呢?一般我们可以直接使用字符数组,基于字符数组做参数拼接。典型的例子是实现一个带字符数组缓冲区的日志对象,提供类似 AppendInt、AppendString 这样的方法拼接参数。比如下面这部分。

type Logger struct{data []byte
}
const maxDataSize = 65536
func NewLogger() *Logger {l := &Logger{data: make([]byte, 0, maxDataSize)}
}
// 整数转成字符数组并追加到缓冲区
func (l *Logger)AppendInt(data int){d := strconv.Itoa(data)l.data = append(l.data, d...)l.tryFlush()
}
// 字符串转成字符数组并追加到缓冲区
func (l *Logger)AppendString(data string){l.data = append(l.data, []byte(data)...)l.tryFlush()
}
// 关闭 Logger,将缓冲区中数据写入到日志文件中。通常在程序退出前调用该函数。
func (l *Logger)Close(){l.Flush()
}
func (l *Logger)Flush(){// 此处省略具体写文件的代码,大家可以自行练习// 将字符切片指向 l.data 的头部,清空缓冲区l.data = l.data[0:0]
}
func (l *Logger)tryFlush(){// 超过 64KB 则写入到磁盘if len(l.data) >= maxDataSize {l.Flush()}
}

在上面的代码实现中,每个 Append 函数中采用追加的方式拼接参数,在缓冲区足够用的情况下,不会为拼接后的数据重新分配内存。

怎么确保缓冲区足够用呢?
答案是最后面的 tryFlush 函数,它能控制缓冲区中的内容不会过大。 当 tryFlush 函数发现数据长度超过设定的最大值时,会将数据写入到日志文件中并清空缓冲区。在这个过程中,Logger 不需要归还、再分配缓冲区。

当然,以上只是个简单的示例,真正生产环境中用的 Logger 要强大很多。感兴趣的可以看看 zap、logrus 等 Logger 的实现。

如何减小丢日志的风险
前面我们了解到,秒杀服务在高并发下发生异常的时候可能导致部分日志丢失。我们还了解到,秒杀服务日志不能实时写入到日志文件。有没有发现,这两件事情是互相矛盾的?实际上,在高并发下,我们无法彻底解决丢日志的风险,只能减小丢日志的概率。为啥呢?

在高并发下,我们需要尽可能将日志先缓存到程序本地内存中,也就是 Logger 的缓冲区中。当日志到一定量后,批量写入日志文件,以便达到良好的写入性能。但是,假如程序异常退出,而缓冲区中日志大小又没达到批量写入的条件,这部分日志就可能丢弃了。

怎么办呢?

程序异常有两种:一种是能捕获的可控异常,比如 Golang 中数组越界触发 panic;一种是无法捕获的不可控异常,比如 Golang 中并发读写未加锁的 map。

这两种异常下,如何尽可能将缓冲区中的日志写入日志文件呢?

对于第一种情况,通常是捕获异常,在退出程序前执行实例代码中的 Close 函数将日志写入到日志文件。对于第二种情况,我们可以采用定时器,定时将缓冲区中的数据写入到日志文件中,比如定时 100 毫秒执行 Flush 函数

本文章内容为转载,如有侵权请联系作者下架。

高性能日志:如何提升日志性能避免 IO 瓶颈?相关推荐

  1. 微服务架构 | 如何利用好日志链路追踪做性能分析?

    导读:做性能分析听到最多的歪理就是,服务做水平.垂直扩容.分表分库.读写分离.XX中间件.资源静态化等等但是归根到底这些方案都是为了尽可能减少对数据库的访问以及堆栈的释放,提高数据库IO的读写速度和程 ...

  2. 主流日志框架使用及性能对比

    一.摘要 不管是使用何种编程语言,何种框架,日志输出几乎无处不再,也是任何商业软件中必不可少的一部分. 总结起来,日志的用途大致可以归纳成以下三种: 问题追踪:通过日志不仅仅包括我们程序的一些bug, ...

  3. SQL Server中事务日志自动增长对性能的影响

    SQL Server中事务日志自动增长对性能的影响 SQL Server中事务日志自动增长对性能的影响(上) SQL Server中事务日志自动增长对性能的影响(下) posted on 2011-0 ...

  4. boost::log模块测量日志记录发射的性能

    boost::log模块测量日志记录发射的性能 实现功能 C++实现代码 实现功能 boost::log模块测量日志记录发射的性能 C++实现代码 #define BOOST_NO_DYN_LINK ...

  5. log buffer及日志管理深入分析及性能调整(七)

    3.2.2 其他相关的等待事件 当进程需要向日志缓冲区里拷贝重做记录时,发现没有足够的可用空间时,则必须等待log buffer space事件.如果一个进程花费了太多的时间在log buffer s ...

  6. 监控日志loging Elasticsearch(性能优化八)

    在当今世界,各行各业每天都有海量数据产生,为了从这些海量数据中获取想要的分析结果,需要对数据进行提取.转换,存储,维护,管理和分析. 这已然远远超出了普通处理工具.数据库等的实现能力,只有基于的分布式 ...

  7. 搭建高性能日志服务器,syslog日志服务器搭建

    syslog日志服务器搭建 内容精选 换一换 提供多个业务节点提供共享的日志输出目录,方便分布式应用的日志收集和管理.业务特点:多个业务主机挂载同一个共享文件系统,并发打印日志.大文件小I/O:单个日 ...

  8. 高性能MySQL学习——提高查询性能

    高性能MySQL学习--提高查询性能 提高查询性能 MySQL 查询优化器 MySQL 执行计划分析"三步曲" MySQL 执行计划查询分析 如何优化 SQL MySQL 自身优化 ...

  9. cx_oracle写日志信息_日志系统的设计

    笔者在写作本章节的时候,并不敢把此章节的标题叫做<高性能日志系统的设计>,之所以不敢加上"高性能"三个字的原因是: 第一,我对于日志系统设计知识和经验都来自于学习和工作 ...

最新文章

  1. 为什么处理排序数组要比处理未排序数组快?
  2. 正则表达式贪婪模式及最短匹配
  3. cytoscape使用方法_7种方法 ,订制你的专属venn图!-代谢组学/蛋白组学研究
  4. 【DP】数字游戏(jzoj 2131)
  5. 传递给系统调用的数据区域太小怎么解决_一口气说出“分布式追踪系统”原理!...
  6. 1.5编程基础之循环控制_16买房子
  7. Springboot05整合FastJson优化输出
  8. 作为前端,你需要了解的js构造函数和原型
  9. 一分钟区分一流公司、二流公司、三流公司(转)
  10. oracle分区索引优化,SQL优化思路结果集重用优化、分区索引优化测试
  11. 【Vue中的坑】Vue中的修改变量没有效果?
  12. CCF推荐的A类、B类、C类中文科技期刊
  13. 基于python的贪吃蛇游戏设计论文_《贪吃蛇游戏课程设计》报告毕业设计(论文)...
  14. 三段式 matlab,1stopt三段式函数拟合
  15. QT5编译android安卓程序的sdk安装问题 android sdk manager
  16. 多项式拟合缺点_常见算法优缺点
  17. 512-rear chassis fan not detected的解决方法
  18. PCM与DSD究竟是什么??
  19. Linux/ Unix 键盘检测程序
  20. python中排序英文单词怎么写_Python 排序最长英文单词链(列表中前一个单词末字母是下一个单词的首字母)...

热门文章

  1. Python-easygui模块之插入图片
  2. 用图章工具 修改数字
  3. 黑苹果系统是服务器系统,黑苹果是什么系统(深入解读黑苹果系统)
  4. CAD怎么把圆分割为多段圆弧呢,一起来看看吧
  5. CAD梦想画图中的“绘图工具——点”
  6. sze品牌创始人的故事
  7. 优维科技将参加微软加速器·上海一期Demo Day
  8. 【python】PyQt6和pyqt6-tools在PyCharm2021的详细配置方法
  9. EasyRecovery最新MacBook版本安装包下载地址
  10. 织梦后台内容模型使用教程