Kymotz's Blog

一次SQL优化

最近在大数据平写 Hive SQL 跑离线作业,数据量大概在三千万,有一个离线任务每次执行都要两个小时以上,我感觉太慢了。为什么觉得慢?刨除实现 SQL 逻辑的时间,自测、冒烟测试、上线每次执行一次需要 2h,也就是修改一处 SQL 要经历 6 小时才能看到最终结果。根据我的经验判断不应该耗费这么长时间,于是想着看看能不能改善一下。我先让数据平台的同学帮忙看,由于他们忙没得到结果,于是自己动手优化了一下,优化后的结果还是比较让我吃惊的,因为我并没有使用很复杂高深的手段(主要是减少临时表、减少嵌套查询数),却得到了意向不到的效果。这使我产生许多想法和思考。

优化后的效果:时间减少 95%,内存减少 80%。

  • 优化前:耗时 2h,内存占用 80G
  • 优化后:耗时 6min,内存占用 16G

总结

  1. 这次SQL优化并不复杂却带来了可观的性能收益,说明简单的理论基础和参数调整也能达到不错的性能,仅仅一点点变化性能就可能差一个数量级;

  2. 熟悉理论基础和计算机原理,是写出高效的程序必要条件;

随笔|飞向外太空

好久不见

前段时间,我和实验室的同学开了一次线上会议。我们这届实验室自 22 年年底以来还没怎么聚,大家进入视频会议都有些兴奋。一句“好久不见”点燃了埋藏在心底的情感,“是呀,好久不见了~”。这次会议我印象深刻的有两点。第一个是当我们讨论其中一个主题时,有些同学不怎么发言,远远鼓励大家理性说出自己的观点并表示允许尖锐的声音。她随后解释:“一个积极正向发展的团队,是同时需要温和的声音和尖锐的声音的。尖锐不代表错,而一味地温和可能是在隐藏问题。”,我很赞同她的这个观点,相信好的团队能自己消化问题,形成良性循环。第二个是一个问大家的问题:“工作后你的目标变化了吗?”,大家犹豫了一下,每个人都说了一些,但又好像没有说到重点,我发言时说:“变了一些”,从工作以来我越来越觉得自己之前话有些狂妄和异想天开,在开发中,编程语言是简单的,然而业务是复杂的!即使一个简单的登录认证功能在企业环境里也可能很复杂。


从摄影学到的

在我买相机的几个月拍了一些照片,起初我在网上看了些教程,学到了些伎俩,但拍出的照片让人满意的不多,一组照片只有几个,有时甚至一个也没有,这不禁让我陷入自我怀疑:是我设备不行还是技术不到家?这种情况直到我开始主动思考相机运作机制和每个参数配置的含义后才有所改善,拍不好、不出片是因为不熟悉工具,还有是部分教程看似有用实则无用。现在,如果一个新手问我怎么学摄影,我会告诉他先把工具用熟,了解每一个配置对画面的影响,通过实践形成对光影和环境处理的认知,最后再学习构图知识。


理性、感性

两周前跟朋友一起出去拍照,我问了他一个问题:“你认为摄影是理性的还是感性的?”,朋友可能也没有思考过这个问题,随口回了我一句:”随性的,喜欢的事物,心里有拍照的冲动就按快门“。我倾向于摄影是理性的,每当我们按下快门心里都会对周围环境做一个判断,然后决定用多大光圈、快门,我们也会主动选择一个角度呈现这个画面,即使当我们希望画面呈现出感性时也是心智做出的一个选择(配置参数、控制画面)。

使用多线程的实践思考

image

本文源自一个线上问题引起的思考。诚然多线程是有益的,但使用不当反而会造成系统吞吐能力下降,甚至发生死锁。在使用多线程时我们可能面临下列情况:

  1. 当写的并发代码包含框架类的方法调用,总是可能存在线程安全问题,因为框架在不停升级,我们不能保证它一直线程安全的;
  2. 线程配置不当会引起安全问题,例如:缓存队列溢出、瞬时任务增多导致线程池打满,我们的业务在不断变化,在一个新的上下文环境中,没有人能保证线程配置一直合理;
  3. 多线程让编程更复杂(需要处理更多情况),例如:控制执行顺序、并发访问变量;
  4. 在多线程中进行远程请求容易对下游服务(数据库服务或其他业务服务)造成压力;

通过以上,我们看到在项目中引入一种技术带来的额外风险,有时这种风险不是线性增长而是指数级别,因此从这些角度看应该谨慎使用多线程。好的实践是先寻找其他解决方案,最后再考虑使用多线程,把多线程当作性能扩展的最后一道防线。

如果不用多线程就不存在上述的问题,我们假设使用多线程背景下来总一些实践技巧。

MySQL 的字符集与排序规则

通俗讲字符集就是字符码的集合,在 MySQL 中字符集的选择影响字符码的存储,字符集选择不好不仅影响存储展示还会有问题,例如乱码。

在业务中常用的字符集是 UTF-8 字符集,mysql 有两种这样的字符集:utf8、utf8mb4,它们的区别如下:

  • utf8: 支持最长 3 Byte 的字符编码,但一部分 UTF-8的 4 Byte 编码不支持,例如 emoji。
  • utf8mb4:支持最长 4 Byte 的字符编码。这是业务中使用最多的字符集,从 MySQL 8.0 开始成为默认字符集。

字符的排序规则(collate):字符在比较、排序时以及大小写敏感的规则。涉及字符比较的操作均与其相关,例如:排序、分组、索引、比较(=、>、<等)。MySQL 中有些字符排序规则是忽略大小写的,例如 utf8mb4_general_ci 排序规则,这种带有 ci 后缀的是大小写不敏感的标志(ci 即 case insensitive 的缩写)。

Java 中使用枚举的正确姿势

在项目开发中我们经常用到枚举定义常量,例如定义系统中用户的身份类型(ADMIN、USER…)、定义项目启动环境的类型(DEV,PROD…)、某个字段的值的枚举等。往往我们发现在项目里定义枚举类时只是有枚举成员,像下面这样:

1public enum Type {
2  T1,T2,T3
3}

不能说这样不好,只是这样差不多又回到了我们使用类定义静态常量常量。Java 中除了有类(class)这种类型,又实现了枚举(enum)这种类型,说明肯定它独特的用处,不妨我们先回忆下使用类成员定义静态常量和枚举定义常量的区别。

  • 使用类的静态成员定义常量,常量只能有一个固定的值,通常是一个简单的字符串或一个数值。
  • 使用枚举定义常量,实例化枚举后不可变,枚举可以携带多个值,这为常量提供了更多信息。(补充,虽说枚举实例化后不可变,但它的成员变量如果不用 final 修饰,是可变的!)
  • 如果你熟悉枚举的原理,你可以通过类(class)模拟出一个枚举(enum),但通常你需要两个类来完成这个工作,一个类来定义枚举和其成员,另一类完成初始化。而枚举(enum)通过编译消除了这些复杂性,可以轻松实例化对象和定义方法。

通过它们之间的区别,可以看出枚举能为我们的常量提供更多的信息和更加便捷的定义、实例化。那么如何利用好枚举这些特性,如何定义好用(便捷)的枚举呢?

2022 年总结:积极生活每一天

不平凡的一年过去了,生活是个五味调料罐,喜怒哀里交加,平凡的生活中偶尔跳出点惊喜。

今年终于看到疫结束的希望了,距离 2019 年年底发现新冠整整三年了,无数学生感叹“大学有几年,疫情占三年”,我印象最深刻的是大学的网课,老师们猝不及防地适应直播教学,学生们不再去教室上课了,还有各个地方的管控,以至于过年、假期回不来家以及线上办公。疫情期间这段记忆对国人真的是太深刻了,以至于听到放开的消息,心情激动就像在工作日无缘无辜给放了个假似的,以后可以大胆和朋友聚餐,愉快地出去旅游了。

“最近发展区” 理论

最近发展区理论 是教育学上的一个概念,它把人理解事物的等级做了区分,使教育者可以使用这个理论评估学生和教学内容,以达到最优的教学效率。最近发展区理论具备一定的普适性,也可以用在日常沟通、会议、汇报、分享等场景中,作为评估用户的方法。

根据 “最近发展区” 理论,可以人把当前具备的知识划分为三个级别:知识舒适区、最近发展区、知识困难区。

  • 知识舒适区:人已有的知识,理解应用这些知识自然且没有压力
  • 最近发展区:人可以根据已有的知识可能达到的知识,通常需要借助外力达到;
  • 知识困难区:知识超过了人能理解的范围,理解应用这些知识困难

如果把我们日常场景分成两类:沟通交流(会议、汇报)、分享教学(分享、讲课),那么:

  • 在沟通交流的活动中,应该尽量在双方的知识舒适区进行交谈、讨论,避免出现知识困难区的词汇、概念;

  • 在分享教学活动中,最应该关注的是受众群体的最近发展区,最近发展区的知识能有利地让受众成长和受到启发。知识的掌握是一个循序渐进的过程,知识的准备需要遵循这样的逻辑与原理,在准备时首先要找到受众群体的上限和下限(受众群体存在差异时要找重叠区),在组织材料时通常由下限逐渐延伸至上限。

数据优先级

今天上午和前端同学开周会,我捕获到了一个字眼 —— “数据优先级”,这让我想到了我工作中处理任务的优先级,如果把程序想象成一个具备独立思维的个体,它想更好的处理业务也是需要考虑这些的。

在工作中,同时处理多个工作有时让人头大,从一件事转向另一件事,脑海里必然要切换事情的上下文,有时话费的时间甚至比做这件事还多,这种情况对应到程序中自然是操作系统调度线程切换上下文的开销,我们不能忽略,程序也是。还有数据竞争、锁机制等,都能找到和生活中对应的,如果把我们现实中的最佳实践应用在程序肯定会提升程序设计能力。

用 Java 实现生成 Markdown 文本的工具

公司的 IM 每天有许多机器人推送的消息,我也在使用,这个功能是好的,但是当我们想去发送一些格式优美的消息时,却要费许多功夫,主要来源于字符串拼接,如果要拼接出 Markdown 格式的那就更费力了,另外由拼接带来的是混乱的代码,为了解决这个痛点,我写了一个 Java 代码生成 Markdown 文本的工具,还给它起了一个酷酷的名字 MdKiller。

MdKiller —— Markdown 杀手。

MdKiller 可以格式化生成 Markdown 文本的工具,支持常用 Markdown 格式生成,例如引用块、代码块、有无序列表、表格等,内容上支持字体样式(style)和内容的嵌套,适用于 IM 消息 Markdown 排版。

Rust语言笔记

image

本文不讨论是否选择 Rust,只做 Rust 介绍以及它最新动态的陈述,另外一些资料我会列在文末。

系统级开发语言一直是 C/C++ 的代名词,C 语言偏低层缺少类似 C++ 标准模版库这样的利器,虽然 C++ 开发效率高,但是复杂很难精通,而 Rust 语言同时兼顾了 C 和 C++,即有低层控制硬件的能力又有高级语言的语法特性,例如面向对象、范型等。

Rust 是一门静态语言,其源文件编译后可以不依赖 SDK 运行;Rust 具有安全的内存管理并且没有 GC,对于系统级别软件(文件系统、系统内核、驱动…)是不允许存在 Stop The World(俚语,暂停整个程序)的,对于内存管理的苦楚恐怕只有 C/C++ 程序员最清楚,常常引发问题的就是这块,Rust 以其独有的方法在编译前就解决了这块,相当于拥有了自动内存管理。