每秒百万级高效C++异步日志实践

news2025/1/15 6:58:16

一个高效可拓展的异步C++日志库:RING LOG,本文分享了了其设计方案与技术原理等内容

详细代码见github路径:点击打开链接

导论

同步日志与缺点

传统的日志也叫同步日志,每次调用一次打印日志API就对应一次系统调用write写日志文件,在日志产生不频繁的场景下没什么问题

可是,如果日志打印很频繁,同步日志有什么问题?

  • 一方面,大量的日志打印陷入等量的write系统调用,有一定系统开销

  • 另一方面,使得打印日志的进程附带了大量同步的磁盘IO,影响性能

那么,如何解决如上的问题?就是

异步日志与队列实现的缺点

异步日志,按我的理解就是主线程的日志打印接口仅负责生产日志数据(作为日志的生产者),而日志的落地操作留给另一个后台线程去完成(作为日志的消费者),这是一个典型的生产-消费问题,如此一来会使得:

主线程调用日志打印接口成为非阻塞操作,同步的磁盘IO从主线程中剥离出来,有助于提高性能

对于异步日志,我们很容易借助队列来一个实现方式:主线程写日志到队列,队列本身使用条件变量、或者管道、eventfd等通知机制,当有数据入队列就通知消费者线程去消费日志

但是,这样的异步队列也有一定的问题:

  • 生产者线程产生N个日志,对应后台线程就会被通知N次,频繁日志写入会造成一定性能开销

  • 不同队列实现方式也各有缺点:

  • 用数组实现:空间不足时,队列内存不易拓展

  • 用链表实现:每条消息的生产消费都对应内存的创建销毁,有一定开销

好了,可以开始正文了

简介

RING LOG是一个适用于C++的异步日志, 其特点是效率高(实测每秒支持125+万日志写入)、易拓展,尤其适用于频繁写日志的场景

一句话介绍原理:

使用多个大数组缓冲区作为日志缓冲区,多个大数组缓冲区以双循环链表方式连接,并使用两个指针p1和p2指向链表两个节点,分别用以生成数据、与消费数据

生产者可以是多线程,共同持有p1来生产数据,消费者是一个后台线程,持有p2去消费数据

大数组缓冲区 + 双循环链表的设计,使得日志缓冲区相比于队列有更强大的拓展能力、且避免了大量内存申请释放,提高了异步日志在海量日志生成下的性能表现

此外,RING LOG还优化了每条日志的UTC格式时间的生成,明显提高日志性能

具体工作原理

数据结构

Ring Log的缓冲区是若干个cell_buffer以双向、循环的链表组成

cell_buffer是简单的一段缓冲区,日志追加于此,带状态:

  • FREE:表示还有空间可追加日志

  • FULL:表示暂时无法追加日志,正在、或即将被持久化到磁盘;

Ring Log有两个指针:

  • Producer Ptr:生产者产生的日志向这个指针指向的cell_buffer里追加,写满后指针向前移动,指向下一个cell_buffer;Producer Ptr永远表示当前日志写入哪个cell_buffer,被多个生产者线程共同持有

  • Consumer Ptr:消费者把这个指针指向的cell_buffer里的日志持久化到磁盘,完成后执行向前移动,指向下一个cell_buffer;Consumer Ptr永远表示哪个cell_buffer正要被持久化,仅被一个后台消费者线程持有

​起始时刻,每个cell_buffer状态均为FREE Producer Ptr与Consumer Ptr指向同一个cell_buffer整个Ring Log被一个互斥锁mutex保护

相关视频推荐

如何设计高效日志库

C++ Golang日志库Glog源码分析

c++后端绕不开的7个开源项目,每一个源码值得深入研究

免费学习地址:c/c++ linux服务器开发/后台架构师

需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

大致原理

消费者

后台线程(消费者)forever loop:

1.上锁,检查当前Consumer Ptr:

  • 如果对应cell_buffer状态为FULL,释放锁,去STEP 4;

  • 否则,以1秒超时时间等待条件变量cond;

2.再次检查当前Consumer Ptr:

  • 若cell_buffer状态为FULL,释放锁,去STEP 4;

  • 否则,如果cell_buffer无内容,则释放锁,回到STEP 1;

  • 如果cell_buffer有内容,将其标记为FULL,同时Producer Ptr前进一位;

3.释放锁

4.持久化cell_buffer

5.重新上锁,将cell_buffer状态标记为FREE,并清空其内容;Consumer Ptr前进一位;

6.释放锁

生产者

1.上锁,检查当前Producer Ptr对应cell_buffer状态:

如果cell_buffer状态为FREE,且生剩余空间足以写入本次日志,则追加日志到cell_buffer,去STEP X;

2.如果cell_buffer状态为FREE但是剩余空间不足了,标记其状态为FULL,然后进一步探测下一位的next_cell_buffer:

  • 如果next_cell_buffer状态为FREE,Producer Ptr前进一位,去STEP X;

  • 如果next_cell_buffer状态为FULL,说明Consumer Ptr = next_cell_buffer,Ring Log缓冲区使用完了;则我们继续申请一个new_cell_buffer,将其插入到cell_buffer与next_cell_buffer之间,并使得Producer Ptr指向此new_cell_buffer,去STEP X;

3.如果cell_buffer状态为FULL,说明此时Consumer Ptr = cell_buffer,丢弃日志;

4.释放锁,如果本线程将cell_buffer状态改为FULL则通知条件变量cond

在大量日志产生的场景下,Ring Log有一定的内存拓展能力;实际使用中,为防止Ring Log缓冲区无限拓展,会限制内存总大小,当超过此内存限制时不再申请新cell_buffer而是丢弃日志

图解各场景

初始时候,Consumer Ptr与Producer Ptr均指向同一个空闲cell_buffer1

​然后生产者在1s内写满了cell_buffer1,Producer Ptr前进,通知后台消费者线程持久化

​消费者持久化完成,重置cell_buffer1,Consumer Ptr前进一位,发现指向的cell_buffer2未满,等待

​超过一秒后cell_buffer2虽有日志,但依然未满:消费者将此cell_buffer2标记为FULL强行持久化,并将Producer Ptr前进一位到cell_buffer3

​消费者在cell_buffer2的持久化上延迟过大,结果生产者都写满cell_buffer3\4\5\6,已经正在写cell_buffer1了

生产者写满写cell_buffer1,发现下一位cell_buffer2是FULL,则拓展换冲区,新增new_cell_buffer

UTC时间优化

每条日志往往都需要UTC时间:yyyy-mm-dd hh:mm:ss(PS:Ring Log提供了毫秒级别的精度)

Linux系统下本地UTC时间的获取需要调用localtime函数获取年月日时分秒

在localtime调用次数较少时不会出现什么性能问题,但是写日志是一个大批量的工作,如果每条日志都调用localtime获取UTC时间,性能无法接受

在实际测试中,对于1亿条100字节日志的写入,未优化locatime函数时 RingLog写内存耗时245.41s,仅比传统日志写磁盘耗时292.58s快将近一分钟; 而在优化locatime函数后,RingLog写内存耗时79.39s,速度好几倍提升

策略

为了减少对localtime的调用,使用以下策略

RingLog使用变量_sys_acc_sec记录写上一条日志时,系统经过的秒数(从1970年起算)、使用变量_sys_acc_min记录写上一条日志时,系统经过的分钟数,并缓存写上一条日志时的年月日时分秒year、mon、day、hour、min、sec,并缓存UTC日志格式字符串

每当准备写一条日志:

  1. 调用gettimeofday获取系统经过的秒tv.tv_sec,与_sys_acc_sec比较;

  2. 如果tv.tv_sec 与 _sys_acc_sec相等,说明此日志与上一条日志在同一秒内产生,故年月日时分秒是一样的,直接使用缓存即可;

  3. 否则,说明此日志与上一条日志不在同一秒内产生,继续检查:tv.tv_sec/60即系统经过的分钟数与_sys_acc_min比较;

  4. 如果tv.tv_sec/60与_sys_acc_min相等,说明此日志与上一条日志在同一分钟内产生,故年月日时分是一样的,年月日时分 使用缓存即可,而秒sec = tv.tv_sec%60,更新缓存的秒sec,重组UTC日志格式字符串的秒部分;

  5. 否则,说明此日志与上一条日志不在同一分钟内产生,调用localtime重新获取UTC时间,并更新缓存的年月日时分秒,重组UTC日志格式字符串

小结:如此一来,localtime一分钟才会调用一次,频繁写日志几乎不会有性能损耗

性能测试

对比传统同步日志、与RingLog日志的效率(为了方便,传统同步日志以sync log表示)

1. 单线程连续写1亿条日志的效率

分别使用Sync log与Ring log写1亿条日志(每条日志长度为100字节)测试调用总耗时,测5次,结果如下:

单线程运行下,Ring Log写日志效率是传统同步日志的近3.7倍,可以达到每秒127万条长为100字节的日志的写入

2、多线程各写1千万条日志的效率

分别使用Sync log与Ring log开5个线程各写1千万条日志(每条日志长度为100字节)测试调用总耗时,测5次,结果如下:

多线程(5线程)运行下,Ring Log写日志效率是传统同步日志的近3.8倍,可以达到每秒135.5万条长为100字节的日志的写入

2. 对server QPS的影响

现有一个Reactor模式实现的echo Server,其纯净的QPS大致为19.32万/s

现在分别使用Sync Log、Ring Log来测试:echo Server在每收到一个数据就调用一次日志打印下的QPS表现

对于两种方式,分别采集12次实时QPS,统计后大致结果如下:

传统同步日志sync log使得echo Server QPS从19.32w万/s降低至11.42万/s,损失了40.89% RingLog使得echo Server QPS从19.32w万/s降低至16.72万/s,损失了13.46%

TODO

  • 日志本身缓存大小的配置

  • 程序正常退出、异常退出,此时在buffer中缓存的日志会丢失

  • 第N天23:59:59秒产生的日志有时会被刷写到第N+1天的日志文件中

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/960972.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

国际音标学习笔记

目录 1.单元音2.双元音3.辅音4.音节5.自然拼读法则5.1辅音字母的音标 1.单元音 我觉得单纯的音标并不好记住,所以就跟着老师整,根据单词记住音标的发音,以下是我的理解 音标对应的单词汉化iis衣əer饿ɔorigin奥u/ʊwoman五ʌart啊eanything哎…

哪个视觉语言模型更优?InstructBLIP、MiniGPT-4?全面评估基准LVLM-eHub告诉你

夕小瑶科技说 原创 作者 | 王思若 LLaMA、GPT-3等大型语言模型实现了对自然语言强大的理解和推理能力,为AI社区构筑了强大的语言基座模型。进而,继续迭代的GPT-4,更是赋予了模型处理图像的视觉能力。 如今,构建强大的多模态模型…

有源医疗器械技术要求编写要求之附录内容

目录 一、附录A中主要安全特征内容 二、附录内容简介 1、按防电击类型分类: 2、按防电击的程度分类: 3.按对进液的防护程度进行分类: 4.按在与空气混合的易燃麻醉气或与氧或氧化亚氮混合的易燃麻醉气况下使用时的安全程度分类: 6.设备的额定电压和频率 7.设备的输入功率…

QT 发布软件基本操作

一、配置环境变量 找到Qt安装时的bin目录的路径:D:\Qt\Qt5.14.2\5.14.2\mingw73_64\bin,将目录拷贝至下述环境变量中。 打开计算机的高级系统设置 选中环境变量-->系统变量-->Path 点击编辑-->新建-->粘贴 二、生成发布软件的可执行程序 …

【Python从入门到进阶】34、selenium基本概念及安装流程

接上篇《33、使用bs4获取星巴克产品信息》 上一篇我们介绍了如何使用bs4来解析星巴克网站,获取其产品信息。本篇我们来了解selenium技术的基础。 一、什么是selenium? Selenium是一种用于自动化Web浏览器操作的开源工具。它提供了一组API(应…

SpringBoot——整合Mongodb

简单介绍 Mongdb是一个开源,高性能,无模式的文档型数据库,NoSQL数据库产品中的一种,是最像关系型数据库的非关系型数据库。 使用场景 用户数据 存储位置:数据库特征:永久性存储,修改频率极低游…

【C++】C++ 引用详解 ⑩ ( 常量引用案例 )

文章目录 一、常量引用语法1、语法简介2、常引用语法示例 二、常量引用语法1、int 类型常量引用示例2、结构体类型常量引用示例 在 C 语言中 , 常量引用 是 引用类型 的一种 ; 借助 常量引用 , 可以将一个变量引用 作为实参 传递给一个函数形参 , 同时保证该值不会在函数内部被…

可观测性用观测云,观测云护航「杭州亚运会」

2023 年亚洲运动会定于 2023 年 9 月 23 日至 10 月 8 日在中国杭州举办,这是在党的二十大召开后,我国疫情防控措施优化调整后举办的最大规模、最高水平的国际综合性运动会,意义十分重大。杭州亚组委以「举办一届史上最成功的亚运会」为工作目…

【0901作业】QTday3 对话框、发布软件、事件处理机制,使用文件相关操作完成记事本的保存功能、处理键盘事件完成圆形的移动

目录 一、思维导图 二、作业 2.1 使用文件相关操作完成记事本的保存功能 2.2 处理键盘事件完成圆形的移动 一、思维导图 二、作业 2.1 使用文件相关操作完成记事本的保存功能 void Widget::on_saveBtn_clicked() {QString filename QFileDialog::getSaveFileName(this,&…

MySQL数据库学习【进阶篇】

MySQL数据库学习进阶篇 MySQL进阶篇已经更新完毕,点击网址查看👉:MySQL数据库进阶篇

【点击checkbox复选框,显示or隐藏某区域】

功能&#xff1a; 1. 选中复选框&#xff0c;显示隐藏的区域&#xff1b; 2. 取消选中&#xff0c;再隐藏该显示的区域。 方法1&#xff1a;在layui jquery框架下 代码如下&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta char…

接口测试系列 —— 什么是接口测试?

接口测试系列 为什么要做这个事情&#xff1f; 对自己过往在接口测试上的经验&#xff0c;写一个小结的系列文章&#xff0c;是一个系统性的思考和知识构建。发布的同时&#xff0c;也是希望获得更多感兴趣的同学的意见和反馈&#xff0c;可以把这个部分做的更好。 接口测试概…

充电比加油还快?一秒一公里,全液冷超充项目试点,5 年全覆盖?

华为中国数字能源旗舰峰会广西站在桂林举行&#xff0c;标志着桂林在数字能源领域的迈进。与此同时&#xff0c;桂林市和华为数字能源展开了合作&#xff0c;启动了全液冷超充示范项目&#xff0c;为城市的电动车充电基础设施添砖加瓦。 根据华为数字能源技术有限公司副总裁何波…

如何解决微信收款异常问题

一、自查原因 当您遇到微信收款异常问题时&#xff0c;首先需要了解异常的原因。可能的原因包括违反微信支付相关规定、存在异常交易行为、账户安全问题等。了解原因后&#xff0c;您可以采取相应的措施进行处理。 二、联系客服 您可以通过微信支付官方客服渠道咨询具体的问题&…

Is f(z)=1/z truly an analytic function

https://math.stackexchange.com/questions/755566/is-fz-1-z-truly-an-analytic-function

react快速开始(三)-create-react-app脚手架项目启动;使用VScode调试react

文章目录 react快速开始(三)-create-react-app脚手架项目启动&#xff1b;使用VScode调试react一、create-react-app脚手架项目启动1. react-scripts2. 关于better-npm-runbetter-npm-run安装 二、使用VScode调试react1. 浏览器插件React Developer Tools2. 【重点】用 VSCode …

Mediasoup在node.js下多线程实现

mediasoup基于socket.io的交互消息来完成join-room的请求过程。Join的过程&#xff0c;实际就是获取stream的过程&#xff0c;也就是视频加载时间(video-load-speed)。在RTMP系统&#xff0c;视频加载时间是秒开。Mediasoup给出的第一个frame是I-frame&#xff0c;但由于交互的…

web服务基础以及简单的站点应用部署

一、 简述静态网页和动态网页的区别。 静态网页&#xff1a; 优势&#xff1a; ①访问的效率比较高 ②网页内容是固定不变的&#xff0c;因此&#xff0c;容易被搜索引擎收录 ③网页程序在用户浏览器端解析&#xff0c;当客户端向服务器请求数据时&#xff0c;服务器会直接从磁…

2001-2021年中国城市分产业创新指数(574万+)

2001-2021年中国城市分产业创新指数&#xff08;574万&#xff09; 1、时间&#xff1a;2001-2021年 2、来源&#xff1a;中国城市和产业创新力报告 3、指标&#xff1a;包括城市创新指数、产业创新指数、城市-产业创新指数 4、整理方式&#xff1a; 整理方式是基于国家知…

【重要】Nand Flash基础知识与坏块管理机制的介绍

概述 Flash名称的由来&#xff0c;Flash的擦除操作是以block块为单位的&#xff0c;与此相对应的是其他很多存储设备&#xff0c;是以bit位为最小读取/写入的单位&#xff0c;Flash是一次性地擦除整个块&#xff1a;在发送一个擦除命令后&#xff0c;一次性地将一个block&…