背景与挑战订单单表大小早已突破 200GB，由于查询维度较多，即使增加了两个从库并优化了索引，仍然存在很多查询性能不理想的情况。随着去年大量抢购活动的开展，数据库达到瓶颈，应用只能通过限速、异步队列等机制对其进行保护。此外，业务需求层出不穷，原有的订单模型很难满足新的业务需求，但基于原订单表的 DDL 变更非常吃力，无法达到业务要求。随着这些问题越来越突出，订单数据库的切分变得愈发急迫。这次切分的目标是：未来十年内不需要担心订单容量的问题。总体架构演进垂直切分先对订单库进行垂直切分，将原有的订单库分为基础订单库、订单流程库等（本文不再展开详述）。垂直切分示意图垂直切分缓解了原来单集群的压力，但是在抢购场景下依然捉襟见肘。水平切分原有的订单模型已经无法满足业务需求，于是我们设计了一套新的统一订单模型。为同时满足 C 端用户、B 端商户、客服、运营等多方需求，我们分别通过 用户 ID 和 商户 ID 进行切分，并通过 PUMA（我们内部开发的 MySQL binlog 实时解析服务）同步到一个运营库。水平切分示意图分库分表策略1. 查询切分将 ID 和库的 Mapping 关系记录在一个单独的库中。查询切分示意图优点：ID 和库的 Mapping 算法可以随意更改。缺点：引入额外的单点风险。2. 范围切分比如按照时间区间或 ID 区间来切分。范围切分示意图优点：单表大小可控，天然支持水平扩展。缺点：无法解决集中写入瓶颈的问题。3. Hash 切分一般采用 Mod 来切分，下面着重讲一下 Mod 的策略。Hash 切分示意图数据水平切分后，我们希望方案是一劳永逸或易于水平扩展的，所以推荐采用 mod 2^n 这种一致性 Hash 策略。以统一订单库为例，我们分库分表的方案是 32 * 32 的：通过 UserId 后四位 mod 32 分到 32 个库中。同时将 UserId 后四位 Div 32 Mod 32，将每个库分为 32 个表。共计分为 1024 张表。线上部署情况为 8 个集群（主从），每个集群 4 个库。为什么这种方式易于水平扩展？我们分析如下两个场景：场景一：数据库性能达到瓶颈方法一：按照现有规则不变，可以直接扩展到 32 个数据库集群。扩展方法一方法二：如果 32 个集群也无法满足需求，那么将分库分表规则调整为 (32 * 2^n) * (32 / 2^n)，可以达到最多 1024 个集群。扩展方法二场景二：单表容量达到瓶颈（或者 1024 张表无法满足需求）方法：扩展方法三假如单表都已突破 200GB，200 * 1024 = 200TB（按照现有的订单模型估算，大概一万千亿订单，相信这一天指日可待）。没关系，采用 32 * (32 * 2^n) 策略，这时分库规则不变，单库里的表再进行裂变。当然，在目前订单这种规则下（用 UserId 后四位 mod）还是有极限的，因为只有四位，所以最多拆 8192 个表。至于为什么只取后四位，后面会有篇幅讲到。另外一个维度是通过 ShopID 进行切分，规则 8 * 8 和 UserID 比较类似，就不再赘述。需要注意的是，Shop 库我们仅存储了订单主表，用来满足 Shop 维度的查询。唯一 ID 生成方案这个方案很多，主流的有以下几种：1. 利用数据库自增 ID优点：最简单。缺点：单点风险、单机性能瓶颈。2. 利用数据库集群并设置相应的步长（Flickr 方案）优点：高可用、ID 较简洁。缺点：需要单独的数据库集群。3. Twitter Snowflake优点：高性能高可用、易拓展。缺点：需要独立的集群以及 ZK（ZooKeeper）。4. 一大波 GUID、Random 算法优点：简单。缺点：生成 ID 较长，有重复几率。我们的方案为了减少运营成本并减少额外的风险，我们排除了所有需要独立集群的方案，采用了带有业务属性的方案：时间戳 + 用户标识码 + 随机数有下面几个好处：方便、成本低。基本无重复的可能。自带分库规则：这里的用户标识码即为用户 ID 的后四位。在查询的场景下，只需要订单号就可以匹配到相应的库表而无需用户 ID。只取四位是希望订单号尽可能的短一些，并且评估下来四位已经足够。可排序：因为时间戳在最前面。当然也有一些缺点，比如长度稍长，性能要比 int/bigint 的稍差等。其他关键问题事务支持：我们是将整个订单领域聚合体切分，维度一致，所以对聚合体的事务是支持的。复杂查询：垂直切分后，就跟 Join 操作说拜拜了。水平切分后，查询的条件一定要在切分的维度内，比如查询具体某个用户下的各位订单等。禁止不带切分维度的查询。即使中间件可以支持这种查询（可以在内存中组装），但是这种需求往往不应该在在线库查询，或者可以通过其他方法转换到切分的维度来实现。数据迁移方案数据库拆分一般是业务发展到一定规模后的优化和重构。为了支持业务快速上线，很难一开始就分库分表。垂直拆分还好办，改改数据源就搞定了；一旦开始水平拆分，数据清洗就是个大问题。为此，我们经历了以下几个阶段。第一阶段数据迁移第一阶段数据库双写（事务成功以老模型为准），查询走老模型。每日 Job 数据对账（通过 DW），并将差异补平。通过 Job 导历史数据。第二阶段数据迁移第二阶段历史数据导入完毕并且数据对账无误。依然是数据库双写，但是事务成功与否以新模型为准，在线查询切新模型。每日 Job 数据对账，将差异补平。第三阶段数据迁移第三阶段老模型不再同步写入，仅当订单有终态时才会异步补上。此阶段只有离线数据依然依赖老的模型，并且下游的依赖非常多，待 DW 改造完就可以完全废除老模型了。总结与建议并非所有表都需要水平拆分，要看增长的类型和速度。水平拆分是重量级方案，拆分后会增加开发的复杂度，不到万不得已不使用。在大规模并发的业务上，尽量做到在线查询和离线查询隔离，交易查询和运营/客服查询隔离。拆分维度的选择很重要，要尽可能在解决拆分前问题的基础上，便于开发。数据库并非坚不可摧，需要保护。尽量使用简单的、良好索引的查询，这样数据库整体可控，也易于长期容量规划以及水平扩展。说明：本文基于 2016 年左右的技术实践整理，文中提到的内部工具（如 PUMA）及具体架构参数仅供参考，实际场景请根据当前业务规模与技术栈进行调整。
贯穿应用开发始终的八大性能陷阱
贯穿应用开发始终的八大性能陷阱编者注：本文为历史博文归档，内容涉及 JDK、框架与工具链版本请以当前官方文档为准。文中引用外链及图片可能失效，阅读时请注意时效性。摘要： 将应用交付给终端用户正变得越来越复杂，涉及的组件越多，犯错的可能性就越大。技术性能公司 Compuware 总结了贯穿应用交付流程始终的八大性能陷阱，这些陷阱不仅局限于开发阶段，还涉及从终端用户到后台系统的所有组件、数据库以及第三方服务。希望运维人员、架构师、测试人员和开发人员能引以为戒。常见的性能陷阱包括数据库访问低效、框架配置错误、内存使用过度、网页臃肿，以及不遵循常见 Web 性能最佳实践等。以下是 CSDN 摘译的详细内容：一、臃肿的 Web 前端面向群体： 运维人员、架构师、测试人员、开发人员通常企业在重构代码、优化 SQL 语句、应用缓存等方面都做了充足的优化，但其中大部分优化对应用的最终用户并不可见。然而，直接与用户交互的 Web 前端却常常被完全忽视。根据跟踪数据，传递给用户的页面在过去的 3-4 年里，无论是大小还是复杂度都稳步增长，同时用户对性能的要求也在提高。企业需要意识到，他们认为重要的功能其实并不是最重要的，更需要解决的是如何迎合用户的需求。但如何判断 Web 前端是否臃肿？通常的做法是与主要竞争对手以及行业领导者进行比较，通过横向对比更容易设定一个合理的目标。另外，理解用户为什么会离开你的网站也能够帮助解决客户体验问题：离开网站的主要是哪类人群？臃肿、缓慢的页面上是否有他们需要的应用功能？你可以使用缓存、压缩以及 CDN（Content Delivery Network，内容分发网络）来应对这样的问题，同时应该更严格地审核每一个新添加的功能，必要时做一些精简，以提供更好的用户体验。推荐阅读：Performance Improvement is not Performance OptimizationSuper Bowl Sunday 2013 – Winners, Losers, and Casualties二、缓慢的第三方内容和 CDN面向群体： 运维人员、架构师、测试人员仅仅关注自己的内容容易导致忽略外部组织对网站性能的影响。随着企业越来越多地使用第三方资源，应用性能管理（APM）变得更加复杂，即使这些第三方服务是为提升性能而引入的。通过观察过去 12 个月的性能峰值时期（比如假日购物季和超级碗）的数据，我们发现了两大趋势：第三方服务在用户集中访问的高峰期难以支撑，CDN 也在庞大的流量下艰难应付。分析、社交媒体、网络字体以及流行的 JavaScript 库等项目往往来自于无法直接控制的第三方服务，但你却依赖它来保证网站有效、可靠。当这些服务出问题时，用户记住的是“你”出问题了，而对这背后的第三方服务一无所知，导致你的企业品牌受损。监视、管理第三方意味着需要将它们当作独立应用、独立的基准、独立的 SLA（Service Level Agreement，服务品质协议）或者独立的 SLO（Service Level Objective，服务水平目标）。因此你需要自问：系统进行过负载测试了吗？网站三大用户高峰期同时遇到时，第三方服务是否能够抗住压力？遇到性能问题时是否存在扩展计划？过去的 12 个月最繁忙的 8 个小时中，网站用户体验达到平均性能要求了吗？最后，你的团队还需要应对这样的场景：如果相关第三方服务/CDN 遇到严重服务故障或者降低你网站的性能指标怎么办？永远要准备好 B 计划、C 计划，以缓解这些意外的问题。具体操作包括：在访问高峰期移除第三方标签、图片、内容；将内容移动到备用云服务商；多 CDN 间负载平衡；在访问高峰期将用户切换至简化版本的网站，直到流量恢复正常。想要真正控制第三方服务，你必须了解其对网站性能的影响，甚至控制第三方组件的方方面面。推荐阅读：You only control 1/3 of your Page Load Performance!Third Party Content Management applied: Four steps to gain control of your Page Load Performance!The Ripple Effect of Facebook's Outage, Third-Party Issues and the Performance Ripple EffectWebsite's Vulnerability to Third-Party Services ExposedSuper Bowl Sunday 2013 – Winners, Losers, and CasualtiesWhy Bon Ton needs real-time visibility into 85% of its content delivered by Akamai三、框架的错误使用面向群体： 架构师、开发人员通常网站或者应用少不了使用到第三方类库，比如 Hibernate、Spring、Telerik RadControls、jQuery、ExtJS、GWT 等等。因为这些第三方工具能够让你免于重写很多常见功能代码，大大提升了工作效率，因此这种现状可能永远不会改变。根据调查，很多框架都没有被正确地使用，或者没有根据应用做专门的优化。趋势表明，开发人员习惯于在网络上寻找框架，往往在简单的示例上做过验证后就应用于企业应用，而并没有验证框架是否支持所有用例、是否易于扩展并且符合需求。在很多情况下，这些框架“仅仅”是需要更好的设置，比如为 Hibernate 做正确的缓存策略，或者换用高级 API 方法访问 SharePoint 中 List 数据。下面这张图片显示 Hibernate 多次执行了同一条 SQL 语句，而在优化的模式下，应该使用缓存来避免之后的重复查询。最后，框架常常在更新中提升功能、性能以及稳定性，您应该仔细查看这些更新以从中受益。常见的案例就是从不更新 jQuery，以至于在过旧版本的浏览器中体验糟糕，甚至也无法充分发挥最新 IE、Firefox、Chrome 浏览器的性能。推荐阅读：The Session CacheThe Query CacheSecond Level CacheTop SharePoint Performance Mistakes101 on jQuery Selector Performance四、网络基础设施问题面向群体： 运维人员、架构师、测试人员网络基础设施是企业经营成功的基础，但是出问题时也难以分析原因。同一个问题可能有不同的起因，运维团队需要应用程序性能监控解决方案，以轻松、快速地将故障域隔离。性能问题的代价可能非常昂贵。根据 Aberdeen Group 的报告，甚至可能降低 9% 的收入以及 64% 的生产效率。如果采用的是 SAP 基础设施，每分钟宕机成本甚至可能高达 $15,000。判断究竟是网络问题还是应用问题，可以通过检查网络、服务器时间异常值与基准流量的比较而分辨出。但仅仅目测报告是不足以避免问题的，积极的应用性能管理首先需要及时响应关键衡量值在异常时 APM 工具发出的警报。五、云平台的伸缩性面向群体： 运维人员、架构师云计算提供了一个非常宏伟的承诺：只要你需要，可以提供无尽的资源、无尽的扩展性。这避免了硬件的过度闲置，同时可以随时应对超出预期的性能需求。但云计算同样存在陷阱：如果你的应用程序并非为可扩展而设计，硬件上再大的可扩展性也没有意义。同时，你还需要明白，云计算（除非是私有云设施）实际上并不为你所有。直接访问底层硬件要更加困难得多，因此监控起来也更困难。云也不仅仅是一个无穷无尽的 CPU、内存和磁盘的资源池，它同样提供了很多其他的服务，例如存储、通知，以及其它必须了解和监控的服务，它们都是非常关键的组件。推荐阅读：Managing Hybrid Cloud EnvironmentsAnalyzing Performance of Windows Azure StorageWhy Performance Monitoring is easier in Public than onPremise CloudsMonitoring your Clouds六、过多的数据库调用面向群体： 架构师、测试人员、开发者数据库访问也是我们经常遇到的应用问题，当我们在几乎所有共事的应用中都发现了这一问题。我们注意到一点，被问责的通常是应用对数据库的访问模式而非数据库设置。我们经常看到一条 Web 请求会查询数千条数据库状态，这背后常常有很多原因。下面这张图中显示了某企业应用调用大型机的工作流程，该大型机每次事物会执行 225 条 SQL。通过更仔细地观察我们发现，因为上面提到的原因，同一条声明被请求了数百次。除了访问模式问题，我们同时发现某些语句的执行时间过长。在这种情况下，不仅需要关注调整索引等数据库优化，分析这些语句是否可以从应用内优化也至关重要。我们经常遇到很多数据从数据库中被检索出来（使用外部存储），然后迅速被释放（GC 过于活跃）。常见的陷阱还有连接池配置错误以及应用维持连接过久导致其它线程访问数据库遭阻塞。下面的图片显示某数据库查询执行的事务流程，其中多数需要很长时间来执行。解决这个问题需要同时在应用和数据库端做优化。推荐阅读：Don't let your load balancers ruin your holiday businessSaving MIPS and MoneyThe reason I don't monitor connection pool usage七、大数据未经优化面向群体： 运维人员、架构师、测试人员、开发者应用需要处理的数据正在持续增长，大数据解决方案（NoSQL、MapReduce……）提供了新的数据存储、处理方案，但是每一项新的技术都需要为你特定的应用做相应的优化。但是这常常被误解，甚至有人认为只要增加一个额外的功能/模块（比如 MapReduce）就能处理/存储更多的数据。实际上，这仅适用于应用可扩展的情况。下面这张图中事务处理的大部分时间都花费在 MongoDB 上，仔细观察你会发现该框架访问 MongoDB 时会额外执行一次计数方法，而这是完全不必要的。在本例中，通过简单地消除不必要的调用就获得了 15 倍的事务性能提升。推荐阅读：MongoDB Anti-PatternNoSQL vs Traditional DatabasesInside Cassandra Write PerformanceWhat we can Learn from Cassandra Pagination15x Performance Improvements for Pig+HBase八、隐秘的内存泄漏面向群体： 架构师、测试人员、开发者内存和垃圾回收（GC）问题在任何企业应用中都非常突出，原因之一就在于 GC 的本质常常被人误解。除了常见的内存相关问题，比如高内存使用率、错误的缓存策略，同样的问题还存在于类的加载、过大的类以及本地内存。下面这张图中的问题就在于需要加载一个庞大的类，它占据了过多的内存，这是非常糟糕的做法。内存泄漏通常会导致内存用尽异常，往往进一步导致虚拟机崩溃，甚至会使用户当前会话和活跃事务的丢失，带来的影响非常负面，因此避免内存泄漏非常有必要。此外，高的内存占用率也会导致更为频繁的 GC，对用户响应时间产生直接影响，会话会因为长期运行的 GC 进程而停止。这种情况可以通过优化 GC 设置来调整，同时保证不“浪费”内存。即使 equals/hashCode 实现错误的相关问题也有可能导致内存问题。推荐阅读：How Garbage Collection worksDifference between JVMsGC TuningHigh Memory Usage and the Root CauseClass Load Related ProblemsMemory Monitoring in WebSphere EnvironmentsGC Bottlenecks in Heterogeneous EnvironmentsLeak Detection in Production EnvironmentsTop Memory Problems – Part I、Part II原文链接：Compuware Blog说明： 本文归档于 2013 年，文中涉及的部分工具、框架版本及性能数据可能已过时。现代开发环境中，云原生架构、容器化技术及新一代监控工具已带来新的最佳实践，请参考当前官方文档及行业最新标准。
本文是 YouTube 系统设计系列的第二篇文章。我们将承接 第一部分 的内容继续讨论，如果您尚未阅读，建议先查阅前文。在 上一篇文章 中，我们主要探讨了数据库和存储方案。本周，我们将深入讨论更多主题，包括可扩展性（Scalability）、Web 服务器、缓存策略以及安全性。数据库扩展一旦产品拥有数百万甚至数十亿的用户，就需要解决许多架构问题，其中可扩展性是最关键的挑战之一。基本上，将所有数据存储到单个数据库中不仅效率低下，而且在规模扩大后不可行。那么，您将如何扩展 YouTube 的数据库？扩展数据库时，我们可以遵循许多通用规则。最常见的方法是按需扩展。换句话说，不建议在项目第一天就完成所有工作（例如对数据库进行分区），因为几乎可以肯定的是，当您真正需要扩展时，整个基础设施和产品形态已经发生了巨大的变化。因此，合理的思路是从单个服务器开始。随后，您可以过渡到具有多个读取副本的单个主节点架构（主从模型)）。在某个阶段，您将必须对数据库进行分区并采用分片（Sharding）方法。例如，您可以按用户的位置拆分数据库，当请求到来时，将请求路由到相应的数据库分片。对于 YouTube，我们可以进一步优化这一策略。YouTube 的核心功能是视频。因此，我们可以通过将数据分为两个集群来优先处理流量：视频集群和常规集群。我们可以为视频集群提供大量资源，而其他社交网络功能则路由到资源配置较低的集群。这里的一个普遍理念是：在解决可扩展性问题时，应首先确定瓶颈，然后对其进行优化。在这种情况下，瓶颈在于视频观看流量。缓存策略我不会在此主题上过多谈论缓存，因为我们在上一篇文章"设计缓存系统"中已对此进行了详细介绍。但这里有几点值得一提。首先，谈到缓存时，大多数人的反应是关于服务器端缓存。实际上，前端缓存同样重要。如果要使网站快速运行并具有低延迟，则无法避免为前端设置缓存。这是在构建网站界面时非常常见的技术。其次，正如我们在上一篇文章中简要讨论的那样，缓存在提供视频内容方面不会带来显著好处。这主要是因为 YouTube 的大多数流量来自那些长尾视频（Long-tail Videos），为所有视频设置缓存将非常昂贵。因此，这里的总体思路是：如果您要构建类似的长尾产品，请不要在缓存上投入过多的赌注。安全性与反作弊YouTube 中有很多可以讨论的安全性话题。我想在这里介绍一个有趣的话题——视图造假（View Hacking）。在每个 YouTube 视频下，它都会显示观看次数，这表示视频的受欢迎程度。人们可以通过编程方式发送请求以伪造观看计数，那么我们应该如何保护它呢？最直接的方法是，如果特定 IP 发出太多请求，则将其阻止。或者，我们甚至可以限制每个 IP 的观看次数贡献。该系统还可以检查诸如浏览器代理（User Agent）和用户过去的历史记录之类的信息，这可能会阻止很多作弊攻击。人们可能会使用 Tor 之类的) 服务来隐藏 IP，而 Mechanical Turk 之类的 网站则允许您以非常低的成本向人们付费以单击视频。但是，黑客入侵系统比大多数人想象的要难。例如，观看次数高但参与度低的视频非常可疑。YouTube 拥有大量视频，因此提取实际观看次数的模式并不难。为了破解系统，您需要提供合理的参与度指标，例如分享数、评论数、观看时长等。几乎不可能伪造所有这些指标而不被检测到。Web 服务器架构许多人忽略了 Web 服务器，因为就系统设计而言，它似乎没有太多要讨论的内容。但是，对于大型系统（如 YouTube），您需要考虑很多事情。我想分享一些 YouTube 使用的技术策略。YouTube 服务器最初是用 Python 构建的，因此可以快速灵活地进行开发和部署。您可能会注意到，许多初创公司选择 Python 作为其服务器语言，因为迭代速度更快。Python 有时会遇到性能问题，但是有许多 C 扩展允许您优化关键部分，这正是 YouTube 早期的工作方式。要扩展 Web 服务器，您可以简单地拥有多个副本并在它们之上构建负载均衡器（Load Balancer）。服务器主要负责处理用户请求和返回响应。它应该没有什么繁琐的逻辑，其他所有东西都应该内置到单独的服务器中。例如，建议系统应该是一个单独的组件，以允许 Python 服务器从中获取数据。总结在本文中，我们尝试涵盖尽可能多的不同主题，并且可以在单独的文章中对每个主题进行更深入的讨论。将来我仍然想谈论很多事情，但是读者可以考虑一下并在评论中进行讨论。例如，YouTube 推荐是一个很大的话题，它极大地推动了用户参与度指标。您将如何建立推荐系统？此外，您如何确定当天的热门视频并向相关受众推荐？说明：本文基于早期 YouTube 架构案例整理，部分技术栈描述（如 Python 主导）反映的是历史演进阶段，现代大规模系统可能已采用更多元的技术组合（如 C++、Go 等），仅供参考设计思路。
LinkedIn 网站技术架构简介以下是 LinkedIn 网站早期的基本运营数据与技术架构概况。基本运营数据用户总量：2,200 万月度独立访客：400 万日均页面浏览量 (Page View)：4,000 万日均搜索流量：200 万日均邀请发送量：25 万日均回答提交量：100 万日均邮件消息发送量：200 万这是一个具备世界顶尖流量的网站，以下是其当时的系统架构细节。技术栈概况操作系统：Solaris（运行于 Sun x86 平台及 Sparc 架构）应用服务器：Tomcat 和 Jetty数据库：Oracle 和 MySQL数据访问：无 ORM 框架（如 Hibernate），直接使用 JDBC消息队列：使用 ActiveMQ 发送 JMS 消息（按消息类型分区，后端由 MySQL 支持）搜索引擎：基于 Lucene 构建框架架构：使用 Spring 作为逻辑架构的粘合剂（Glue）架构演化历程随着流量的增长，LinkedIn 的架构经历了以下阶段的演化：2003 ~ 2005 年单体 Web 应用程序。单一核心数据库。所有网络关系图（Network Graph）缓存于 Cloud 中（Cloud 是用于缓存的独立服务器）。搜索功能基于 Lucene，同样运行在 Cloud 中。2006 年数据库复制：复制另一个数据库以减少核心数据库的直接负载，由另一台服务器管理非只读数据的更新。搜索独立：将搜索服务从 Cloud 中移出，单独使用服务器运行搜索。引入 Databus 数据总线：作为分布式更新的核心组件，任何组件的数据更新均需通过 Databus。2008 年服务拆分：Web 应用不再处理所有业务逻辑，将逻辑拆分为多个部分，通过服务器群处理。Web 应用主要负责提供用户界面，而用户资料、小组等数据由服务器群管理。数据库分域：每个服务拥有自己的域数据库。开放架构：新架构允许其他应用链接 LinkedIn，例如支持新增的招聘和广告业务。核心缓存层（The Cloud）这里的 "Cloud" 指的是 LinkedIn 内部自主研发的缓存层，而非公共云计算服务。核心地位：Cloud 是整个架构最重要的部分，LinkedIn 的整个网络关系图都缓存在其中。数据规模：2,200 万节点（Nodes），1.2 亿边（Edges）。内存需求：单个实例需要 12GB RAM。生产部署：生产环境需运行 40 个实例。重建耗时：从硬盘重建一个 Cloud 实例需要 8 小时。实时更新：通过 Databus 进行实时更新。持久化：关闭时将数据持久化到硬盘。实现语言：缓存通过 C++ 实现，通过 JNI 调用。LinkedIn 选择 C++ 而非 Java 主要基于以下原因：尽可能减少 RAM 的使用。避免垃圾收集（GC）暂停导致系统不可用（尽管当时已采用最新的 GC 程序）。设计权衡：将所有数据放入缓存是一种限制，但 LinkedIn 指出，分割业务图将带来更大的复杂性。硬件支持：Sun 提供了 2TB 的 RAM 支持。通信架构（Communication Architecture）通信服务（Communication Service）主要用于提供持久化信息，例如收件箱消息和 Email。异步通讯：整个系统通过 JMS 进行异步通讯。消息发送：客户端通过 JMS 发送消息。路由机制：消息通过路径服务器到达相应的邮箱，或直接放入 Email 处理进程。推送模式：同时使用 Pull（用户主动获取信息）和 Push（如发送 Email）模式。技术实现：使用 Spring 及 LinkedIn 专业的 Spring 插件完成，基于 HTTP-RPC。扩展技术（Scaling Techniques）功能划分：按发送、接收、文档等功能进行划分。类别划分：按用户信箱、访问者信箱等类别进行划分。等级划分：按用户 ID 等级、Email 等级等进行划分。异步处理：所有的操作均采用异步方式。参考资料LinkedIn Communication ArchitectureA Professional Network built with Java Technologies and Agile Practices原文地址：http://www.liyingfei.com/read.php/9.htm说明：本文内容主要基于 2008 年左右的技术分享资料，反映了 LinkedIn 早期的架构设计。随着技术发展，LinkedIn 后续已迁移至更多现代技术栈（如 Kafka、Rest.li、Cloud 云服务等），文中部分组件（如 Solaris、Oracle、内部 Cloud 缓存）可能已不再适用当前生产环境。
编者注：本文为历史博文归档，主要反映 Twitter 早期（约 2011 年前后）的架构设计。文中涉及的 JDK、框架与工具链版本请以当前官方文档为准。部分引用外链图片可能失效，阅读时请注意时效性。Twitter 网站架构学习笔记作为 140 字符限制的缔造者，Twitter 既简单又复杂。简单在于，仅用 140 个字符便使数次世界性事件的传播速度超越了任何媒体；复杂在于，要为数亿用户提供这看似简单的服务，背后的架构挑战巨大。正所谓“因为简单，所以复杂”。本文将对 Twitter 网站架构的学习体会与大家分享。一、Twitter 网站基本情况概览截至 2011 年 4 月，Twitter 的注册用户约为 1.75 亿，并以每天 30 万的新用户注册数增长。但其真正的活跃用户远小于这个数目，大部分注册用户没有关注者或未关注他人，这也是其与 Facebook 当时 6 亿活跃用户不能相提并论的原因。主要运营数据如下：独立访问用户数：每月约 180 万，其中 75% 的流量来自 Twitter.com 以外的网站。API 请求：每天通过 API 产生约 30 亿次请求。Tweet 产生量：每天平均产生约 5500 万次 Tweet。用户终端：37% 的活跃用户为手机用户。来源分布：约 60% 的 Tweet 来自第三方应用。技术栈平台：Ruby on Rails、Erlang、MySQL、Mongrel、Munin、Nagios、Google Analytics、AWStats、Memcached下图是 Twitter 的整体架构设计图：二、Twitter 的平台组成Twitter 平台大致由 Twitter.com 官网、手机客户端以及第三方应用构成，如下图所示：其中，流量主要以手机和第三方应用为主要来源。以下是核心组件的技术选型与说明：Ruby on Rails：Web 应用程序框架。Erlang：通用的面向并发的编程语言。开源项目地址：http://www.erlang.org/AWStats：实时日志分析系统。开源项目地址：http://awstats.sourceforge.net/Memcached：分布式内存缓存组件。Starling：Ruby 开发的轻量级消息队列。Varnish：高性能开源 HTTP 加速器。Kestrel：Scala 编写的消息中间件。开源项目地址：http://github.com/robey/kestrelComet Server：Comet 是一种 Ajax 长连接技术，利用 Comet 可以实现服务器主动向 Web 浏览器推送数据，从而避免客户端轮询带来的性能损失。libmemcached：一个 Memcached 客户端库。MySQL：关系型数据库服务器。Mongrel：Ruby 的 HTTP 服务器，专门应用于 Rails。开源项目地址：http://rubyforge.org/projects/mongrel/Munin：服务端监控程序。项目地址：http://munin-monitoring.org/Nagios：网络监控系统。项目地址：http://www.nagios.org/三、缓存架构缓存在大型 Web 项目中起到了举足轻重的作用，毕竟数据越靠近 CPU 存取速度越快。下图是 Twitter 的缓存架构图：大量使用 Memcached 作缓存计数优化：例如，如果获得一个 count 非常慢，可以将 count 在 1 毫秒内写入 Memcached。朋友状态：获取朋友的状态涉及安全等复杂问题，因此朋友状态更新后直接写入缓存而不是查询数据库，避免接触数据库层。ActiveRecord 对象：由于对象很大所以没有被整体缓存。Twitter 将 critical 的属性存储在一个哈希（Hash）里，并且在访问时进行迟加载（Lazy Load）。API 请求缓存：90% 的请求为 API 请求，所以在前端不做任何 Page 和 Fragment 缓存。页面非常时间敏感，直接缓存效率不高，但 Twitter 缓存了 API 请求。Memcached 缓存策略改进在 Memcached 缓存策略中，Twitter 做了如下改进：直写式向量缓存（Vector Cache）：包含了一个 Tweet ID 的数组，Tweet ID 是序列化的 64 位整数，命中率是 99%。直写式行缓存（Row Cache）：包含了数据库记录（用户和 Tweets）。这一缓存有着 95% 的命中率。直读式碎片缓存（Fragment Cache）：包含了通过 API 客户端访问到的 Tweets 序列化版本，这些 Tweets 可以被打包成 JSON、XML 或者 Atom 格式，同样也有着 95% 的命中率。页面缓存池（Page Cache）：为页面缓存创建一个单独的缓存池。该页面缓存池使用了一个分代的键模式，而不是直接的失效策略。四、消息队列Twitter 大量使用消息队列。生产者生产消息并放入队列，然后分发给消费者。Twitter 主要的功能是作为不同形式（SMS、Web、IM 等等）之间的消息桥接。消息队列技术的演进历程如下：DRb (Distributed Ruby)：意味着分布式 Ruby。有一个库允许你通过 TCP/IP 从远程 Ruby 对象发送和接收消息，但是它有点脆弱。Rinda：它是使用 tuplespace 模型的一个共享队列，但是队列是持久的，当失败时消息会丢失。Erlang：曾尝试使用 Erlang 处理并发。Starling：最终移到 Starling，用 Ruby 写的一个分布式队列。分布式队列通过将它们写入硬盘用来挽救系统崩溃，其他大型网站也使用这种简单的方式。原文链接：http://www.itivy.com/ivy/archive/2011/8/14/the-architecture-of-twitter.html说明：本文内容基于 2011 年左右的技术资料整理，部分数据（如用户量、Tweet 产量）及技术选型（如 Starling、Mongrel）可能已随 Twitter 架构演进发生变化（例如后续迁移至 Scala、Java 及自研中间件等），仅供架构学习参考。
新浪微博技术架构分析新浪微博在短短一年时间内从零发展到五千万用户，我们的基础架构也经历了几个版本的演进。本文将从架构演进的角度，分析微博在不同发展阶段面临的技术挑战与解决方案。第一版架构：快速实现与推模式第一版架构的核心目标是快速实现模块功能。从架构层面分析，微博产品主要需要解决「发表」和「订阅」的问题。消息模式：第一版采用的是推（Push）模式。例如，一个明星用户拥有 10 万个粉丝，当他发表一条微博时，系统会将这条消息攒成 10 万份，分别推送给粉丝。技术栈：典型的 LAMP 架构。存储引擎：使用 MyISAM 存储引擎，其优点是读取速度非常快。部署模式：采用 MPSS 模式（多端口/进程部署）。关于部署模式的思考：假设一个互联网应用包含三个单元，通常有两种部署方式：分离部署：将三个单元分别部署在三台不同的服务器上。全量部署（MPSS）：将这三个单元部署在每一台服务器上。我们选择了第二种模式，主要解决了两个问题：负载均衡：每一个单元都有多个结点处理请求。防止单点故障：在模式一下，任何一个结点故障都会影响系统服务；而在模式二下，单个结点故障不会影响整体服务。第一版遇到的问题第一版上线后，用户增长迅速，但技术层面很快遇到了瓶颈：发表延迟：尤其是明星用户发表微博时，由于粉丝量大，推模式导致严重的延迟。资源争抢：处理明星用户发表时的延迟，可能会影响同一时间发表普通用户的体验。数据规模：数据库表从一百万增长到一亿，单库单表模式无法满足需求，需要进行拆分。锁表问题：高并发下出现锁表，考虑更改存储引擎。发表过慢：同步处理流程过长，考虑引入异步模式。第二版架构：模块化与异步优化针对上述问题，第二版架构进行了模块化拆分与异步化改造。1. 投递模式优化推模式是延迟的首要原因。我们将用户分为有效用户和无效用户：策略：如果一个用户有 100 个粉丝，发微博时不需要推给所有粉丝。例如，只推送给当天登录过的「有效粉丝」。效果：大幅减轻了推送压力，减小了投递延迟。2. 数据拆分与存储随着用户量增多，数据拆分势在必行。拆分维度：互联网产品常用 UID 拆分，但微博用户访问特点集中在「最近内容」。因此，我们按照时间拆分（例如一个月一张表）。内容与索引分离：索引数据：如微博发表地址。内容数据：采用 Key-Value 方式存储，易于扩展。分页定位问题：如果索引按月拆分，用户访问第 5 页时很难定位记录所在表。解决方案：在索引上建立二次索引。记录每个月用户发表的偏移量（ID 范围），从而迅速定位记录。3. 异步处理发表操作涉及入库、统计索引、进入后台等繁重步骤。优化：采用异步模式。前端提示发表成功后，后台通过消息队列慢慢处理索引统计。工具：使用了新浪开源的 MemcacheQ，其 stats queue 指令对大规模部署运维非常有利。架构演进驱动力：从 V2 到 V3第二版改进后，用户和访问量仍在持续增长，新的问题随之出现：系统稳定性：单点故障可能导致雪崩。访问速度：国内网络环境复杂，不同地区访问图片、JS 等资源速度差异大。数据压力与峰值：MySQL 复制延迟、慢查询；热门事件（如世界杯）可能导致每秒发表量达到数百条。平台化需求：开放平台需要支持 Web 系统与 API 系统。API 客户端请求具有不间断性，且用户行为难以预测。参考 Google 首席科学家的理念：「一个大的复杂系统，应该要分解成很多小的服务。」 例如在 Google.com 执行一个搜索查询，内部可能调动一百多个服务。因此，第三版架构的核心思路是：先有服务，才有接口，最后才有应用。第三版架构：服务化与多机房部署1. 分层架构基础服务层：分布式存储、去中心化、自动化操作。平台服务层：将微博常用功能封装成独立小服务。应用服务层：专门考虑平台各种应用的需求。API 层：支撑第三方应用。优势：平台服务与应用服务分离，实现模块隔离。即使应用服务访问量过大，也不会直接影响平台服务。2. 核心引擎改进关注关系：改为多维度索引结构，性能极大提高。计数器优化：旧版：绝对计数，容易产生一致性问题。新版：基于偏移（Offset） 思路。例如用户原读 ID 为 10000，系统最新 ID 为 10002，则未读数为 2。这种技术基本不会出错。3. 存储与部署DB 冷热分离与多维度拆分：微博主业务按时间拆分，但如私信等业务按 UID 拆分更简单。去中心化存储：极大提高用户上传与查看图片的速度。多 IDC 动态内容更新：支持动态内容在多机房同时更新，这是国内比较新颖的实践。高性能与高可用设计目前新浪微博拥有五千万用户，最高发表速率超过 3000 条/秒，明星用户微博会被几百万用户同时读取。架构设计需解决高访问量、海量数据下的三个核心问题：易于扩展、低延迟、高可用与异地分布。1. 扩展性设计面对数十亿次外部网页及 API 接口需求，且微博用户请求无法 Cache 的特点，我们采取以下思路：模块去状态：任意单元支持任意节点。去中心化：避免单点及瓶颈。线性扩展：减少模块依赖。2. 低延迟与实时性实时性是微博的核心价值，核心原则是让数据离 CPU 最近，避免磁盘 IO。淘宝核心系统专家余锋曾比喻："CPU 访问 L1 就像从书桌拿一本书，L2 是从书架拿一本书，L3 是从客厅桌子上拿一本书，访问主存就像骑车去社区图书馆拿一本书。”Cache 设计策略：Inbox：放在最快的地方，用户随时访问。Outbox：L1 Cache：存放最近发表的内容。L2 Cache：存放中期内容，访问较少时可被剔除。内容体本地化（L0）：将明星发表等高概率访问的内容本地化。3. 高可用性（HA）业界指标参考：S3 为 99.9%，EC2 为 99.5%。微博平台目前承诺 99.95%（即一年故障时间小于 4.5 小时）。容量规划：提前进行规划。监控与入口管理：设置开关，在访问量过大时拦截服务。量化监控：监控指标尽量量化。例如延迟 30 秒是小问题，延迟 10 分钟则需立即采取措施。自动化运维：亚马逊 VP 曾指出：“监控系统确实特别好，可以立即告诉我们哪里有故障，但是有 20% 的概率人是会出错的。”大型系统应为自动化设计（发布、安装、启停）。参考 Google 工程师的做法：第一周处理线上业务与系统问题，随后将碰到的情况用程序解决，下次只需一个按钮即可处理。多机房同步方案在国内网络环境下，需考虑 IDC 灾难、机房检修或掉电等情况，每个服务单元需支持多机房部署。多机房部署不仅能容灾，还能提高用户访问速度。1. 复制策略对比Master/Slave：缺点：Master 中心化，异地访问慢；存在单点风险，切换需要人工干预且有时间窗口。Multi-Master：特点：应用需避免冲突。微博用户通常只在一个地点发表内容，应用层可避免冲突。现状：最成熟的策略，但当时缺乏成熟产品。Paxos：特点：强一致写，但延迟非常大。2. 微博的解决方案我们自行实现了多机房同步方案：流程：前端应用写入数据库 -> 消息代理（Message Agent）-> 广播到多个机房。优势：数据提交成功后即不丢失，应用无需关心同步细节。类似 Yahoo 的 YMB 消息代理产品，专为广域网设计，将多机房应用归到内部。实时推送架构针对 API 请求大部分为空返回（客户端轮询）的问题，我们改用推（Push）模式。技术特点：低延迟（发表到接收 1 秒内完成）、高并发长连接服务。架构流程：Receiver：接收数据。引擎：获取用户关系，推送给相应粉丝。唤醒操作：在接口处唤醒等待的客户端。长连服务器：单台支持 10 万 + 并发连接。Stream Buffer：保存用户最近数据，用于断线重连后的数据补发（如断线半分钟，补发半分钟数据）。持久化：推送流程经过测试，持久化操作可在 100-200 毫秒内完成，确保数据不丢失且系统稳定。平台安全架构由于接口完全开放，需防范垃圾广告、刷粉丝等恶意行为。安全架构分为三个层面：实时处理：根据频度、内容相似性判断是否为广告或垃圾内容。离线纠正：针对潜伏行为（如几个月后开始发广告），事后清除以保证平台健康。监控维度：保证内容安全。实时拦截可防止约 50% 的恶意行为。离线分析可防止约 40% 的恶意行为。此外，接口安全还包含业务模块划分、安全层与权限层，为开发者营造公平环境。总结架构演进的本质是解决不同阶段的核心问题：第一版：解决发布规模问题。第二版：解决数据规模问题。第三版：解决服务化问题。将复杂问题简单化，才能设计出易于扩展的大规模架构。附录：用户规模对架构设计的影响用户规模每上一个数量级，许多设计需要重新考虑。以下数字仅供理解「用户规模影响设计」，本身并无具体指导价值。用户规模架构特征与建议10 万级单服务器，前端、后端、Cache、DB 在一起。百万级DB 和 Cache 单独部署服务器；DB 可按业务拆分（Sharding）；Cache 使用一致性 Hash 扩展；前端后端根据业务拆分，分配不同数量服务器。千万级开始重视架构设计，配备专门技术架构师；需跨机房部署，前端增加反向代理加速，数据库异地使用 Slave 副本；后端拆分，系统内部需远程调用协议。亿级架构更细分（增加数据架构师、Cache 架构师、分布式架构师）；DB Sharding 碰到瓶颈，考虑分布式数据服务；数据访问根据业务特点细分；具备专有开发、运维、测量、调优工具；所有服务地理多机房分布，具备 IDC 容灾设计；服务可降级。说明：本文内容基于新浪微博早期发展阶段的架构分享整理（用户规模约五千万时期），部分技术选型（如 MyISAM、MemcacheQ 等）具有特定的历史背景，仅供参考学习架构演进思路。
订单表的分库分表方案设计(大数据)
订单表的分库分表方案设计在业界的大数据场景下，订单表的分库分表（Sharding）设计通常主要有两类思路：按照订单号切分或按照用户 ID 切分。以下是对这两种方案的详细分析、优缺点对比以及常见问题的解决方案。主流分库分表方案方案一：按照订单号 Hash 分散该方案将订单号视为字符串，通过 Hash 算法将数据分散到多个数据库服务器中。路由规则：具体存储到哪个库、哪张表，通常由订单号中的数字特征决定。典型案例：例如微信红包的订单分库分表，是对订单号进行 Hash 计算（非简单的取模或取整），以实现数据的均匀分散。订单号末尾的 3 位数字往往包含了库名和表名的信息。查询挑战：如果需要查询某用户的所有订单，由于数据是根据订单号分散的，该用户的订单可能分布在多个库、多张表中。若全库全表扫描，效率极低。对比：即便是按照用户 ID（UID）切分，查询特定订单号时也会面临类似问题（不知道数据在哪个库）。方案二：按照用户 ID 分散以用户 ID（UID）作为切分键，业界使用较为普遍。主要有两种思路：范围切分：例如 0-2000 万 UID 对应库 A，2000 万 -4000 万 UID 对应库 B。缺点：容易出现数据热点。若某个范围内的用户下单量特别大，会导致对应库压力过高，而其他库闲置。取模运算（推荐）：使用 UID % N 的方式。优点：处理简单，数据能均匀分散到多个库，不易出现单库性能瓶颈。缺点：扩容麻烦。扩容通常需要进行数据迁移。扩容策略：为了减少迁移量，一般以倍数形式增加库数量（例如从 8 库扩容到 16 库，再到 32 库）。这种方式虽不能彻底避免迁移，但能显著减少迁移数据量，且一次扩容可支撑较长时间的业务增长。基于用户 ID 分片的详细设计以下以“按照用户 ID 取模”为例，分析具体的库表定位规则。1. 路由算法逻辑假设使用用户 ID 末尾 4 位数字进行计算：库名称定位：用户 ID 末尾 4 位 % 32Mod 表示取余数。例如 15 % 4 = 3。余数范围 0-31，对应 32 个数据库实例。库名示例：order_db_0 至 order_db_31。表名称定位：(用户 ID 末尾 4 位 / 32) % 32Dev 表示取整数商（向下取整）。例如 15 / 4 = 3.75，取整为 3。代码表示：floor(15/4)。表名示例：order_tb_0 至 order_tb_31。每个库内包含 32 张表。2. 容量估算按照上述规则：32 个库 * 32 张表 = 1024 张表。若单表数据量控制在 1000 万至 5000 万行，理论上可支撑海量数据。若希望表数量更少，可调整取模的基数（如将 32 改小）。查询场景与挑战方案一的查询问题若按照订单号分散数据，查询某用户的所有订单时，必须进行跨库、跨表查询，效率低下，通常不可行。方案二的查询问题若按照用户 ID 分散数据，虽然查询用户订单列表很方便，但在实际应用中会面临以下高频查询需求：按订单号查询：前台或后台输入订单号查询详情（Select）。按订单号更新：修改订单状态（Update）。由于数据是按用户 ID 分散的，仅凭订单号无法直接定位到具体的库和表。若全库扫描，性能无法接受。解决方案：建立索引映射表没有一种技术方案是十全十美的。针对“按用户 ID 分片后无法通过订单号查询”的问题，常见的解决思路是建立订单号与用户 ID 的索引关系表。1. 核心思路映射表设计：建立一张 order_user_idx 表，存储两项核心数据：订单号 、 用户 ID。查询流程：输入订单号，查询索引表获取用户 ID。根据用户 ID 利用路由算法定位到具体的库和表。查询订单详情。写入流程：创建订单时，需同步插入关系记录。为降低用户等待时间，该操作可异步化（如加入消息队列处理）。2. 索引表的性能优化随着时间推移，一个用户的订单量可能达到几百甚至更多，索引表本身也可能成为瓶颈。索引表分片：order_user_idx 表本身也需要分库分表。建议直接根据订单号取模进行分片。缓存策略：订单与用户的绑定关系创建后通常不会变更。可将该关系表放入内存缓存（如 Redis）。查询时优先走缓存，未命中再查数据库，以提升速度。复杂业务场景思考思考一：B2B 平台的分片键选择在 B2C 平台，卖家通常是平台自身。但在 B2B 平台，买家和卖家均需查看订单。按买家 ID 分片：卖家查询自己的所有订单时，因商品被多个买家购买，订单分散在不同库表，导致跨库查询性能低下。按卖家 ID 分片：买家在多个店铺下单，订单同样分散，买家查询自身订单时性能低下。行业实践（如淘宝）：采用数据冗余方案，拆分买家库和卖家库。买家库：按买家 ID 分库分表。卖家库：按卖家 ID 分库分表。同步机制：下单时写两份数据。先写入买家库，再通过消息中间件异步同步到卖家库。状态变更时同样通过异步消息通知对方库更新。思考二：订单号中嵌入分片信息是否可以直接按订单号分库分表？若订单分散在不同库表，用户查询历史订单列表（分页）会非常麻烦。优化方案：在生成订单号时嵌入分片信息。案例：淘宝订单号中包含卖家 ID 末 2 位、买家 ID 末 2 位。微信红包订单号末尾 3 位包含库表信息。优势：解析订单号即可直接定位库表，无需额外计算或查表。实现：若按用户 ID 后 4 位取模分片，可在订单号生成时追加用户 ID 后 4 位。B2B 场景下可同时包含买卖双方 ID 特征（需注意订单号长度）。思考三：按时间分表例如每月一张表或每年一张表。业界较少单独使用此方案切分订单数据，主要存在以下问题：查询困难：查询用户所有订单需跨库跨表。折中方案是限制查询范围（如仅查近一年）。写入热点：若按月分表，当月订单量暴涨会导致单表写入瓶颈，影响整个系统交易。原则：优秀的分表方案应尽量将写入数据分散到多个表，以达到分流效果，提高系统并发能力。总结与建议水平分库分表会显著增加开发和维护工作量。切分后会产生新的查询问题，需要额外的代码逻辑和中间表支持。建议：若业务未达到瓶颈，尽量不要过早进行水平分表。优先通过提升硬件配置（垂直扩展）扛住压力。现状：许多大型企业（如银行、运营商）早期使用高性能 Oracle 搭配小型机服务器，单表数据量可达十多亿。小型机性能强劲但成本高昂。重构时机：当交易量达到上亿规模，且公司具备足够经济实力时，再聘请经验丰富的技术团队进行架构重构。说明：本文提到的“小型机 + Oracle"方案属于传统集中式架构的典型代表。随着云原生技术的发展，现代架构更多倾向于使用分布式数据库（如 TiDB、OceanBase）或云厂商提供的分库分表中间件（如 ShardingSphere）来解决海量数据存储问题，成本与扩展性更具优势。
再谈最终一致构建全球可靠的分布式系统，往往需要在一致性与可用性之间进行权衡。2008 年，亚马逊公司（Amazon）的 CTO Werner Vogels 发表了一篇文章，描述了在大型分布式系统中容忍最终数据一致性的方法。正如 InfoQ 之前的一篇新闻 里所讨论的：系统架构师角色关键的一方面就是衡量相互冲突的需求、决定解决方案，常常要牺牲一个方面来换取另一个方面。Werner Vogels 发布的新 帖子 讨论了这些基本需求如何应用于基础设施服务，为构建 Internet 范围的计算平台提供资源。鉴于这些系统分布在世界范围内，我们处处利用复制技术来保证稳定的性能和高可用性。尽管复制技术使我们达到部分目的，但它的实现并不是完全透明的；在许多情况下，在服务内部使用复制技术都会给服务的客户带来后果。后果之一体现为对数据一致性的限制，特别是在底层分布式系统提供了一种最终一致的数据复制模型的时候。在亚马逊设计这些大型系统时，我们凭借一套有关大规模数据复制的指导原则和抽象，把注意力集中于高可用性和数据一致性之间的权衡选择。一致性的两种视角按照 Werner 所说，考虑一致性有两种方式：一种是从开发者 / 客户端的角度——他们如何观察数据更新；第二种是从服务器的角度——更新如何流经整个系统，系统对更新有何保证。客户端一致性模型在定义客户端一致性模型时，应该考虑以下内容：存储系统：它在本质上是大规模且高度分布的系统，其创建目的是为了保证耐用性和可用性。进程 A：对存储系统进行读写。进程 B 和 C：这两个进程完全独立于进程 A，也读写存储系统。客户端一致性必须处理一个观察者（在此即进程 A、B 或 C）如何以及何时看到存储系统中的一个数据对象被更新。强一致性（Strong Consistency）：在更新完成后，（A、B 或 C 进行的）任何后续访问都将返回更新过的值。弱一致性（Weak Consistency）：系统不保证后续访问将返回更新过的值，在那之前要先满足若干条件。从更新到保证任一观察者看到更新值的时刻之间的这段时间被称为不一致窗口。最终一致性（Eventual Consistency）：这是弱一致性的一种特殊形式；存储系统保证如果对象没有新的更新，最终所有访问都将返回最后更新的值。如果没有发生故障，不一致窗口的最大值可以根据下列因素确定：比如通信延迟、系统负载、复制方案涉及的副本数量。客户端一致性模型的变体因果一致性（Causal Consistency）：如果进程 A 通知进程 B 它已更新了一个数据项，那么进程 B 的后续访问将返回更新后的值，且一次写入将保证取代前一次写入。与进程 A 无因果关系的进程 C 的访问遵守一般的最终一致性规则。“读己之所写”一致性（Read-your-writes Consistency）：这是一个重要的模型。当进程 A 自己更新一个数据项之后，它总是访问到更新过的值，绝不会看到旧值。这是因果一致性模型的一个特例。会话一致性（Session Consistency）：这是上一个模型的实用版本，它把访问存储系统的进程放到会话的上下文中。只要会话还存在，系统就保证“读己之所写”一致性；系统的保证也不会延续到新的会话。单调读一致性（Monotonic Read Consistency）：如果进程已经看到过数据对象的某个值，那么任何后续访问都不会返回在那个值之前的值。单调写一致性（Monotonic Write Consistency）：系统保证来自同一个进程的写操作顺序执行。要是系统不能保证这种程度的一致性，就非常难以编程了。服务器端一致性与改进服务器端的一致性水平取决于如何在数据副本之间传播更新（这是改善吞吐量、提供可伸缩性的典型方式）。只有部分数据副本参与更新操作，且/或作为读操作的一部分与其它副本进行联系时，就会出现弱/最终一致性。发生这种情况的两种常见场景分别是为读伸缩而做大量复制的情况和有复杂数据访问的情况。在大多数这样的系统中，更新以一种“懒”方式传播到副本集内的其它节点上。所有副本都完成更新前的这段时间就是不一致窗口，读取尚未接收到更新的节点是整个系统的薄弱环节。服务器提供的一致性水平可以通过客户端/服务器通信的特定实现来改进，或者由客户端自己来完成：“读己之所写”一致性、会话一致性和单调一致性是否可以达成，取决于客户端对为其执行分布式协议的服务器的“粘度”。如果每次都是同一台服务器，那么就比较容易保证“读己之所写”一致性和单调一致性。这样做会使管理负载平衡以及容错变得稍困难一些，但这是一种简单的方案…… 客户端有时会实现“读己之所写”一致性和单调读一致性。通过给写入添加版本，对那些版本早于最后版本的值来说，客户端会丢弃这些值的读出。结语每个客户端应用对服务器造成的不一致性都有自己的耐受力，但在任何情况下，客户端应用都应该知道服务器应用提供的一致性水平。有很多改善最终一致性模型的实用方法，比如会话级别的一致性和单调读一致性，它们都为开发人员提供了更好的工具。查看英文原文： Eventually Consistent, Revisited说明：本文内容主要基于 2008-2009 年期间的技术文章与讨论（如 Werner Vogels 的 "Eventually Consistent" 原文）。虽然最终一致性的核心概念至今仍然适用，但具体的分布式系统实现与中间件技术已有所演进，请读者结合当前技术栈参考。
Java 开源建站工具美国程序员 Jon Scott Stevens 曾公布过其创业公司所使用的开发工具清单。他的技术栈基于 Java 平台，开发项目为一个尚未上线的网站，所列工具均为开源免费软件。在创业者群体中，选择 Java 作为网站开发语言的情况似乎并不多见。这并非没有原因：Java 给人的印象往往是配置繁琐、开发效率不如脚本语言便捷。但不可否认的是，Java 平台生态成熟，拥有丰富的工具包与类库，JVM 虚拟机技术亦十分先进。此外，Google App Engine 平台当时提供免费的 Java 主机服务，这使得使用 Java 建站成为一种合理的选择。笔者将他的工具清单整理如下，供日后参考。一、开发平台Google App Engine：Google 的主机服务，提供数据库支持。使用它无需自行架设服务器，并能轻松解决扩展性问题。Objectify-AppEngine：GAE 平台的数据库管理工具。GitHub：代码托管服务。二、Java 编程环境Eclipse：著名的 Java 集成开发环境（IDE）。Lombok：Eclipse 插件，用于自动生成 Java 代码，减少手工输入量。Guice：轻量级的 Java 依赖注入（Dependency Injection）框架。Apache Commons：Java 开发常用工具包。Guava：Google 发布的 Java 工具库。JRebel：允许立即查看 Java 代码修改后的执行结果，无需重启服务。Cambridge Template Engine：为 Java 应用程序提供 HTML/XML 模板引擎。RestEasy：Java 语言的 RESTful 编程框架。HtmlEasy：基于 RestEasy 的 HTML MVC 模式编程框架。三、前端工具RequireJS：对 JavaScript 进行模块化管理并按需加载的框架。JQuery：强大易用的 JavaScript 编程框架。CoffeeScript：对 JavaScript 进行改写，使其更易于开发的脚本语言。Sass Scss：CSS 代码生成工具。说明：本文整理自 2011 年左右的技术分享。文中部分链接（如 Google Code 相关项目）可能已失效或迁移，Google App Engine 对 Java 的支持策略也已发生变化。请参考时注意工具的适用版本与时效性。
低代码编程及其市场机遇剖析 | 低代码技术内幕
自 2020 年来，网易数帆探索可视化低代码编程已两年有余，打造了轻舟低代码平台用于企业应用开发。然而，不少编程技术人员对这一领域还比较陌生。我们开设 《低代码技术内幕》专栏，旨在讨论低代码编程领域中的困难、问题，以及高效的解决方案。本文为第一篇，深度剖析低代码编程带来的机遇。类比早期人类语言，最初只有“听说”系统，后因国家税收之需演化出了“读写”系统。相较于“听说”，“读写”具有易于复制传播、歧义少、保存持久等优点。类似地，早期用于人机交流的程序语言只有“字符”系统，现在因特定诉求，市场上涌现出一批“可视化低代码”系统。什么是低代码？它有哪些优势？我们为什么要学习并使用它？低代码未来的市场是怎样的？ 本文重点描述低代码未来 5~10 年的广阔市场，但仍将循序渐进，从概述低代码编程的历史发展开始。（想要了解市场规模和前景的读者，可直接跳转至「低代码的市场和机遇」部分）低代码定义：发展与变迁提高生产效率是人类永恒的主题。正因此，我们认为广义的低代码编程，取其字面意思，应当是指在某个时间段内，完成项目所需的代码量（大幅）低于其他语言（平均值）的编程模式。无独有偶，我们的认知与 ITPro Today 相合 [1]：文中描述 Fortran 和 COBOL 在 1950 年代刚出现时，它们作为高级编程语言的始祖，是那个年代里不折不扣的低代码编程语言——即便现在大多数编程人员已经不这么认为。文章 [1] 还表示，1987 年苹果公司开发的 HyperCard 编程语言（尽管不为大多数人所知）是低代码发展中的一个里程碑。HyperCard 融合了一套图形化、可扩展、可编辑的接口和数据库功能，它的部分设计理念与微软的 Visual Basic 相似，但早于后者 4 年发布。图 1：HyperCard 图标。图片来源：维基百科。图 2：HyperCard 界面。图片来源：OSXDaily。1990 年代，随着第四代编程语言（SQL、MATLAB、Clipper 等）的发展与成熟，以及高效的应用程序开发工具如 Visual Studio、Delphi 等的横空出世，UML + 设计器 + MDD 红极一时。模型驱动设计、自动代码生成和可视化开发思想成为了当年低代码开发的核心 [2]。直到这时为止，低代码的核心诉求都是提高专业开发人员的开发效率。图 3：UML 设计图2010 年代，低代码的核心诉求发生剧烈变化，开始向"让业务专家将自己的专业知识和经验落地成在线应用"转移 [2]。很多公司没有经费聘用专业的开发人员，因此希望业务专家经过一定的培训也可以上手开发应用。这些应用很大一部分是用于市场营销、销售的，也比较同质化，容易从统一的模板中搭建出。由此，便有了我们所熟知的在当前时代背景下的（狭义）低代码编程。国际上最具有影响力和代表性的产品有 OutSystems、Mendix、MicroSoft Power Apps 等，这些产品具有以下特点 [3]：为让非专业编码人员易于使用，配备了完善的可视化开发框架。不仅提供可视化开发前端、UI 的能力，还提供可视化开发复杂逻辑的能力：无需记忆编程语言的语法，通过拖拽和点击即可实现业务功能。提供完整的前后端、数据库、部署、运维等解决方案，屏蔽了前端（HTML、JS、Vue / React 等）、后端（NodeJs 或 Java + SpringBoot 等）、数据库（SQL 或 MongoDB 等）、云服务等技术，无需配置，即开即用。图 4：OutSystems 可视化逻辑。图片来源：轻舟低代码团队为什么要做编程语言？图 5：轻舟低代码可视化逻辑。图片来源：轻舟低代码团队为什么要做编程语言？了解了低代码的大致历史后，我们便知道它并不是先有定义再发展起来的事物 [2]：它的定义随着时代发展而变化，以反应当前时代市场的诉求。因此，我们不妨援引西门子 Mendix 的定义来概述 21 世纪 20 年代的低代码：低代码开发是一种可视化应用开发方法。通过低代码开发，不同经验水平的开发人员能够通过图形用户界面，使用拖放式组件和模型驱动逻辑来创建 Web 和移动应用。了解了低代码的概念后，我们来看看低代码在未来 5~10 年的广阔市场。低代码的市场和机遇本章我们先讨论低代码这一新兴技术在全球的发展，再描绘其在中国特定时代背景下（更大）的前景。全球的发展在低代码领域，目前海外大约活跃着六十七家供应商，而且它的生态系统正在迅速成长。2017 年是一个分界点，在大型软件供应商中，2017 年之前只有 Salesforce 采用 Force.com 的低代码平台。随后，微软、甲骨文、IBM 和 SAP 等企业都纷纷加入了这个市场，低代码（含无代码）市场呈爆发式增长：2020 年市场规模达到 108.2 亿美元 [4]。2021 年市场规模达到 163 亿美元 [5]。……2030 年市场规模达到 1485 亿美元 [5]。……更重要的是，目前低代码市场的增长是超过预期的。例如 2021 年 2 月曾有机构预测该年的低代码市场为 138 亿美元 [6]，而 2022 年回顾的市场实际值为 163 亿 [5]。图 6：低代码全球市场份额。原始数据来源：SpreadSheetWeb [7]当我们聚焦代表性的个体时，数据同样展现出了潜力 [2]：2018 年 Outsystems 获得 KKR 和高盛 3.6 亿美元融资，估值超过 10 亿美元。WordPress、Wix 已逐渐成长为生态完善的可视化（低代码）建站平台，数以百万计的个人和企业在这些平台上搭建自己的官网或者应用。其中 WordPress 的生态厂商 Elementor 在 2020 年初获得 1500 万 A 轮融资，在过去一年中，该插件已部署了 300 多个新功能，还被翻译成 55 种语言，目前获得超过了 400 万活跃安装。低代码建站市场和低代码企业服务领域一样快速发展中。……为什么低代码会有如此广阔的市场？正是因为低代码开发高效灵活稳定，降低了应用搭建的门槛和对专业工程师的依赖，让业务部门用拖拽的方式自行搭建应用平台，减少与 IT 部门反复沟通的流程，最终实打实地降低了人力成本，克服了传统开发交付周期长、定制能力差、难以应对不断变化的市场和客户期望等弱点 [8]：You don't need to invest in expensive training programs for your employees. You can have them build apps faster, with less training. And at the end of the day, all of this makes your business more revenue [9].翻译： 你不需要为你的员工投资昂贵的培训项目。你可以让他们以更快的速度建立应用程序，并减少培训。而在一天结束时，所有这些都使你的企业获得更多的收入。中国的优势在《“十四五”数字经济发展规划》等政策以及企业数字化转型大背景下，能为企业提供降本、增效、提质，并推动数字经济发展的低代码、无代码正在蓬勃发展 [10]；数字化转型是企业必经的可持续进化历程，越来越多的企业将从“不得不转”转变到“主动要转” [11]。其中无论是政企、医疗、金融业务，还是房地产、制造、零售等，我们都能看到低代码的身影 [12]（以下内容均来自该引用）：泰康人寿 - 泰行销 APP 分公司专栏项目：包含新闻、文章管理，搜索、滚动新闻，消息提醒，新闻、文章列表，大量的数据统计、业绩数据、排名数据、考核数据以及客户数据等特性、内容。使用轻舟低代码平台，通过可视化方式实现数据建模、页面搭建以及业务逻辑编排，在教练指导下 2 周完成开发。项目通过扩展组件实现 IT 资产复用，可导出为模板并成为平台资产，其他分公司从而可以通过模板快速搭建专栏。中国联通软件研究院：针对业务部门业务需求多样化，开发门槛高，开发周期长等问题，通过研究低代码在轻量级业务场景、通用型业务场景下的应用，帮助企业开发人员提高研发效率，降低开发门槛，赋能企业运营，助力数字化转型。同时，中国联通低代码赋能政企业务受理集约，实现联通政企业务受理集约流程可视化、分钟级配置、所见即所得，流程发布由原来的 2 周提升至 0.5 天，支撑 30 省超 300 个地市，8 大类业务的受理集约。重庆长安汽车与华为基于 AppCube 合作：构建智慧党建系统、在线业务预算系统等 6 个企业应用。其中在线业务预算系统需要将庞杂的表单业务在线化，实现计划管理系统、合同管理系统、整车利润分析系统，以及填报、审核、分析、自动生成相应财务报表等预算编制流程等。借助 AppCube 低代码，应用构建效率整体提升了 2~3 倍，经过 3 个多月的努力，基本实现预期目标。中国雄安集团电子招标采购交易平台：结合雄安集团业务复杂性和具体需求，最终在评审结果汇总计算、价格评审计算、费用计算等招采业务场景中使用计算规则引擎整体解决方案，只需 2 小时即可完成传统做法需要 5 天的业务系统升级迭代流程。此方案的计算代码部分由低代码组成。……宏观统计数据也表明：2022 年中国低代码无代码市场规模预计为 40.6 亿 元，并预计在 2025 年达到 118.5 亿元，其年均复合增长率高达 42.9%。其中，低代码产品是总体市场的主要构成，占比 77.6% [10]。2022 年将有 40%~60% 的大型企业使用低代码开发应用 [13]。低代码的领军之一 Mendix 更是表示，低代码市场在中国展现出巨大潜力 [14]：低代码发展正当时，中国将取代美国成为低代码开发的全球领导者。报告显示 85% 的 IT 决策者表示正积极拥抱低代码技术，认为低代码是他们不容错过的趋势，且近一半（44%）的日常开发工作可以在低代码平台上完成。尽管转型领军者的数字化优势进一步加大，2022 年进入转型领军者行列的企业比例仅为 17%，与去年（16%）基本持平。低代码在中国有着巨大的增量市场 [11, 14]。低代码应用在高校放彩，促进产学研全链路一体化建设 [15]…………立足中国市场，随着“十四五”规划的推进，中国企业的数字化转型必将取得长足的进步，在降本增效的驱使下，低代码也必将成为中国大多数企业的最佳选择。更多的统计数据根据 UserGuiding 的统计，人们对低代码持有以下观点 [16]：美国有超过 50 万个 计算机科学职位空缺，预计它的增长速度是其他工作领域的 2 倍，但世界上只有 0.5% 的人口知道如何编码。到 2024 年，低代码将占到应用开发活动的 65% 以上。“易用性”是人们在描述 低代码 / 无代码开发平台时使用最多的 (20%) 正面词汇，这是基于从所有低代码 / 无代码开发平台公司的客户评论中收集的数据。24% 的用户在使用低代码 / 无代码平台之前完全没有经验。其中 40% 的用户大多有商业背景。84% 的企业转向低代码 / 无代码，因为它们能够减少 IT 资源的压力，提高上市速度，并让企业参与数字资产开发。31% 使用低代码/无代码的企业没有使用它们来构建和交付任何最高价值的应用程序。72% 的用户在 3 个月或更短的时间内用低代码应用开发应用程序。低代码 / 无代码解决方案有可能将应用程序的开发时间减少 90%。30% 的企业在未来更愿意使用定制的低代码 / 无代码来处理复杂的商业逻辑。……现有的低代码产品最后我们再介绍一些支撑起低代码市场的代表性产品，从离我们比较近的国内产品开始。时至今日，国内的低代码产品已经比较丰富，例如 轻舟低代码平台、阿里低代码引擎、葡萄城活字格、奥哲氚云、轻流、普元 EOS 平台 等：轻舟低代码：是网易数帆打造的企业级应用开发平台，兼具（下述）原生低代码和开发工具的特点。它通过简单易上手的可视化编程语言，帮助企业搭建高复杂度、融合企业 IT 资产、交互视觉高保真还原的企业级应用，致力于帮助企业实现低成本、高效率的数字化转型和应用创新。阿里低代码引擎：代表产品有钉钉宜搭等。钉钉宜搭基于阿里云的云基础设施和钉钉的企业数字化操作系统，提供低门槛、高效率的数字化业务 [2]。云平台的厂商希望借助低代码吸引更多用户购买其云服务，因此其低代码产品的发展方向上以深度整合自家云资源，降低门槛为主。葡萄城活字格（Forguncy）：是厂商整合自身的开发工具资源后推出了低代码产品，发展方向上以产品力提升为主，在技术门槛和扩展性中间会更倾向于后者 [2]。用户集中在初创型软件开发团队、行业软件代理商、系统集成商和中大企业 IT 中心，而不是一线业务人员。奥哲氚云：是低代码行业的创业公司，正处于风口快速发展阶段，后获得阿里系投资，更关注流量和市占率。产品发展上倾向于在降低门槛、与钉钉等流量平台整合 [2]。类似的还有明道云、简道云等。轻流：是专注于流程和表单的 BPM 厂商在自身软件的基础上增加可视化设计器，进一步降低使用门槛后，从而实现低代码转型的产品。这些产品的核心优势是强大的工作流引擎，但这种基于表单而不是数据模型的架构，在应对复杂应用场景时可能会遇到诸多障碍 [2]。类似的产品还有炎黄盈动 AWS PaaS 等。普元 EOS 平台：可以看成行业软件向通用平台的演进的尝试，它希望利用低代码技术降低实施、特别是实施中客户化开发环节的工作量，提升行业软件自身的竞争力 [2]。典型产品还有用友的 iUAP 等。目前上述产品的海外市场并不多。根据 Fortune Business Insights [4]，我们列出几个重要的海外低代码产品及其简介 [17, 18] 供参考：Appian：配备了原生部署工具和与 Jenkins 等开发工具的集成选项，允许用户建立业务流程管理（BPM）应用程序，帮助企业组织和优化业务流程。Microsoft Power Apps：可快速构建客制化的商业应用程序，它包含了预建的人工智能组件，还具备良好的可扩展性。Salesforce：为每个用户创建个性化的体验，用拖放字段和动作来定制页面，在正确的时间提供正确的信息，构建智能的、可移动的应用程序。OutSystems：提供可视化、模型驱动的工具，以实现快速开发和部署应用程序。它还提供实时性能监控和拥有高安全性。Mendix：常被用于构建改善公司内部流程的解决方案，其用户只需最少量的编程知识和一个良好的策略。Mendix 被用于数字保险、SAP 供应链、货物交付管理和商业分析等各种市场领域。Oracle APEX：Oracle APEX 使开发人员能够快速构建应用程序并解决实际问题。开发人员不需要成为掌握大量网络技术的专家，他可以专注于要解决的问题，Oracle APEX 会完成其余繁重的工作。……那么，当前哪些低代码平台最具市场影响力？对于这个程序员永远关注的话题，我们找到了来自 GradientFlow 的“排名” [19] 如下（选购产品时需综合考量，不能依赖单一排名）：图 7：低代码开发平台指数。数据来源：GradientFlow小结我们概述了上世纪和本世纪的低代码产品定位（提高专业开发人员的开发效率 vs 让业务专家将自己的专业知识和经验落地成在线应用），讨论了低代码的大致定义，然后通过大量统计数据和一些中国的典型案例描绘了低代码在近年的高速发展和其在未来 5~10 年国内外的巨大市场，最后介绍了一些支撑起这个市场的国内外的低代码产品。为了较好地解决低代码产品的诉求，低代码平台具体需要怎么做，技术上有哪些挑战，不同的方案效果如何？我们会在本专栏文章的后续篇章中一一解明。参考The History of Low-Code/No-Code Development - ITPro Today低代码发展现状调研和思考 - 管中窥豹非其全轻舟低代码团队为什么要做编程语言？(起草中，后续发布)Low Code Development Platform Market Scope with Size, Share - Fortune Business InsightsLow Code Development Platform Market Size is projected to - Globe News WireGartner Forecasts Worldwide Low-Code Development Technologies Market to Grow 23% in 2021 - GartnerHow Big is the Global Low-Code / No-Code Market and How Fast is it Growing? - Spread Sheet Web化繁为简：低代码行业研究报告 - 艾瑞咨询The History of Low-Code Development Platform - kissflow《2022 年中国低代码无代码市场研究及选型评估报告》- 海比研究院数字化转型：可持续的进化历程 - 埃森哲《2022 低代码 · 无代码 应用案例汇编》，2022 年 8 月第一版 - 企业数字化发展共建共享平台2022 中国“低代码”领域十大趋势 - 腾讯新闻低代码发展正当时，中国将取代美国成为低代码开发的全球领导者 - 美通社资讯2022 年中国低代码行业生态发展洞察报告 - 艾瑞咨询No-Code / Low-Code Statistics and Trends - User Guiding9 Best Low-Code Platforms To Use in 2022 - Trio Developers10 Best Low-Code / No-Code Platforms To Create an Digital Product - Mobi TouchRanking Low-code Development Platforms - Gradient Flow说明：文中引用的市场规模、案例数据及统计报告主要基于 2020 年至 2022 年期间发布的信息，旨在反映该阶段低代码技术的发展趋势与市场机遇。