IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者
首页 / 淘宝搜索
IT 2015-01-11 23:51:00 / 累计浏览 2,880

从未降级的搜索技术-Hippo在线服务调度系统

这篇讲的是,在搜索团队经历了一次手忙脚乱的双11“搬机器”救援后,如何从零开始构建一个真正服务于在线系统的调度平台——Hippo。 故事要从一次教训说起。当年双11,团队为天猫和主搜分别预留了14倍和1倍的资源余量。然而流量突变,主搜压力远超预期,天猫却只涨了4倍。工程师们被迫手动迁移机器来救场,改配置、发数据、起进程,折腾一个半小时才勉强应对。更无奈的是,这种紧急操作往往还未必能准确命中流量高峰。每年大促都像一场无法预演的战役,让运维和开发都身心俱疲。 为了解决这些痛点——资源僵化、扩容迟缓、手动部署风险高,团队调研了当时主流的调度系统。但发现Yarn对于C++在线服务显得笨重,而FUXI和Mesos在资源回收上采用强制策略,可能影响在线服务的稳定性,这与搜索系统“高可用、资源分配稳定”的核心要求相悖。因此,他们决定自研一个专注在线服务的平台。 Hippo采用了两层架构:Master层负责核心的资源管理与调度,而具体的AM层则允许各应用定制自己的调度逻辑。它的设计核心在于保证在线服务的平稳运行:资源回收策略更为柔性,并针对海量数据(如40G索引、多GB模型)的快速分发和部署做了特别优化。这篇文章详细拆解了系统从需求诞生到架构落地的全过程,展示了一个为复杂在线场景量身定制的调度系统是如何思考的。

本机暂存
IT 2015-01-11 23:49:09 / 累计浏览 2,600

聊聊多线程程序的load balance

这篇讲的是,如何在一个常见的“接收者-工作线程池”模型中,主动优化负载均衡以提升性能。作者从一个大家熟悉的设计出发:一个 receiver 线程接收请求,放入队列,通过条件变量唤醒任意一个 worker 处理。他敏锐地指出,完全依赖内核的调度和负载均衡可能带来问题。 核心问题有两个:如果 worker 线程远多于 CPU 核心数,唤醒时几乎无法均匀分配到不同 CPU,导致某些核过载而某些核空闲,形成“伪并发”。其次,即使 worker 数量合理,内核的负载均衡也未必能确保将任务分配到不同的物理核心(避免争抢共享缓存和计算资源)。 对此,作者提出了应用层的“微调”方案。一方面,将 worker 线程数控制在接近或略小于 CPU 核心数。另一方面,更关键的是,通过线程绑定(affinity)固定 worker 在特定 CPU 上,并设计一个分级的条件变量唤醒机制。这能确保新任务被优先分配给空闲或低负载物理核心上的 worker,从而主动实现更优的负载分布。 文章通过精心设计的实验验证了结论。例如,将 worker 线程数从 240 降至 24 后,CPU 利用率从 2200% 提升至接近 2400%。启用绑定线程和分级唤醒后,处理 12 个并发任务时性能得到进一步提升。作者也发现,对于依赖内存缓存的任务(如 mmap 读文件),让 worker 集中在相邻 CPU 上反而可能提升性能,这体现了负载均衡策略需具体场景具体分析。 作者通过细致的对比实验表明,这些在应用层主动进行的微调,有时能取得比等待内核调度更优的效果。

本机暂存
IT 2015-01-11 23:45:41 / 累计浏览 3,520

从未降级的搜索技术-天猫SKU搜索

这篇技术文章详细拆解了天猫搜索从“商品级”跨越到“SKU级”的完整演进历程。作者直面传统搜索的痛点:当用户想买特定规格(如64G白色iphone6)时,旧引擎只能按商品维度返回结果,导致价格展示不准确、过滤和排序形同虚设。 文章核心聚焦于如何实现既能支持SKU粒度精准检索,又不造成海量数据冗余的难题。最终提出的“主表(商品)+子表(SKU)”二维架构是解决方案的关键:引擎能同时处理两个维度的查询,并形成“一主带多子”的结果结构,让过滤、排序等环节都能基于真实的SKU信息工作。 通过CSPU聚合、精准排序等具体场景的实现,该技术上线后在多个类目带来了可度量的收益,例如沙发类目平均IPvUV价值增长8.50%。这不仅是一次架构升级,更是将“尺码个性化”等精细体验从想法变为现实的基石。

本机暂存
IT 2015-01-04 14:17:59 / 累计浏览 2,320

从未降级的搜索-主搜索分层优化

这篇讲的是淘宝主搜索如何通过索引分层技术,将集群架构从二维升级到三维,从而解决长期存在的性能与扩展性瓶颈。 作者从主搜索沿用多年的二维架构出发,指出其存在机器消耗多、低质量商品拖累效率、索引结构单一且难以支持多样化排序等核心问题。文章提出的分层优化方案,核心思路是将商品按质量(Good/Bad)和特定排序需求(如人气)拆分成不同集群,并设计相应的检索策略。例如,对人气排序查询优先走仅包含头部商品的Excellent集群,而对一般查询则优先查Good集群,不足时再补充Bad集群。 这种三维架构带来了显著收益:不仅将集群规模缩减了36%,整体检索性能提升了120%,最终还带动了6%的搜索GMV增长。文章用清晰的架构图和具体数据,展示了如何通过精巧的索引设计,在控制成本的同时满足多样化的排序需求,为主搜索的业务拓展提供了坚实的技术基础。

本机暂存
IT 2014-12-04 13:21:22 / 累计浏览 3,960

当cpu飙升时,找出php中可能有问题的代码行

当PHP进程CPU占用率突然飙升至接近100%,如何在解释型语言中精确定位问题代码行?这篇内容深入Zend引擎内部,展示了如何通过调试工具直击PHP运行时状态。 文章的核心思路是,利用Zend引擎维护的全局数据结构(`executor_globals`)来获取执行现场。作者聚焦于其中关键的两个变量:`active_op_array`和`current_execute_data`。通过分析其C语言结构体定义,文章指出`active_op_array`中的`filename`和`function_name`记录了当前执行的文件与函数,而`current_execute_data`里的`opline`则指向了正在执行的操作码(opcode),其`lineno`字段直接对应源码行号。 实战演示部分极具操作性:编写一个包含死循环的PHP脚本,通过`gdb`附加到进程后,只需几条简单的打印命令,就能立即看到脚本正卡在第四行的`sleep(1)`上。文章还介绍了PHP源码附带的`.gdbinit`文件,其中的`zbacktrace`命令能一键生成完整的调用栈回溯,大大简化了调试流程。 这篇内容为PHP性能问题排查提供了一种底层的、引擎级的视角。它告诉我们,即使面对解释型语言,依然可以通过理解其底层实现(如Zend执行器),在应用层优化之外,找到更直接的故障诊断路径。

本机暂存
IT 2014-12-04 13:20:25 / 累计浏览 2,440

HQueue:基于HBase的消息队列

这篇讲的是阿里一淘团队如何用HBase“搭积木”,造出一个叫HQueue的分布式消息队列。作者从时间序列存储、MapReduce数据输入输出等场景的实际需求出发,选择了站在HBase的肩膀上。 核心思路很巧妙:把消息直接存为HBase的KV对,利用HTable的多Region实现高并发,用Coprocessor来保证消息ID的唯一有序,并处理消息的持久化。这样一来,HBase本身的自动Region迁移、动态负载均衡和数据持久化能力,就直接变成了HQueue的“超能力”,实现了自动容错、消息不丢和性能优化。 文章还详细拆解了它的设计细节:比如用PartitionID+Timestamp+SequenceID组合成RowKey来保证消息全局有序,通过不同的Scanner支持灵活扫描,以及在0.3版本后引入的基于ZooKeeper的订阅推送机制。整体来看,这为需要可靠消息队列又已有HBase技术栈的团队,提供了一个无需额外组件、可随HBase无缝升级的解决方案。

本机暂存
IT 2014-12-03 23:58:46 / 累计浏览 2,200

Pora2应用中HBase高并发读写性能优化

这篇讲的是淘宝搜索的Pora2实时分析系统在大量使用HBase进行高并发读写时,所遇到的一系列性能“坑”及优化实践。系统上线后出现处理延迟、集群压力大的问题,排查发现根源主要在于HBase的使用方式。 文章拆解了几个典型案例:一是HBase默认的Periodic Flusher机制引发了过于频繁的flush与compact,通过调整其超时阈值得到了缓解;二是下游消费消息队列时未控制Scan频率,对Region Server造成了无谓压力;三是在超大并发下,过多的客户端连接耗尽了服务端Handler,作者的解决方案是减少进程数、增加线程数以复用连接。 此外,还涉及了因rowkey生成代码bug导致的数据访问热点,以及Bulk Load数据未做Major Compaction引起的读取性能衰减。文章最后总结道,高并发场景下必须合理使用HBase,避免不当操作形成“越慢越压、越压越慢”的恶性循环。这些从实战中沉淀的细节,对同类系统的设计与调优很有参考价值。

本机暂存
IT 2014-12-02 00:07:03 / 累计浏览 7,460

memory prefetch浅析

作者在用VTune分析程序性能时,发现大数组的非连续访问成为了CPU热点。经过排查,主要原因是这类访问模式对CPU缓存(Cache)很不友好,导致了大量的缓存未命中,从而拖累了性能。 为了优化这个问题,作者引入了x86架构提供的`prefetch`系列指令。其核心思想是,在程序真正用到数据之前,提前将指定地址的内存数据预取到各级缓存中,从而“掩盖”掉后续访问时的内存延迟。 文中提供了一段详细的测试代码,通过控制内存访问模式(顺序或跳跃)和计算复杂度,量化对比了预取指令的效果。测试数据显示,在跳跃访问内存导致性能严重下降的情况下(例如从22秒涨到66秒),加入恰当的预取操作后,执行时间基本恢复到了顺序访问时的水平(约28秒)。这直观地证明了预取指令在特定场景下能有效隐藏内存访问开销。 文章最终总结出prefetch的适用边界:当程序同时存在可观的内存访问延迟和一定的计算开销时,预取能有效提升性能。但如果计算本身很轻量,或者数据本身已在缓存中(如顺序访问),单纯依靠预取来加速读内存的意义则不大。

本机暂存
IT 2014-12-02 00:00:14 / 累计浏览 2,660

PHP语法分析器:RE2C && BISON 总结

这篇文章从作者的PHP到C编译项目phptoc说起,详细拆解了如何使用re2c和bison这对经典组合,为PHP构建一套自定义的语法分析器。作者的目标是让PHP程序员能更轻松地编写接近C扩展性能的代码,因此需要理解并重现PHP的核心解析流程。 文章的核心在于厘清re2c(扫描器)与bison(解析器)的分工与协作。re2c负责将原始的PHP代码字符串,根据预设的规则(如scanner.l文件)“扫描”并拆解为一个个token(如T_ECHO、T_LNUMBER)。随后,bison依据语法定义文件(如parse.y),接收这些token流,并按照语法规则执行相应的语义动作,最终完成代码的解析与转换。文中通过“echo 1;”这个简单例子,直观地展示了从字符识别到token生成,再到语法动作执行的完整闭环。 作者没有停留在理论,而是提供了具体的代码结构示例和关键宏定义解释,比如YYCURSOR如何定位当前扫描位置,以及yyparse如何调用yylex形成一个协作循环。这种从项目需求出发,结合工具原理与实践代码的讲述方式,将原本复杂的编译原理知识点拆解得清晰可循,对希望深入PHP底层或需要实现类似解析工具的开发者来说,是一份扎实的实践笔记。

本机暂存
IT 2014-12-01 23:41:09 / 累计浏览 2,320

小心,apc可能导致php-fpm罢工!

这篇讲的是一次线上502故障的深度排查。作者从php-fpm进程数异常飙升入手,通过`pstack`命令分析进程堆栈,发现几乎所有进程都卡在申请APC互斥锁上,指向了死锁。 根因的追查过程很有价值。通过GDB调试,他们定位到互斥锁被一个已不存在的进程(11274)持有。结合php-fpm日志,最终确认是该进程因`hsf`扩展的内存错误(SIGSEGV)而异常退出,退出时未能释放它持有的APC锁,从而导致了整个进程池的级联阻塞。 解决思路清晰:一是彻底的方案,即根据APC维护者建议迁移到不再有此风险的`opcache`;二是针对性修复,参考PHP官方bug报告修改APC源码,在进程异常退出时强制释放锁。文章完整展示了一个由扩展缺陷引发的内存级死锁案例及其分析路径。

本机暂存
IT 2014-12-01 23:38:03 / 累计浏览 2,800

redis超时问题分析

这篇讲的是Redis在实际运维中遇到超时问题的深度排查。作者从dump中心cm8集群的真实故障出发,发现内存充足的情况下依然出现超时,进而深入Redis源码寻找根因。 问题最终定位在三个方面:一是网络闪断,可通过监控带宽排查;二是内存使用,尤其是RDB持久化时fork子进程会触发Linux的写时复制机制,可能导致物理内存不足而发生swap,引发超时。解决方案包括调低swappiness参数、谨慎使用RDB持久化,或改用AOF及读写分离架构。 第三个原因在于Redis单进程串行处理命令的架构。基于epoll的事件驱动模型意味着任何慢命令(如sort、hgetall)都会阻塞后续请求,导致超时。因此,从应用层避免使用慢命令、增加实例分流是关键优化方向。文章结合源码片段,清晰剖析了从网络、内存到内部执行模型的完整故障链路。

本机暂存
IT 2014-12-01 23:37:27 / 累计浏览 3,040

gbk和utf8编码自动识别方法[php版]

这篇讲的是如何在PHP中自动识别中文字符串的编码是GBK还是UTF-8。 它针对一个具体场景:当输入字符串只能是这两种编码之一时,如何快速准确地做出判断。文章给出的核心方案是一套清晰的判断逻辑,并提供了可直接使用的PHP函数。 作者的思路很巧妙,不是简单地二选一,而是先“假定”字符串是UTF-8,然后逐步排查它不符合UTF-8规则的各种情况。具体的判断规则有四条:从检查字节是否符合UTF-8的多字节结构,到验证其中是否包含中文字符,再到处理“鏈條”、“瑷媄”等容易混淆的特殊情况,逻辑层层递进。 代码实现上,它通过分析字节的二进制模式来逐字节解析,效率很高。作者提到,实测在处理20万条查询时,整个过程仅需1秒左右,准确率也令人满意。 这段代码在需要处理大量未知编码的中文文本(例如爬虫解析、数据清洗)的场景下非常实用。不过,使用时要注意确保PHP文件本身以GBK编码保存,因为代码中包含了用于特殊情况比较的汉字常量。

本机暂存
IT 2014-12-01 23:14:11 / 累计浏览 6,520

萃取(traits)编程技术的介绍和应用

这篇讲的是C++中一项优雅而实用的泛型编程技巧——萃取(traits)技术。作者从STL中迭代器与算法解耦的需求出发,首先展示了一个核心矛盾:为了让统一的算法(如find)既能作用于自定义容器迭代器,又能兼容原生指针(如int*),我们需要一种机制来统一获取迭代器所指对象的类型信息。文章清晰地介绍了如何通过`iterator_traits`这个中间层,结合模版偏特化来解决这一问题,使`int*`这样的原生指针也能被正确萃取出`value_type`。 接着,文章将这一思想从迭代器扩展到了更广阔的“类型萃取”领域。作者以SGI STL的`__type_traits`为例,说明了如何利用萃取技术探测类型特性(如是否有平凡的构造函数、析构函数)。通过为内置类型(如int)提供特化版本,算法(如copy)就能在运行时选择最高效的实现路径——比如直接进行内存拷贝而非调用复杂的构造函数,从而显著提升性能。 文章从具体问题切入,逐步抽象出“特性萃取”的通用编程模式,最后落脚于其对C++泛型库性能优化的实际价值,展示了如何通过编译期的类型信息提取来弥补语言本身的局限。

本机暂存
IT 2014-11-27 12:58:20 / 累计浏览 1,600

使用HBase EndPoint(coprocessor)进行计算

当面对千万、亿级数据量时,对HBase表进行全表扫描来统计行数或分组聚合,会带来巨大的网络与RPC开销。这篇技术分享给出了一个优雅的解法:使用HBase的Endpoint协处理器。 作者的核心思路是,将计算逻辑直接部署到数据所在的RegionServer上执行,只将聚合后的结果返回客户端。这就好比把计算任务“下发”到每个数据分区,避免了海量原始数据的网络传输。文章将这个过程比作一个精简高效的、运行在RegionServer上的MapReduce。 具体实现分为三步:首先定义一个继承自CoprocessorProtocol的计数接口;然后在服务端实现该接口,在Region内完成数据扫描与计数;最后在客户端通过HBase API发起远程调用,并汇总各个Region的统计结果。文章不仅给出了清晰的代码示例,还详细说明了如何通过配置文件或Shell命令来部署这个协处理器。 通过行数统计这个简单例子,文章展示了Endpoint协处理器如何为HBase添加灵活的计算能力,使其成为高效应对大规模数据聚合挑战的利器。

本机暂存
IT 2013-07-15 13:14:05 / 累计浏览 3,000

解决进程间共享内存,由于某个进程异常退出导致死锁问题

这篇讲的是在进程间共享内存编程中,因某个进程异常退出而导致死锁的经典坑。作者从一个线上服务重启后的超时问题出发,层层排查,最终定位到根源:一个读进程在持有读写锁时突然崩溃,导致锁的计数器(`nr_readers`)没有递减,写进程因此永远等不到锁,共享内存数据无法更新,最终引发服务故障。 作者不仅用测试代码复现了这一问题,更深入探讨了如何解决。读写锁在这种场景下缺乏自动恢复机制。一个巧妙的出路是改用互斥锁,并设置其`PTHREAD_MUTEX_ROBUST_NP`属性(Robust锁)。当持锁进程死亡时,锁不会永久阻塞,而是返回`EOWNERDEAD`,后续线程调用`pthread_mutex_consistent_np`即可修复锁状态,使其恢复正常。此外,作者还提醒,通过共享内存交换数据时,务必增加完成标记,以确保数据在进程崩溃时不会处于不完整的中间状态。 文章从实际故障切入,完整呈现了“发现问题-分析根因-测试验证-寻求方案”的解决链条,特别是对Robust锁的应用,为处理跨进程的异常状态恢复提供了非常实用的思路。

本机暂存
IT 2013-07-15 13:02:02 / 累计浏览 3,900

JVM的GC简介和实例

这篇文章从JVM内存布局讲起,重点剖析了Java堆的分代模型——新生代如何划分Eden与Survivor区,以及Minor GC触发时基于复制算法的运作机制。作者通过一段简单代码和jstat监控,演示了在默认配置下,新对象如何优先在Eden区分配,当Eden空间不足时如何触发一次Minor GC,以及部分存活对象如何被直接晋升到老年代。整个过程配合真实的GC日志进行了拆解。 文章还简要介绍了Serial、ParNew、CMS等常见的垃圾收集器,并利用JVM参数(如-Xmn、-XX:SurvivorRatio)调整新生代配置,为后续对比不同收集器的行为埋下伏笔。它并非泛泛而谈理论,而是将“对象优先在Eden分配”、“大对象直接进入老年代”等原则,用可观察的内存变化和日志数据做了印证。对于想理解GC基本行为,或对“Minor GC后对象为何直接进入老年代”感到困惑的初学者,这份实战记录提供了清晰的图示和数据支撑。

本机暂存
IT 2013-07-10 13:43:42 / 累计浏览 2,420

玩转robots协议

这篇讲的是互联网上一个看似基础却引发过巨头战争的协议——Robots协议。作者从2012年百度与奇虎360之间那场著名的“爬虫纠纷”与亿元索赔案切入,生动地引出了这个“君子协定”的重要性。 文章的核心在于阐明,Robots协议并非技术强制规范,而是网站与搜索引擎爬虫之间的一种行业默契。它通过一个简单的`robots.txt`文件,让网站管理员能够表达自己的意愿:是欢迎所有爬虫,还是只对特定爬虫关闭某些路径。文章用淘宝(完全禁止百度爬虫)和京东(屏蔽特定路径及一淘爬虫)的真实案例做了对比,清晰地展示了不同网站会根据自身商业策略和需求来制定抓取规则。 在讲解具体用法时,文章区分了“基本玩法”和“高阶玩法”。基础部分详细解释了`User-agent`和`Disallow`两大指令,如何屏蔽整个站点、特定目录或文件。而进阶部分则巧妙解决了“如何屏蔽a1-a100目录却单独允许a50”这类实际问题,引入了`Allow`指令的使用逻辑——“谁管得细就听谁的”,并指出了谷歌等搜索引擎对高级指令支持度更好的现状。整个讲解由浅入深,将技术细节融入到了实际场景的考量之中。

本机暂存
IT 2013-06-09 13:34:53 / 累计浏览 3,260

不同SSD盘组合搜索引擎单机性能测试[2013年版]

作者从搜索引擎单机性能优化的需求出发,在一台配置了24核Xeon E5 CPU、近50GB内存的Linux服务器上,对不同SSD盘组合策略下的HA3引擎性能进行了系统测试。测试数据规模可观,索引达59G,摘要73G。 文章详细对比了多种方案:从全内存基准、单盘SSD,到由两块或三块盘组成的RAID 0、RAID 1,以及不使用硬件RAID而采用软连接或数据分片的方式。测试严格控制了IO调度、预读、线程数等变量,并通过abench工具获取峰值QPS。 核心发现颇具实用价值:当索引量翻倍时,性能近似减半,表明IO是关键瓶颈。单纯增加RAID 0的磁盘数对搜索引擎引擎性能提升有限,瓶颈会转移至CPU锁开销。最引人注目的结论是,两块SSD盘不使用硬件RAID,而是通过软件将数据按term哈希键分片存储,能达到约18%的性能提升,优于RAID 0的12%提升,也远超传统的多段软连接方式。 文章最终给出了分层建议:在CPU性能制约时,应重点解决IO瓶颈(如采用多盘按term切分);当磁盘增多后,则需关注CPU锁优化。对于写入场景,也提出了普通盘与SSD搭配的实践方案。

本机暂存
IT 2013-05-01 22:35:53 / 累计浏览 3,960

google group varint 无损压缩解压算法的高效实现 改进版

这篇讲的是Google Group Varint无损压缩解压算法的一个高效C++实现,作者在此前版本的基础上进行了关键优化,使整体性能提升了约20%。 文章的核心亮点在于那个256行的静态索引表。传统的Varint解码需要逐个判断每个字节的编码长度,而Group Varint将四个整数打包,通过一个字节的索引就能“一表查四数”,直接确定这组整数在压缩流中的起始位置和各自的长度。这种查表法将解码操作从逐位判断提升为批量定位,是速度飞跃的关键。 优化后的效果非常惊人:处理100万个整数(4MB数据),压缩耗时约3.2毫秒,解压仅需3.7毫秒。换算下来,其解压吞吐量可以达到每秒1GB,非常适合对速度要求极高的场景。文章不仅展示了性能对比,还直接提供了完整的实现源码,包括那个精心构造的索引表,开发者可以即拿即用。 对于追求极致性能的系统工程师而言,这个实现展示了如何通过巧妙的数据结构设计,将算法理论上的优势转化为实际运行效率的巨大提升。

本机暂存
IT 2013-04-07 13:11:07 / 累计浏览 1,600

分享两个强符号,弱符号引起的编译问题

这篇文章从两个真实的编译问题出发,剖析了C++开发中一个隐蔽的“坑”:强符号与弱符号机制在debug和release模式下可能引发截然不同的结果。 作者分享了两个具体案例:一是模板类的静态函数特例化,在release模式下由于符号未被正确声明为弱符号而丢失,导致只执行了默认实现;二是在cpp文件内部定义的内联函数,release模式下因内联展开而在外部符号表中找不到定义。这些问题在debug时一切正常,却在上线前的关键阶段暴露。 文章指出,根因在于编译优化(release模式)改变了符号的属性与生成规则。随后,作者系统梳理了强符号与弱符号的定义、链接规则及其实际用途——弱符号允许重定义和可选覆盖,是实现类似“插件”式扩展的基础。最后,文章还以一个Zookeeper日志函数的弱符号改写为例,展示了如何利用这一特性优雅地解决实际工程问题。 这类问题的隐蔽性很强,只有深入理解底层的链接机制,才能在开发和构建阶段主动规避风险。

本机暂存