IT技术博客大学习 共学习 共进步
首页 / 四火的唠叨
IT 2018-07-05 13:27:32 / 累计浏览 2,800

评审的艺术——谈谈现实中的代码评审

这篇讲的是代码评审的实践智慧——那些很难从课本学到、却在日常团队协作中至关重要的经验。作者从自身出发,认为代码创造本身就带有个性,评审时过度追求一致既不现实也容易扼杀活力。他坦言评审风格因人而异,有人温和、有人直接,而自己更倾向于对事不对事地坚持原则。 文章将评审问题分为三类:需求业务层面、代码结构层面和风格格式层面。前两者可能成为合并代码的阻碍,而后者通常不是关键。作者分享了一套沟通技巧:比如为风格问题标注“picky”以降低火药味;多用“也许”“可能”等虚拟语气缓和建议;或是用提问代替断言(如“为什么这里用3?”)。他还强调,评审应“先抓主干”,遇到满是问题的代码时,先聚焦核心设计缺陷而非细枝末节。 一个有趣的观点是,评审标准不必完全统一。对新项目代码和新人提交的代码要求应更严格,前者影响后续质量基调,后者关乎职业习惯养成。最后作者指出,若对业务或技术背景不熟,勉强评审反而有害,此时不如坦诚沟通、确保质量。整篇文章没有空谈流程,而是像一位经验丰富的工程师在分享如何在真实团队中让评审变得高效而有人情味。

IT 2017-10-15 10:17:30 / 累计浏览 2,980

分布式系统中唯一ID的生成

这篇讲的是分布式系统中一个看似简单却至关重要的问题:如何生成全局唯一的ID。作者从实际大型系统的共同需求出发,对比了几种主流方案,分析了它们各自的适用场景与取舍。 文章首先剖析了“独立生成服务”这类集中式方案。最典型的是利用数据库的自增序列,它保证了递增性,但存在单点瓶颈。对此,一个变通思路是通过划分序列范围或设置不同步长,用多个节点分摊生成任务,但这又牺牲了全局的递增性。作者重点介绍了开源方案Twitter Snowflake,它通过组合时间戳、节点编号和自增序列,在保证高性能与有序性的同时,减少了中心化依赖(尽管节点ID仍需从Zookeeper获取)。 另一大类是“本地生成器”。这类方案在节点本地生成ID,通常要求不同节点间无状态依赖。例如用主机号加时间戳,简单但受限于单毫秒只能生成一个ID;而UUID(通用唯一识别码)则提供了更灵活的128位随机标识,不过理论上仍存在极低概率的冲突。 整体来看,作者并未简单评判优劣,而是引导读者思考:在递增性、全局有序、高可用、高性能与实现复杂度这些不同维度间,应如何根据具体业务场景做出合适的选择。

IT 2016-06-06 23:43:17 / 累计浏览 3,520

Spark性能优化——和shuffle搏斗

这篇讲的是Spark性能调优中一个最头疼的问题——shuffle。作者把shuffle比作必须击败的“大boss”,因为它会触发大量网络传输和序列化,让原本在内存中飞快的计算慢下来。 文章没有堆砌理论,而是直接切入实战技巧。比如,作者用一个从3小时缩短到20分钟的例子,说明“先各自去重,再合并”为何能大幅减少shuffle数据量;还对比了`mapValues`与`map`、`reduceByKey`与`groupByKey`,点明哪些操作会偷偷引发shuffle,而哪些能保持本地高效计算。 针对常见的大小表join,文章给出了一个巧妙思路:把小表广播出去,用`broadcast`加`filter`直接替代耗时的`join`操作,能完全避免shuffle。对于数据倾斜导致单节点过载的问题,作者也指出了改进key设计的解决方向。 整篇文章就像一位有经验的工程师在分享如何“避坑”,从原理到代码示例都很具体,最后还提醒了关于`collect`、避免RDD嵌套操作等容易忽略的细节。对于用Spark处理大数据的人来说,这些围绕shuffle的优化思路相当实用。

IT 2016-05-05 22:43:15 / 累计浏览 1,640

记录一种工作流心跳机制的设计

这篇讲的是在基于Amazon SWF的工作流中,如何设计一个可靠的心跳机制来维持长时间任务的存活。作者从实际开发踩坑出发,分享了应对SWF 5分钟超时限制的解决方案。 核心方案是采用两个双端队列(main queue和backup queue)来统一管理所有需要心跳的任务。每秒从主队列取出一个任务发送心跳,完成后放入备份队列;每两分钟(一个周期)再将备份队列的任务批量移回主队列,开始新一轮循环。这个设计巧妙地解决了并发下的任务状态跟踪问题,比单队列加计数器的方案更简单高效。 文章深入探讨了几个关键设计考量:心跳频率并非越快越好,需要在及时性和避免服务端限流之间做权衡;周期长度(如120秒)的设置要能覆盖超时时间并提供重试余地。更重要的是,作者详细剖析了心跳失败时的分级处理策略:对于资源已取消等常规异常直接移除任务;对于限流错误立即重试;对于其他未知异常则放入当前周期队尾重试并计数,避免影响其他任务。 最后,通过一个EMR集群因心跳超时和检查逻辑缺陷被误回收的实例,说明了在真实分布式环境中,看似简单的心跳机制与任务超时、资源监控等环节环环相扣,设计时需要全局考量,用绝对时间而非操作次数来判断状态才更可靠。

IT 2016-03-20 22:18:59 / 累计浏览 2,580

从Java和JavaScript来学习Haskell和Groovy(类型系统)

这篇技术文章从Java和JavaScript的类型系统出发,对比分析了Haskell和Groovy在类型设计上的核心差异。作者首先厘清了动态类型与静态类型、强类型与弱类型、类型推导等基础概念,并逐一拆解了四种语言的类型特性:Java是典型的静态强类型加显式指定,JavaScript则以动态弱类型和类型推导为主,Groovy提供了双模式——既能通过def实现动态推断,也能用显式类型转向静态检查,而Haskell则以静态强类型和类型推导体现了函数式语言的严谨性。 文章进一步深入到数据类型和函数行为的细节对比。例如,JavaScript中instanceof和typeof的混乱结果、Groovy的flow typing如何在运行时根据赋值推断类型,以及Haskell如何通过类型系统保障不变性。这些具体代码示例生动展示了语言设计背后的哲学:动态语言偏向灵活性,静态语言强调安全性。通过这样的跨语言比较,读者能更直观地理解不同类型系统的适用场景,比如Groovy如何在兼容Java的同时融入函数式特性,而Haskell又如何通过纯类型系统避免运行时错误。

IT 2016-03-20 22:17:59 / 累计浏览 2,860

从Java和JavaScript来学习Haskell和Groovy(引子)

这篇讲的是作者如何打破两个根深蒂固的编程学习误区。他犀利地指出,“语言不重要”和“设计模式万能”这类观点在广义上是误导人的,尤其强调了编程语言绝不仅仅是语法工具,更是其背后范式与思维方式的载体。 作者以自身Java(静态)与JavaScript(动态)的背景为例,提出了一个清晰的学习路径:通过类比已知语言,来深刻理解新的编程范式。他瞄准了两个代表性目标:一是Groovy,为了探索动态语言强大的元编程能力;二是Haskell,为了领略纯粹函数式编程的严谨与独特魅力,比如模式匹配带来的优雅。 文章的后续计划也很明确,将从类型系统、元编程机制等维度,对这四门语言进行特性的横向比较。这种从熟悉到陌生、带着问题对比学习的方法,为想拓宽语言视野的开发者提供了一个扎实的起点。

IT 2016-03-12 22:49:10 / 累计浏览 4,340

从淘汰Oracle数据库的事情说起

作者从公司淘汰Oracle数据库的内部实践说起,类比国内“去IOE”浪潮,点明核心动因是高昂的维护成本与扩展性瓶颈。但他指出,这绝非简单地将Oracle换为MySQL或上云,而是一场有计划的架构演进——部分业务数据已迁移至DynamoDB等NoSQL,甚至落盘至S3,计算层则由Hadoop或Spark接管。 由此引出一个关键问题:NoSQL的兴起是否意味着SQL将过时?作者的回答是“恰恰相反”。他通过两个实例佐证:一是团队将复杂的Scala逻辑重写为Spark SQL,让更熟悉SQL的数据分析师能直接参与;二是基础设施团队通过Hive等工具,在底层从数据库切换至MapReduce后,依然对上层提供稳定的SQL接口。SQL作为一种数据抽象和思维范式,其生命力反而在增强。 文章还反思了关系型数据库自身“被成功到滥用”的问题,例如在不适宜的高并发场景硬用Oracle。最后,作者将话题从技术延伸到人与技能,指出诸如DBA等纯粹依赖维护的岗位将面临挑战,而真正的核心技术价值在于解决那些不易被工具或新平台简单替代的根本问题。整篇文章从一次具体的架构迁移,引发了对技术演进逻辑和工程师核心能力的深层思考。

IT 2016-03-09 23:24:02 / 累计浏览 3,120

一道随机数题目的求解

这篇探讨了一个经典的随机数构造问题:如何基于均匀的1~5随机数函数,实现均匀的1~7随机数函数。作者从直观的思路入手,展示了通过二维数组映射与拒绝采样的核心方案,并提供了对应的Java实现。 然而,文章的价值不止于算法本身。作者在千万次数据测试中,意外发现生成结果的分布并不均匀,某些数字的出现频率显著偏高。经过深入排查,问题被追溯到随机数种子的精度上——即使使用纳秒级时间戳,快速连续调用时获取的种子值仍可能相同,导致随机序列重复,进而破坏了分布的均匀性。 文章通过对比实验(如将种子改为毫秒级、增加调用间隔)验证了这一猜想,揭示了用“小随机”合成“大随机”时,底层伪随机数生成器的缺陷会被放大。这对于理解拒绝采样的实际应用边界,以及随机性工程实现中的细节陷阱,提供了非常具体的参考。

IT 2016-03-09 00:16:23 / 累计浏览 2,640

系统设计典型问题的思考

系统设计面试题没有标准答案,但思考过程有章可循。这篇文章就从“问题该怎么想”入手,梳理了一套从外到内的解题框架。作者的核心观点是:不要急于画架构图,而是反复沟通澄清需求——优先搞定2-3个核心用例,明确用户与数据规模,并识别请求模型(比如读远多于写)。 在此基础上,先定义核心模型与API,再划分系统层次与组件,最后逐层细化。在细化过程中,文章重点讨论了存储选型(关系型分库分表 vs. NoSQL的CAP权衡)、集群策略、消息队列与缓存设计这几个关键环节,并强调所有优化都应建立在明确的系统瓶颈识别之上。 文章后半部分将这套思路应用到了三个经典案例中:设计微博信息流时,需权衡消息推送的push模型与拉取模型,并设计分级的缓存;设计短网址系统时,核心挑战是如何在分布式环境下高效生成全局唯一ID;而设计实时聊天系统,则需解决服务端到客户端的消息推送问题,比如采用Comet技术维持长连接。 最终,文章落脚于工程师对这类开放性问题的反复琢磨与沉淀。这些思考虽不像算法题有唯一正解,却能在实际工程中建立起至关重要的宏观设计直觉。

IT 2016-03-07 23:57:54 / 累计浏览 2,660

从Java和JavaScript来学习Haskell和Groovy(元编程)

这篇文章深入探讨了四种主流编程语言在元编程领域的不同实现路径。作者首先定义元编程为“用程序写程序”,即运行时动态修改类结构的能力,并从Java的静态限制出发,介绍了其依赖JDK 5注解(如Lombok)和Spring框架(AOP、IoC)扩展元编程的方式;对于Haskell,则聚焦于Template Haskell通过抽象语法树(AST)和QuasiQuotation在编译期生成代码的方法,例如用`[| |]`构造自定义语法片段。 对比动态语言,JavaScript的元编程基于对象自省和eval关键字,以极简性著称——通过prototype和动态属性添加实现灵活扩展;而Groovy则提供了更丰富的特性,如MethodMissing处理未知方法调用、GroovyInterceptable实现AOP拦截、Categories提供类似Objective-C的临时能力注入,以及Magic Package自定义元类(如通过命名规约修改String逻辑)。文章揭示了静态语言与动态语言在元编程灵活性上的核心差异:前者受限于编译期,后者则能自由操纵运行时环境。 最终,作者对比了JavaScript的简洁设计与Groovy的功能多样性,指出语言选择如同工具取舍,关键在于程序员如何平衡需求与偏好。这种跨语言视角帮助读者理解元编程的本质,并为项目技术选型提供了清晰参考。

IT 2016-02-21 22:54:19 / 累计浏览 3,940

手滑的故事

这篇讲的是程序员们“手滑”引发的线上惊魂时刻。作者从自己和同行的经历出发,提到了忘带WHERE条件的UPDATE和DELETE、误执行`rm -rf`,以及误杀重要的线上Hadoop任务、误删生产文件等真实案例。那些操作失误后瞬间“浑身颤抖”的体感,相信很多工程师都似曾相识。 文章不仅罗列事故,更着重讨论了事后反应的光谱:从最糟糕的当众批评、追责到底,到更理性的对外冷处理、对内聚焦问题根因而非个人。作者认为,责任主体往往已懊悔万分,过度追责反而导致“不做不错”的消极心态;而复杂的Checklist或繁琐的审批流程,也只是笨拙且降低效率的补救。 他更推崇那些“不知不觉”规避风险的实践,例如建立不同权限的Linux用户,以及做好充分的备份与容错机制。核心观点是:在系统维护中,人远不如机器可靠。与其纠结于事后惩处,不如构建鼓励坦诚报告、聚焦系统性改进的工程文化,因为“没有手滑的人生,是不完整的”。

IT 2016-02-21 22:47:37 / 累计浏览 2,180

从Java和JavaScript来学习Haskell和Groovy(DSL)

这篇讲的是如何借助Java和JavaScript的已有知识,来理解Haskell和Groovy中DSL(领域特定语言)的实现思路。文章开篇厘清了DSL的概念——它专注于特定领域,追求简洁与表达力,而非通用性。 作者首先剖析了Java实现DSL的路径与局限。Java主要依赖链式调用和嵌套函数来构建结构,但受限于冗长的语法。随后,文章深入讲解了JDK8的闭包与Lambda表达式如何让Java拥有函数式编程的可能,使代码(如排序逻辑)得以大幅简化,并解释了`@FunctionalInterface`注解背后的原理。 转向JavaScript时,文章指出其核心优势在于函数可作为“一等公民”,使得DSL能以更灵活、更具表现力的形式被编写,例如通过高阶函数实现数据流的流畅操作。 最后,文章展示了Groovy为何是DSL的“天选之语”。它不仅语法糖丰富、代码接近自然语言,更提供了`MethodMissing`等特性,能优雅地构建如HTML生成器这样可读性极强的DSL示例。通过对比这三种语言的特性,文章清晰地呈现了它们在DSL实现上各自的适用场景与表达能力。

IT 2016-02-16 20:34:34 / 累计浏览 3,460

三次性能优化经历

这篇分享的是作者在技术生涯中三次重要的性能优化经历,涵盖了Portal、Service和Spark三个不同场景,每次优化都持续数月,充满了挑战与实战心得。 在Portal优化中,作者强调首先厘清前后端交互模型,通过划分页面组件的动态与静态部分来实施缓存策略,并指出统一接口设计对于优化的基础性作用——杂乱无章的交互模型往往成为噩梦。Service优化则聚焦高并发查询场景,尝试了Memcached作为中心缓存以提高命中率,但需处理缓存失效带来的风险和延迟问题;同时探索了计算迁移到客户端和异步预处理,最终将数据源迁移到NoSQL的DynamoDB以减轻数据库压力。Spark优化更为系统化,作者测试了不同实例类型、内存配置和executor数量下的性能表现,评估性价比,并修正代码中的并行化问题;特别关注了异常数据量下的稳健性,如Q4业务暴涨时的处理。 作者通过这些复盘揭示,性能优化的核心始终围绕CPU、内存、网络和并行度的平衡,但具体策略需因地制宜。优化时不仅要关注单一指标,还需考虑整体系统行为,比如缓存失效时的压力转移,或Spark中

IT 2016-02-11 22:54:42 / 累计浏览 2,800

系统设计的典型分层和涉及的知识点

这篇讲的是系统设计面试中的典型套路。作者发现,许多看似复杂的设计问题,其实可以拆解为几个标准层次来思考。文章通过一张清晰的图表,梳理了从问题分析到具体技术点的完整框架。 核心将问题分为两大块:一类是“问题本身的分析”,涵盖同步/异步、消息推拉模式、数据结构设计等常见考察方向。另一类是“系统实现的分析”,又进一步细分为前端展示层、业务逻辑层,以及最复杂的数据访问层。每一层都对应着具体的挑战,比如缓存需要分层设计(冷热数据),数据库要考虑分片,而性能优化的核心始终围绕吞吐量与延迟展开。 特别值得注意的是,作者强调一致性模型是分布式系统的灵魂,读写模型则常与存储结构紧密结合。这篇文章的价值不在于给出一个标准答案,而是提供了一个结构化的思考工具,帮助你在面对任何系统设计问题时,都能快速定位关键层次,有条不紊地展开分析。

IT 2016-02-11 16:24:48 / 累计浏览 2,820

研发团队的角色和构成

这篇文章从作者个人经历出发,讲述了研发团队角色与构成的演变。他对比了早年典型的分工模式与当下的新形态:过去团队里有明确的项目经理、SE(相当于产品经理)、独立的测试与QA等角色,流程偏向瀑布式。 如今,许多角色被融合或重新定义。项目经理常由团队负责人兼任,架构设计更多由资深工程师主导。一个显著趋势是“粘合剂”型工程师的出现——如SDE(软件开发工程师)需要承担从设计、编码到测试的更多职责。与此同时,纯粹的测试岗位在很多团队中正变得边缘或消失。 作者对此提出了自己的观察与争议性观点。他认为,将测试视为工程师的基本素养,比设置独立的测试岗位更有利于流程效率。他也提醒,全能型工程师虽好,但其精力若被过度分散于需求澄清或数据排查中,则可能暴露团队在产品设计或系统质量上的深层问题。 文章最终引发对工程师核心价值以及团队如何高效协作的思考。

IT 2016-02-10 22:55:55 / 累计浏览 4,300

七年工作,几个故事

这篇讲的是一位程序员从华为到亚马逊七年间的五个工作故事,以及从中提炼出的职业思考。作者开篇就点明了三个核心观点:要为自己工作,而非为项目或绩效;尊敬同行,但警惕那些异化工程师的制度与文化;要保持开阔眼界,时间会给对错一个公正的答案。 文章通过几个真实案例展开:在华为经历的高强度加班文化,项目结束后近三分之二的人离职;离职时因制度原因与年终奖失之交臂,体会到“人走茶凉”;曾作为“工具人”开发强制性的代码检查工具,反而阻碍开发效率,事后深感这是“助纣为虐”;也观察到换领导引发的办公室政治与人员动荡。最后一个故事则转向积极面,讲述了他和同事如何从传统软件行业转向互联网,甚至跨越国境去寻找更匹配的生活与技术环境。 作者没有给出简单结论,而是通过这些夹杂着无奈、反思与勇气的真实片段,呈现了技术人在职业道路上关于选择、环境与自我成长的复杂图景。对于身处类似阶段的读者,这篇文章更像一面镜子,提供的不是标准答案,而是关于如何清醒工作与生活的深度共鸣与参照。

IT 2016-02-06 23:58:32 / 累计浏览 2,460

Spark的性能调优

这篇文章从实战经验出发,汇总了Spark性能调优的多个关键方向。内容不仅涵盖基础配置,更深入到应用代码设计与任务执行策略。 开篇即点明,调优的第一步往往从数据序列化开始,对比了默认的Java序列化与更快更紧凑的Kryo方案。紧接着是内存管理,文章给出了具体的检测方法(如使用UI或SizeEstimator)和优化建议(如启用压缩指针)。GC调优部分尤为实用,解释了默认内存分配比例、Eden区设置,并分享了如何避免因大量对象创建导致的“GC overhead limit exceeded”错误。 对于影响性能的关键因素,文章详细阐述了并行度、Reduce Task内存使用以及Shuffle的优化。例如,通过广播变量减少大表shuffle是一个经典模式。数据本地性的五个层级及其调度策略也被清晰说明。文件存储与读取优化(如使用Parquet列存格式)和Speculation(推测执行)机制也被纳入考量。 最后,文章强调了合理设置分区数和减少不必要Shuffle的重要性,并给出了具体的代码示例指引。整篇文章既包含JVM级别的参数调整,也涉及Spark应用层的数据结构设计与API选择(如prefetchByKey vs groupByKey),是一份从理论公式到实战经验的综合性调优指南。

IT 2015-11-08 22:11:09 / 累计浏览 1,960

从构建和测试的效率说起

这篇文章从作者在EMR上执行Spark job的真实工作流出发,探讨了软件开发中一个常被忽视却极其重要的话题:构建与测试的效率优化。作者反思了自己初期因跳过本地测试阶段,直接在耗时较长的Workflow上集成测试而导致效率低下的经历,提炼出一个关键教训:测试应当分层,在成本最低的阶段尽早覆盖尽可能多的验证项,跳过大步骤往往事倍功半。 文章进一步将这种“等待的痛苦”延伸到更复杂的场景,例如将新package合并进现有版本集(version set)时,可能遭遇的依赖冲突、环境差异和调试难题,揭示了工业化软件开发中维护成本高昂的现实。 最后,作者对比了两种常见的代码分支管理策略(单分支模式与双分支模式),基于效率考量表达了对单分支模式的倾向,并点出许多团队在实践中也会不自觉地回归至此。作者以一幅经典的“程序员为何看起来很闲”的漫画作为比喻,指出频繁的上下文切换与长时间的编译等待是一种无奈的“低效勤奋”,呼吁团队将构建效率的提升置于更重要的位置——这或许比某些纯技术问题更能切实解放生产力。

IT 2015-09-21 13:50:53 / 累计浏览 1,740

Scala的模式匹配

这篇讲的是作者在从Java转向Scala学习过程中的一个核心发现:模式匹配。作者对比了自己学习Haskell和Scala的体验,指出Scala的模式匹配对有Java背景的开发者非常直观友好。 文章的核心在于对比。作者首先用Haskell的阶乘和字符串翻译为例,说明模式匹配本质上是一种强大的“变化点”控制机制,比传统if-else更清晰地处理多条件分支。接着,他将这一概念平移到Scala,展示了它不仅可以匹配值,还能匹配类型、拆解数据结构(如List)以及复杂的构造器组合。 更深入的对比在于设计范式。文章指出,传统的面向对象多态将行为内聚于类中,新增类型容易,但扩展接口(新增行为)则很麻烦。而模式匹配将核心逻辑抽离到函数中,使得新增一种数据类型(如一个新的树节点)需要修改所有匹配函数,但新增一种操作(如先序遍历)只需增加一个新函数。这清晰地揭示了两者在开闭原则应用上的不同侧重,帮助读者理解何时该选择哪种范式。 作者最后通过二叉树遍历的例子,具体展示了模式匹配如何优雅地处理递归和结构化解构,并给出了从Java转向Scala的实用学习路径建议。

IT 2015-06-01 23:17:53 / 累计浏览 4,300

建立动态规划状态转移方程的练习

这篇文章通过LeetCode上的八个经典题目,生动演示了如何攻克动态规划中最核心的一步:建立状态转移方程。作者从自身的复习笔记出发,挑选了Word Break、Unique Paths、Edit Distance等覆盖字符串、网格、树结构等多个领域的典型问题,逐一拆解其思考过程。 文章的核心观点是,解动态规划题的本质在于“状态识别”和“状态转移方程建立”这两步。像循环与递归的选择、空间换时间的优化,都只是实现技巧而非核心。例如,在解“三角形最小路径和”时,关键在于定义从底层向上积累的最短路径值f(i, k),并建立f(i, k) = min(下一层相邻状态) + 当前值的递推关系。对于“交错字符串”问题,则需定义f(i, j)表示两个子串前缀能否形成目标前缀,并据此建立逻辑或的转移方程。 作者没有停留在给出公式,而是试图还原每道题背后的状态定义逻辑。这种从具体例子提炼通用思考方法的叙述,让抽象的“建立方程”变得可触摸。对于正在学习动态规划的人来说,跟随这八个问题的思路走一遍,能有效训练如何从问题描述中提取状态,并找到状态之间递推关系的“感觉”。