{
    "componentChunkName": "component---src-templates-case-study-detail-ts",
    "path": "/case-studies/36",
    "result": {"pageContext":{"next":{"id":39,"attributes":{"feature":true,"title":"客户端分片到Proxy分片，如丝般顺滑的平稳迁移","content":"client方式的优势是实现简单，只需要通过简单的配置即可完成拆分操作。\n\n在本地通过分片进行计算，得到真实的库和表进行路由，性能相对较高。\n\n不依赖于三方，没有单点故障。 \n\n \n\nclient方式的劣势是每个项目都要去管理分片，读写分离等信息，没办法统一进行管理。\n\n当需要升级的时候只能所有项目都进行升级，没办法统一升级。\n\n最难的点在于需要推动各个业务域进行升级，升级的周期较长，对业务方的压力也比较大。\n\n相对于proxy方式，client方式连接数占用的比较多，一个数据源10个连接，部署10个实例就是100个连接。\n\n \n\nproxy方式指的是部署一个独立的服务，这个服务会实现Mysql协议，应用中只需要连接这个独立的proxy服务，把它当做一个完整的独立的数据库使用即可。\n\n分库分表等规则全部由这个proxy去管理，对应用透明。\n\n \n\n彩虹桥DAL是得物基础架构组研发的proxy方式的代理服务，有了彩虹桥就可以统一管理所有数据库，可以对数据库操作做限流保护，可以做压测时的数据自动进入影子库，可以监控慢SQL等等。\n\n \n\n**接入方案**\n\n**直接修改老数据源-改成连接彩虹桥**\n\n\n\n最简单，最快速的方式就是直接将所有老的数据源改成新的代理数据源，然后重新发布就可以了。\n\n这个方案确实快，但是不够好，不够完美，不够稳定，为啥？\n\n\n\n这其实跟改接口是一样的道理，当某天加了个新的需求，你会发现直接在老接口的基础上改一下就完成了。改完后测试发现当前没问题，但是老版本的产品还在用，然后兼容出问题了。这种场景下如果你是对接口进行升级，之前是v1，现在改成v2，老的逻辑完全不动，就完美了呀。\n\n\n\n如果我们直接改成最新的方式，那么如何平滑的发布上线呢？\n\n \n\n全部发布，万一彩虹桥那边配置有问题，或者有其他的问题导致不可用，你这不就尴尬了么，当然也可以回滚历史版本。\n\n \n\n另一种方式就是灰度发布一台机器，进行测试，没问题后全部发布，有问题就不发，其实也不错。就怕过了一段时间，万一彩虹桥有Bug, 我们还是得回滚到接入彩虹桥之前的版本。\n\n \n\n**新增一套数据源 - 支持开关切换到新数据源，下线老数据源**\n\n \n\n新增数据源，支持开关切换就跟升级API版本是一样的逻辑。保持老逻辑不变，上线时还是走老的数据源，然后通过开关动态切换到新的数据源，完成上线动作。\n\n \n\n当然这边也会出现上面提到的问题，比如彩虹桥不可用之类的情况，也可以通过灰度配置的方式来测试。将开关配置灰度到某个节点上，只切这个节点的数据源，进行测试，没问题后可以扩大范围。有问题开关一关就还原到之前的逻辑了，无影响。\n\n \n\n如果用了一段时间，彩虹桥有Bug的话同样改下开关的配置即可回滚，也不用重新发布项目。\n\n \n\n此方案不足的点就是会占用一分部数据库的连接，因为老的数据源还存在。此时需要改代码去掉老的数据源，就能释放了。如果后面还要去改代码，那还能叫完美的方案么？\n\n \n\n这点我也考虑到了，还是通过配置开关来关闭老的数据源，但是这个操作得重启服务，重启后就只有一套数据源了。（**请确保以后只用彩虹桥的方式才用此开关**）\n\n \n\n**实现步骤**\n\n1. 配置2套新数据源，连接彩虹桥的地址，订单的库分为老订单库和新的分库分表库。\n2. 新增2个动态数据源，用于开关动态切换，使用AbstractRoutingDataSource管理。\n\n![img](https://u01f1kqxrl.feishu.cn/space/api/box/stream/download/asynccode/?code=NTk4N2JjMTE5ZWM2ZDYzNzE3YTc4ZjJiNmNkNjU4MDNfWkxpTWY5N1djdFUwWVRaQ1ExMmEwbDZnUllkd01SeWtfVG9rZW46Ym94Y25IQ25iS3Z3SEo4QVN0TjlrV0JlNFBkXzE2ODAwNzQxMDA6MTY4MDA3NzcwMF9WNA)\n\n3. 老数据源通过@ConditionalOnProperty来控制是否加载，默认加载，用于后期下线老数据源。\n\n4. 读写分离兼容client和proxy方式。\n\n5. 分片算法重写，之前用的Sharding-Jdbc3.X版本，新的彩虹桥基于5.X版本深度定制开发，在自定义算法这块有变化，目前彩虹桥的分片算法全部在彩虹桥的扩展包中，不在订单里面。\n\n \n\n**注意事项**\n\n**select last_insert_id()不支持** \n\n \n\n在insert中通过select last_insert_id()实时返回当前插入的自增ID场景需要修改，目前订单中就一个地方用到了，而且上层其实没消费这个ID, 如果后面有其他场景需要获取刚插入的ID可以手动提前获取分布式ID，然后再用这个ID存到表中。\n\n \n\n**强制走从节点查询**\n\n \n\n老的读写分离用的Sharding-Jdbc做的，正常情况下默认查询走从节点，其他走主节点。而现在订单里面默认都是走主节点，如果需要强制走从节点需要使用HintManagerHolder.clear() 清空Hint信息来实现。\n\n \n\n猜测最开始没有从节点，业务都走主节点。后面加了从节点，如果用默认的方式查询都会走从节点，但是在某些业务场景中一致性比较高，必须走主节点查询，相对于标记出哪些查询走从节点来说，默认全部走主节点更稳。然后根据业务场景再标记是否要走从节点，这样几乎不会影响老的逻辑。\n\n \n\n接入彩虹桥后，默认是彩虹桥和Sharding-Jdbc两套数据源共存的，所以在HintManagerHolder.clear() 这块也需要做兼容，同时支持彩虹桥和Sharding-Jdbc方式的强制走从节点。\n\n \n\n**第一步：兼容老代码**\n\n \n\n将所有使用HintManagerHolder.clear()改成SqlHintUtils.clear()。\n\n```java\npublic class SqlHintUtils {\n    /*      清空Hint信息     */    \n    public static void clear() {        // ShardingJdbc        \n    \tHintManagerHolder.clear();        // 彩虹桥DAL        \n    \tBifrostContext.clearNotAutoClearPrimary();    }\n}\n```\n\n \n\n**第二步：新增注解强制走从节点，比代码更优雅** \n\n \n\n以后有新的查询需要走从节点就在Dao方法上增加@ForceSlave注解即可，此注解只能作用于Dao方法上，加在其它层无效。如果Dao方法上加了注解，那么方法内所有的查询操作都将走从节点。\n\n \n\n老的clear相关的代码其实可以用注解代替，但为了保险起见还是不改变原有的方式，新的可以用注解的方式。","date":"2021-08-13","author":"SphereEx","excerpt":"随着订单数量的增多，以及大促时需要扛住比平时多N倍的流量，单库单表的瓶颈日益显现。\n需要对数据库进行水平拆分，目前订单使用的是客户端分片的方式进行拆分，采用Sharding-Jdbc框架实现。","metaDescription":null,"createdAt":"2023-03-29T07:22:28.685Z","updatedAt":"2023-03-29T07:22:29.697Z","publishedAt":"2023-03-29T07:22:29.693Z","locale":"zh","caseStudiesType":{"data":null},"cover":{"data":null},"metaLogo":{"data":null},"localizations":{"data":[]}}},"prev":{"id":35,"attributes":{"feature":true,"title":"openGauss X ShardingSphere，分布式方案的另一种最佳实践","content":"Apache ShardingSphere 持续助力于 openGauss 分布式数据库能力的构建。openGauss 数据库自 2020 年 6 月开源以来，受到了业界的广泛关注，现已吸引众多伙伴、开发者参与其中，共建繁荣的数据库生态。面对如今海量数据，超高并发等诸多场景，openGauss 将目光转向于分布式解决方案，专注于解决海量数据存储、超高并发吞吐、大表瓶颈等众多难题，与 ShardingSphere 一起构建全栈开源分布式解决方案，实现 openGauss 的又一大突破。\n\n## 分布式解决方案\n![640.png](https://sphereex-media-1305704183.cos.ap-beijing.myqcloud.com/640_f6ba503847.png)\n<center>图1 分布式解决方案整体框图</center>\n\nopenGauss 融合了众多开源组件，用以构建集数据水平扩展、分布式事务及治理一体化的全栈开源分布式解决方案，整体框图如图 1 所示。\n\n- 其中 ShardingSphere-Proxy 为开源分布式解决方案，具有分库、分表、分布式事务、弹性伸缩、读写分离等众多能力；\n- HAProxy 结合 Patroni 的 REST API，可以始终识别数据库的主节点，保证高可用场景，同时可实现负载均衡；\n- 每个 Patroni 高可用节点支持一主多备，每个节点使用 Paxos 协议保证数据的一致性，各个节点可以部署在相同或不同的区域，用以保证多地多中心的数据安全。\n本分布式方案运用 ShardingSphere-Proxy 强劲的分布式能力，通过 kubernetes 管理集群，prometheus 监控集群状态，从而构建全栈开源的分布式解决方案。\n\n## 产品优势\n\n1. **极致扩展能力，灵活扩缩容**\n计算与存储能力可通过水平拆分实现线性扩展，最高可达数据 6400 分片，性能随扩展准线性增长，可有效解决单表数据量膨胀问题；结合业务流量，灵活平滑进行数据节点的扩缩容，智能读写分离，实现分布式数据库的自动负载均衡。\n\n2. **丰富企业级特性**\n支持分布式存储过程、触发器，分布式事务，全密态数据加密，WDR 诊断报告，提供丰富的企业级特性。\n\n3. **一键部署，屏蔽底层依赖**\n标准化镜像确保多环境一致性交付，容器化部署，实现物理资源池化，降低对平台的依赖性，简洁高效，实现应用秒级部署。\n\n4. **超高可用，实现异地容灾**\n强有力的集群管理、运维能力，支持同城、异地、多地多中心灵活部署，基于 Paxos 协议保证数据的安全及强一致性，提供 RPO=0 的多种容灾能力。\n\n5. **开源开放，构建全栈生态**\n开源 openGauss 单机及分布式解决方案，鼓励更多伙伴、开发者共同参与其中，共建数据库的繁荣生态，打造全栈开源生态链。\n\n## 应用场景\n\n- **金融系统**\n基于多数派共识协议 Paxos，提供分布式强一致事务能力，确保分布式环境下数据的强一致能力；多地多中心，提供 RPO=0 的能力，保障金融级可靠。\n\n- **政企办公**\n提供全密态数据加密，安全可信，支持软硬件生态，保障高可用场景，为政企办公保驾护航。\n\n- **智慧电网**\n支持同城、异地、多地多中心灵活部署，根据业务的潮汐特点灵活扩缩容，助力智慧电网。","date":"2021-09-15","author":"SphereEx","excerpt":"Apache ShardingSphere 持续助力于 openGauss 分布式数据库能力的构建","metaDescription":null,"createdAt":"2023-03-28T07:02:39.490Z","updatedAt":"2023-03-28T07:10:25.644Z","publishedAt":"2023-03-28T07:10:25.641Z","locale":"zh","caseStudiesType":{"data":null},"cover":{"data":null},"metaLogo":{"data":null},"localizations":{"data":[]}}},"article":{"id":36,"attributes":{"feature":true,"title":"数据为王，携程国际火车票的 ShardingSphere 之路","content":"## 业务背景\n\n随着国际火车票业务的高速发展，订单量快速增长，单数据库瓶颈层面的问题逐渐显露，常规的数据库优化已无法达到期望的效果。同时，原先的底层数据库设计，也存在一些历史遗留问题，比如存在部分无用字段、表通过自增主键关联和各个应用直连数据库等问题。\n\n为此，经过讨论后，我们决定对订单库进行分库分表，同时对订单表进行重构，进而从根本上解决这些问题。\n\n## 业务挑战\n\n目标确定后，实践起来可不轻松，出现了很多的问题和挑战。这里列举一些典型问题，大致可以分为两大类：分库分表通用问题、具体业务关联问题。\n\n**分库分表通用问题**\n\n- 如何切分，垂直分还是水平分？分片的键，如何选取？\n- 如何根据键值路由到对应库、对应表？\n- 采用什么中间件，代理方式还是中间件的方式？\n- 跨库操作等问题，如跨库事务和跨库关联？\n- 数据扩容问题，后续如何进行扩容？\n\n**具体业务关联问题**\n\n- 各个应用直连数据如何解决？\n- 如何进行平滑过渡？\n- 历史数据如何恰当迁移？\n\n## 方案选型\n\n### **如何切分**\n\n切分方式，一般分为垂直分库、垂直分表、水平分库和水平分表四种，如何选择，一般是根据自己的业务需求决定。\n\n我们的目标是要从根本上解决数据量大、单机性能问题等问题，垂直方式并不能满足需求，所以我们选取了水平分库+水平分表的切分方式。\n\n### **分片键选取**\n\n一般是根据自己的实际业务，来选择字段来作为分片的键，同时可以结合考虑数据的热点问题 、分布问题。比如订单系统，不能根据国家字段进行分片，否则可能会出现某些国家很多的订单记录，某些国家几乎没有订单记录，进而数据分布不均。相对正确的方式，比如订单类系统，可以选择订单 ID；会员系统，可以选择会员 ID。\n\n### **如何路由**\n\n选定了分片的键之后，接下来需要探讨的问题，就是如何路由到具体的数据库和具体的表。\n\n- 映射路由\n- 分组路由\n- 哈希路由\n- 分组哈希路由\n\n在实践中，我们结合了前面的几种方式，借鉴了他们的优点不足，而采用了此种方式。因为分组方式，能很方便的进行扩容，解决了数据扩容问题；哈希方式，能解决分布相对均匀，无单点数据库热点问题。\n\n## 选择 Apache ShardingSphere 的理由\n\n在产品选型过程中，考虑了多款产品，也包括公司自己实现的产品，最终选择了 ShardingSphere，理由如下。\n\n### **技术环境**\n\n- 我们团队是 Java 体系下的，对 Java 中间件有一些偏爱\n- 更偏向于轻量级组件，可以深入研究的组件\n- 可能会需要一些个性定制化\n\n### **专业程度**\n\n- 取决于中间件由哪个团队进行维护，是否是名师打造，是否是行业标杆\n- 更新迭代频率，最好是更新相对频繁，维护较积极的\n- 流行度问题，偏向于流行度广、社区活跃的中间件\n- 性能问题，性能能满足我们的要求\n\n### **使用成本**\n\n- 学习成本、入门成本和定制改造成本\n- 弱浸入性，对业务能较少浸入\n- 现有技术栈下的迁移成本，我们当前技术栈是 SSM 体系下\n\n### **运维成本**\n\n- 高可用、高稳定性\n- 减少硬件资源，不希望再单独引入一个代理中间件，还要考虑运维成本\n- 丰富的埋点、完善的监控","date":"2021-08-24","author":"瑞华，携程高级后端开发工程师","excerpt":"国际火车票业务分库分表重构实践","metaDescription":"我们是携程火车票研发团队，负责火车票业务的开发以及创新。火车票研发在多种交通线路联程联运算法、多种交通工具一站式预定、高并发方向不断地深入探索和创新，持续优化用户体验，提高效率，致力于为全球人民买全球火车票。","createdAt":"2023-03-28T07:18:10.674Z","updatedAt":"2023-03-28T07:18:12.210Z","publishedAt":"2023-03-28T07:18:12.206Z","locale":"zh","caseStudiesType":{"data":null},"cover":{"data":null},"metaLogo":{"data":null},"localizations":{"data":[]}}}}},
    "staticQueryHashes": []}