本文主要分享火焰图使用技巧,介绍 systemtap 的原理机制,如何使用火焰图快速定位性能问题原因,同时加深对 systemtap 的理解。
让我们回想一下,曾经作为编程新手的我们是如何调优程序的?通常是在没有数据的情况下依靠主观臆断来瞎蒙,稍微有些经验的同学则会对差异代码进行二分或者逐段调试。这种定位问题的方式不仅耗时耗力,而且还不具有通用性,当遇到其他类似的性能问题时,需要重复踩坑、填坑,那么如何避免这种情况呢?
俗语有云:“工欲善其事,必先利其器。”个人认为,程序员定位性能问题也需要一件“利器”。 如同医生给病人看病,需要依靠专业的医学工具(比如 X 光片、听诊器等)进行诊断,最后依据医学工具的检验结果快速精准地定位出病因所在。性能调优工具(比如 perf / gprof 等)之于性能调优就像 X 光之于病人一样,它可以一针见血地指出程序的性能瓶颈。
但是常用的性能调优工具 perf 等,在呈现内容上只能单一地列出调用栈或者非层次化的时间分布,不够直观。这里我推荐大家配合使用火焰图,它将 perf 等工具采集的数据呈现得更为直观。
初识火焰图 火焰图(Flame Graph)是由 Linux 性能优化大师 Brendan Gregg 发明的,和所有其他的 profiling 方法不同的是,火焰图以一个全局的视野来看待时间分布,它从底部往顶部,列出所有可能导致性能瓶颈的调用栈。
火焰图整个图形看起来就像一个跳动的火焰,这就是它名字的由来。
火焰图有以下特征(这里以 on-cpu 火焰图为例):
每一列代表一个调用栈,每一个格子代表一个函数; 纵轴展示了栈的深度,按照调用关系从下到上排列,最顶上格子代表采样时,正在占用 cpu 的函数; 横轴的意义是指:火焰图将采集的多个调用栈信息,通过按字母横向排序的方式将众多信息聚合在一起。需要注意的是它并不代表时间; 横轴格子的宽度代表其在采样中出现频率,所以一个格子的宽度越大,说明它是瓶颈原因的可能性就越大; 火焰图格子的颜色是随机的暖色调,方便区分各个调用信息; 其他的采样方式也可以使用火焰图, on-cpu 火焰图横轴是指 cpu 占用时间,off-cpu 火焰图横轴则代表阻塞时间; 采样可以是单线程、多线程、多进程甚至是多 host,进阶用法可以参考附录 进阶阅读; 火焰图类型 常见的火焰图类型有 On-CPU,Off-CPU,还有 Memory,Hot/Cold,Differential 等等。他们分别适合处理什么样的问题呢?
这里笔者主要使用到的是 On-CPU、Off-CPU 以及 Memory 火焰图,所以这里仅仅对这三种火焰图作比较,也欢迎大家补充和斧正。
火焰图分析技巧 纵轴代表调用栈的深度(栈桢数),用于表示函数间调用关系:下面的函数是上面函数的父函数; 横轴代表调用频次,一个格子的宽度越大,越说明其可能是瓶颈原因; 不同类型火焰图适合优化的场景不同,比如 on-cpu 火焰图适合分析 cpu 占用高的问题函数,off-cpu 火焰图适合解决阻塞和锁抢占问题; 无意义的事情:横向先后顺序是为了聚合,跟函数间依赖或调用关系无关;火焰图各种颜色是为方便区分,本身不具有特殊含义; 多练习:进行性能优化有意识的使用火焰图的方式进行性能调优(如果时间充裕); 如何绘制火焰图? 要生成火焰图,必须要有一个顺手的动态追踪工具,如果操作系统是 Linux 的话,那么通常通常是 perf 或者 systemtap 中的一种。其中 perf 相对更常用,多数 Linux 都包含了 perf 这个工具,可以直接使用;SystemTap 则功能更为强大,监控也更为灵活。网上关于如何使用 perf 绘制火焰图的文章非常多而且丰富,所以本文将以 SystemTap 为例。
十一假期宅家无事,发现自己过去写了很多文章,却没有一个自己的博客,系统得管理自己的文章,所以准备将自己过去以及未来的文章都放到博客,以饷读者。另一方面,经过对 Serverless 博客、TCB 建站、虚拟机建站等一系列建站方式对比后,个人认为基于 Github Pages 最适合搭建个人技术博客,最重要的当然是免费,其次网上教程众多,可以快速建站,第三则是所有的博客直接托管在 github,也更符合个人习惯,最后则是自建个人博客可玩性和可扩展性好。
当然,这个方案并不是完美无缺,缺点也比较明显,比如需要考虑到安全信息泄漏问题(比如可能会泄露公司内的机密信息或者秘钥到 Github,所以需要准备安全扫描方案,这个我们会在另一篇文章谈);另一方面,读者需要能够翻墙才可以访问 Github Pages;最后,则是没有 CDN 加速,如果访问者众多或者网站图片众多,加载速度很慢。
为什么要写技术文章? 其实,个人写文章最初是兴趣使然以及工作需要。众所周知,IT 是一个技术革新很快的行业,新的概念、新的语言、新的框架层出不穷,程序员需要持续学习,我有对每一个新的知识有做笔记的习惯,笔记攒多了便需要回顾总结整理,便形成了一篇篇的文章。
以前笔记的图找不到了,差不多在习惯使用电子笔记之前有十几本笔记,后来我习惯性使用思维导图 processon 等一系列工具记录笔记,比如这张图便是我做的思维导图笔记的冰山一角:
那么,写技术文章有何价值?个人认为写技术文章的价值主要有三方面:个人价值、企业价值和社会价值、企业价值。从个人角度来说,技术写作是树立个人技术影响力,提升自我价值的最快路径,没有之一;从公司角度,坚持长线的写作,对于公司的技术品牌,技术文化,有着巨大的推动作用;从更高的维度来说,技术写作也是提升整个社会技术水平,推动技术不断进步的源动力。
hugo 初探 hugo 是什么? Hugo 是由 Go 语言实现的静态网站生成器。简单、易用、高效、易扩展、快速部署。
hugo 中文官方文档
hugo 英文官方文档
安装 hugo 在 windows 下,你可以在此处下载
windows 版本下载链接
如果你是 mac 系统,则可以通过如下命令安装(需要先安装 homebrew)
1 brew install hugo 确认 hugo 安装是否成功 通过检查版本号的方式,确认 hugo 安装是否成功
1 2 hugo version Hugo Static Site Generator v0.73.0/extended darwin/amd64 BuildDate: unknown # 输出结果 初始化网站目录 安装好之后,便可以初始化一个 hugo 项目,
本文主要介绍如何使用 go 语言 database/sql 库从数据库中读取 null 值的问题,以及如何向数据库中插入 null 值。本文在这里使用的是 sql.NullString, sql.NullInt64, sql.NullFloat64 等结构体,为了方便书写,它们的泛指我会使用 sql.Null 来表示
要点 从数据库读取可能为 null 值得值时,可以选择使用 sql.NULL 来读取;或者使用 IFNULL、COALESCE 等命令让数据库查询值返回不为"“或者 NULL 若需要往数据库中插入 null 值,则依然可以使用 sql.NULL 存储所需的值,然后进行插入 NULL 值 直接使用 sql.NULL 类型容易出现 valid 遗漏设置等问题,普通 int、string 与其转换时,请写几个简单的 get、set 函数 本 demo 使用的数据库表以及数据如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 mysql> desc person; +------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | first_name | varchar(100) | NO | | NULL | | | last_name | varchar(40) | YES | | NULL | | | age | int(11) | YES | | NULL | | +------------+--------------+------+-----+---------+----------------+ mysql> select * from person; +----+------------+-----------+------+ | id | first_name | last_name | age | +----+------------+-----------+------+ | 1 | yousa | NULL | NULL | +----+------------+-----------+------+ mysql> show create table person; +--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | person | CREATE TABLE `person` ( `id` int(11) NOT NULL AUTO_INCREMENT, `first_name` varchar(100) NOT NULL, `last_name` varchar(40) DEFAULT NULL, `age` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 | +--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.