在本人现在的公司和本人工作过的上一家公司,本人有幸参与并实施了公司业务系统的架构拆分,现将之前的工作总结下。

1,为什么要进行系统拆分

首先我们需要想想,到底什么样的系统需要进行拆分?并不是所有的系统都需要进行拆分,拆分系统是一件耗时并且有风险的操作,所以再拆分前需要再三考虑是否真的需要拆分,拆分的好处是否大于风险。个人认为进行系统拆分的原因主要有以下几个方面:

a,组织结构发生了变化,从最初的一个team逐渐发展成了多个team,每个team需要负责各自单独的业务,这时需要从原来的系统(或者单体程序)中按照业务线拆分出相应的子系统

b,随着业务的发展,系统的压力也上来了,功能之间的耦合越来越严重,严重影响系统性能,比如核心功能A在某个时间段的业务处理量突然飙升,导致功能B无法访问或者整个应用停顿

c,差异化定制需求,需要根据客户的选择进行自由组装成一个系统,这时就需要对系统进行模块拆分

d,从测试角度来看,单体程序的问题莫过于系统错综复杂,牵一发而动全身,比如一个微小的需求改动可能导致其他功能不能访问,所以测试的时候需要做全量的回归测试,这对项目的按时上线提出了严峻的挑战。

以上几点是本人工作中所遇到的不得不进行系统拆分的场景,其中abd的场景占了绝大多数,后面主要围绕这几方面谈下该如何进行系统拆分。

2,系统拆分的原则

系统拆分不是一个简简单单的活,可能你面临动辄数以百万级的代码,这是一项耗时耗精力且很容易背锅的活,所以在进行拆分之前应该反复思考,是否真的需要进行拆分,是否有其他办法解决当前系统的问题,拆分的好处是否大于弊处,这些都应该在前就思考清楚。那么拆分时该如何下手,从哪里下手呢?肯定不能随便找一个地方就开干了,我们进行系统拆分需要有一定的原则,本人大致总结下有几点:

a,一定要以业务为导向,通常来说一个系统会按照业务功能分成多个块,每个块又与其他业务有关联,所以在拆分时最好是先把大的业务边界先确定,然后再按照业务功能再次进行拆分,循序渐进

b,拆分时要按部就班,按小模块进行,每拆分出来一个小模块,严格测试上线没有问题后再拆其他模块,不要妄想一步登天。

c,尽可能按照现有的技术进行拆分,不要轻易引入新技术,这样会大大增加成本。我们应该根据team的实际情况,让熟悉各个技术的人在自己擅长的范围内进行操作。我本人的观点是引用的技术越简单越好,不要出现为了使用某个看似高端的技术而把系统搞得难以理解

3,系统拆分的实施

系统拆分一般分为数据拆分和功能拆分,从实际项目来看,两个缺一不可,相辅相成。

功能拆分:功能拆分首要的问题是确定系统边界,这中间可能涉及到跟多个team沟通,撕逼,因为涉及到业务边界的问题大家可能会推来推去。我们要坚持的一个原则就是低耦合高内聚,同时识别边界的时候要仔细思考当前的功能对哪个业务的影响较大,那么就归属谁,如果对各个业务影响都很大,那应该抽取出来,形成一个公共的应用,为各个业务提供服务。下面我们举个例子来大致说明下功能拆分该如何实施,还是以本来所在的业务二级市场转让为例,假设我们的系统(网站)最初只有转让这一个功能,其主要流程如下:

后面随着业务的发展,我们有了更多的team负责更多的业务,也有了更多的产品,更多交易,该如何对上面的功能进行拆分呢?

首先我们思考下这个流程,发起转让是业务线上的行为,在转让时,可能需要进行一些必要的检验等,这些都是业务所独有的特征,接下来,产品生成与上架,这里产品的维护和管理具有很强的通用性,是一个通用业务,应该抽出来成为一个基础服务应用,因为其他业务也会生成产品并上架,所以这里会拆分出一个product-app专门负责产品的维护管理工作,它是一个公共基础服务。接着用户的投资/交易也应该是是一个公共基础的服务应用,因为投资/交易具有通用性,同时投资时也涉及到支付,可能支持不同的支付方式,这里我们拆出两个应用,一个应用trading专门处理交易,比如交易前的各种check,各种展示等等,一个应用payment专门处理支付。然后就是到生成资产了,这也应该拆分出来成为一个公共的基础服务,专门用于资产的维护与管理。

综上,上面的功能可以大致做如下的拆分:

上面的例子只是本人从工作中的项目精简抽象出的一个非常简单的例子,实际项目中所要进行的拆分比这个要复杂得多,这里只是提供一种通过分析出通用业务/基础服务后进行拆分的思路,实际中可以根据各自业务的特点,灵活的选择拆分方式。

实际项目中本人还遇到过另一种拆分方式,类似于功能拆分,但又有点区别,它不是基于业务的功能拆分,而是基于性能点的系统拆分,大致可以这样描述:

这是一个系统,当中包含了众多的功能或者模块,项目上线后,运行平稳,无任何异常,平时访问的量也一般般,突然某天搞了一个促销活动,业务量是飙升至平时的N倍,这时与这个促销活动相关的功能块就出现了访问变慢、卡顿的现象,到最后整各系统受其影响,其他与促销无关的业务行为也不能正常进行了。这时候,就迫切需要把这个影响性能的点给拆分出来,这个影响性能的点可能不只是包含单一的业务功能,所以拆分的时候需要把跟这个性能点相关部分一并给拆分出来。

数据拆分:数据拆分其实就是数据库的拆分,主要分为垂直拆分和水平拆分。垂直拆分一个方式是按照业务属性将表进行拆分归类,分属到不同的库上,这样一方面业务更清晰,另一方面也将数据库压力分担到了不同的库上。另一个方式是从性能角度出发,将访问频率较高的和较低的数据拆分开来,形成单独的库。我们实际项目中的拆分都是基于前一种方式,垂直拆分后大致如下图:

其优点在于:

a,拆分后业务清晰明了

b,系统扩展更为容易

c,数据维护更加简单

缺点在于:

a,原来直接join操作就可以获取到的结构,现在需要通过接口访问获取,复杂度上升

b,还是会存在单库瓶颈

c,事务处理比之前复杂,会引入分布式事务

数据库拆分的另一种方式就是水平拆分,可以解决垂直拆分存在的单库瓶颈,水平拆分是把同一表(库)拆分到不同的库中,其大致示意图如下:

从上图可以看出,水平拆分后如果想访问数据库,需要有一层路由,具体的路由规则得根据拆分方式来定,一般来说,拆分方式大致有以下几种:

a,按范围拆分,比如id在0-1000w的拆分到db0,1000w~2000w的在db1,以此类推。这种方式的好处在于扩展容易,后面如果有新增的用户,现在的库容纳不下,可以直接新增库,不需要再次进行数据迁移,缺点在于比如user库,用户有活跃度之分,可能访问量集中在某个库上,着实不大可取,因为很有可能重现单库瓶颈,另一种按照范围拆分的方式是按照日期,不过也会存在热点问题

b,id取模:比如要拆分成4库,id%4=0在db0,id%4=1在db1,以此类推。这种方式的好处是分布均匀,不存在热点问题,缺点在于后续扩展比较麻烦

c,根据实际的业务特性进行拆分:比如有的数据可能未来几年内不会有太大的变动,这时可以根据实际访问的性能要求,拆分为固定几个库。

上面的几种方式中除了c之外,最常用的就是b这种方式了,但是b的缺点也很明显,所以实际拆分中我们会想尽各种办法尽可能避免此方法带来的扩展困难问题。举个例子,我们要从两库扩展到三库,不可避免的需要进行数据迁移,但是数据迁移是一件耗时的工作,所以我们要做的工作就是避免大规模的数据迁移,如何可以做到避免数据迁移呢?我们主要有以下几种思路:

a,充分预估未来业务的增长以及对性能的要求,提前进行规划,保证拆分后的库数能够支撑未来N年的数据库容量。比如从目前来看,未来两三年只需要拆分成4库就可以完全满足需求,那我们提前进行规划,可以拆分成8库或者16库,这样应该在好长一段时间(公司能否撑这么久?)都不会有扩展的需求。

b,为了理解方便,先来看一个数字游戏,看一个取余操作 :

上图表示的是0,1,2,3对1,2,4取余数的结果,那根我们的水平拆分有啥关系呢?下面举个例子就清楚了,假设我们现在是单库,分了4张表(b这种方式需要有这种类似的前提条件,不然无法进行下去),如下图所示:

假设id=0,1,2,3,因为我们只有一个库,所以id=0应该落在t0表中,id=1落在t1表中,id=2落在t2表中,id=3落在t3表中,现在我么要扩展到两个库,按照上面的取余表,id=0还是落在原来的库(标记为db0),id=1应该落在新增的库(db1) 中,id=2落在原来的db0中,id=3落在db1中,相当于是要把原来库中的t1和t3表整表搬迁到db1中,扩展后如下图所示:

扩展后,如果要寻找该所属哪个库,id%2即可,如果要知道该落在哪张表,id%4即可。这里还有个问题就是虽然我么这里所做的是整表搬迁,相对于数据迁移来说好了许多,但是还是需要耗时的。这里有个解决办法就是---考虑到现在的服务器都是可组装可拆卸的,然后数据库存储的数据最终都是要写到硬盘上的,我们只需要保证原来的t0-t3四张表分属4个不同的硬盘即可,这样在扩展时,只需要停机把硬盘拔出来,插入到另一个库对应的服务器硬盘上即可,这样更加节省时间!

从2库扩展到4库的操作与上面的类似,这里就不再说明了。这里可以看出,b这种方式有较大的局限性,它是建立在正好进行过分表的基础上,分表的数目决定了后面可以分库的数目,如果再进行拆分,就要进行数据迁移了

c,结合按范围拆分以及hash取模两种形式的优点,我们想到了一种拆分方式,看个例子:假设我们规划的单库容量是2000w条数据,我们按照group进行划分,第一次划分的时候预计容纳8000w条的数据,所以我们得有4个库,后面我们再次进行扩展到可以容纳24000w条数据,所以此时需要增加8个库,我们把这8个库归属到另一个group,所以会有下面的数据来记录这个对应关系:

例如id=100,通过查询可以知道在group_id=1的组中,这个组有四个库,所以在group_id=1的组中,根据id%4可以定位到该落在哪个组,同理如果id=16000w,查询数据表,可以知道应该落在group_id=2的组,id%8的数据库中。后面如果再进行扩容,只需要建立这样的对应关系即可,完全不用进行数据迁移。但是这个方案有个缺点,那就是多了一次查询,如果有大量的请求进来,压力都在这张表上,所以针对这个方案,我们还需要设计一个合理的缓存方案,缓存这样的映射关系,避免多次查询这个关系映射表。

d,还有种方式就需要依托于数据库架构了,目前应该大多数的数据库都是有基本的主从架构的,基于这个前提,我们可以进行如下的水平拆分,首先看下如下图:

一般来说都会有上图中的数据库架构,主库可以提供读写,从库只读。此时我们想扩展为2库时只需提升从库为主库,解除主从关系,id%2=1的数据落到原来的slave上,然后改动应用程序,重新发布即可。这里有个问题就是拆分后的两个库都会有冗余数据,可以后台跑个任务删掉这些冗余数据即可。有了单库扩展为2库的经验,后面我么分别针对拆分后的2库分别添加从库,这样后面就可以从2库扩展到4库了,大致示意图如下:

采用这个方案时,因为从库升为主库,所以有一段时间系统所有的读操作都会落在主库上(除非等到新的从库加上去后同步完主库的数据),这点需要结合实际的业务看下数据库是否能够撑得住,会不会有大量的读和写操作同时进来(不过即使这种情况应该问题不大,我么还可以上缓存)。

从上面可以看到,水平拆分也会存在引入分布式事务的问题,以及跨库join的问题,实际使用中,线上投产的数据是不允许跨库查询的,可通过rpc等接口方式获取数据,同时我们会建立一个统一的库,然后把需要的表的数据都同步到这个库,专门用于我们定位线上问题时查数据。

4,实际实施过程中我们遇到的问题

a,跨数据库事务问题

根据具体的业务场景,以及对实时性的要求,引入一些轻量级的分布式事务解决方案解决这类问题(用的最多的就是补偿方式保证数据最终一致),总之有个原则就是,我们的业务尽可能不要引入重量级的分布式事务框架。

b,跨数据库序列问题

其实就是全局唯一id,我们会在另一篇文章中单独说说这个问题

c,数据查询问题

我们正常的水平拆分基本是按照id维度进行hash取模的,比如userId,orderId等,但是有些场景的查询条件不是根据userId或者orderId来进行查询的,该如何进行处理?

可以引入Elasticsearch,采用全订单字段索引,支持数据查询

未完待续。。。。。后续再慢慢补充!

个人对系统拆分的理解相关推荐

  1. 数据库拆分的理解和案例(详细版)

    数据库拆分的理解和案例 1 数据库拆分过程及挑战 1.1 垂直拆分 1.2 读写分离 1.3 分库分表 挑战1:基本的数据库增删改功能 挑战2:分布式id 挑战3:分布式事务 挑战4:动态扩容 2 主 ...

  2. 架构方案(17) 分布式架构系统拆分原则、缘由、以及实战如何拆分步骤

    分布式架构率先开始的就是应用工程拆分,如何拆分,什么情况拆分,拆分的原则是什么,能否实战详解拆分步骤?让我一一娓娓道来. 为什么需要应用拆分 我以淘宝技术架构演进为例,淘宝从一个大系统工程向分布式架构 ...

  3. 分布式架构之系统拆分

    系统拆分是单体程序向分布式系统演变的关键一步,也是很重要的一步,拆分的好坏直接关系到未来系统的扩展性.可维护性和可伸缩性等,拆分工作不难理解,但是如何正确拆分.有什么样的方法和原则能帮助我们拆分得到一 ...

  4. 分布式架构系统拆分原则、需求、微服务拆分步骤

    为什么需要应用拆分 我以淘宝技术架构演进为例,淘宝从一个大系统工程向分布式架构演变过程,你就能很清楚的知道为什么要需要进行应用拆分. 1 人员的角度 维护一个代名工程Denali的百万级代码怪兽(虽然 ...

  5. 分布式面试 - 为什么要进行系统拆分?

    分布式面试 - 为什么要进行系统拆分? 面试题 为什么要进行系统拆分?如何进行系统拆分?拆分后不用 dubbo 可以吗? 面试官心理分析 从这个问题开始就进行分布式系统环节了,现在出去面试分布式都成标 ...

  6. 为什么要进行系统拆分?

    面试题 为什么要进行系统拆分?如何进行系统拆分?拆分后不用 dubbo 可以吗? 面试官心理分析 从这个问题开始就进行分布式系统环节了,现在出去面试分布式都成标配了,没有哪个公司不问问你分布式的事儿. ...

  7. 苏宁11.11:系统拆分的一些经验谈

    "平京战役"一发布使本来就热闹的电商促销大战呛出了火药味,也为双11的大促增添了许多谈资,更让消费者享受到实实在在的优惠.而在技术上这种竞争则温和许多. 技术上的压力来源于业务的需 ...

  8. 秦小明 金融思维笔记 第三讲 财务分析1 商业分析的演绎框架amp;利润表的系统拆分

    这套课程对于赚到钱的程序员如何让钱生钱,保持财富非常重要,也对于做互联网金融行业的程序员非常重要. 有兴趣听课程的可以联系我的公众号:湾区人工智能 回复:秦小明 秦小明 第一讲 宏观和微观经济 htt ...

  9. php 系统平均负载,理解 Linux 的平均负载和性能监控

    <理解 Linux 的平均负载和性能监控>要点: 本文介绍了理解 Linux 的平均负载和性能监控,希望对您有用.如果有疑问,可以联系我们. 在本文中,我们将解释 Linux 系统中最症结 ...

最新文章

  1. 学习Java需要达到的25个目标
  2. mysql druid 多数据源_SpringBoot使用阿里数据库连接池Druid以及多数据源配置
  3. java 斗地主桌号_求用java编写的斗地主程序就,要求可以在局域网内实现两桌以上同时玩。...
  4. bartlett方差齐性检验_基于R实现统计中的检验方法方差分析
  5. docker问题备忘:“rpc error: code = 2 desc = containerd: container not found“
  6. boost::mp11::mp_eval_or相关用法的测试程序
  7. 能力清单:2020年SAAS的思考框架
  8. 卡尔曼滤波原理(2)
  9. java foxpro_java解析FoxPro DBF数据文件
  10. 买的首套房开发商指定的银行是5.88的利率,朋友都说利率有点高,怎样才能省点钱呢?
  11. MFC多国语言——资源副本
  12. python画图如何调整图例位置_Python——legend()图例位置调整
  13. java之Junit
  14. Meshing Tutorials(网格划分教程)
  15. ubuntu重装显卡驱动
  16. Windows下安装X710网卡驱动
  17. jQuery实现选择“学科门类”、“学科大类(一级学科)”、“专业”(二级学科)实现三级联动
  18. 作用域 (局部作用域和全局作用域) 详细介绍
  19. 香港服务器怎么加速?
  20. 模拟角频率和数字角频率区别

热门文章

  1. 一个计算机爱好者的不完整回忆(十)插播游戏
  2. 阿里飞冰的介绍以及使用
  3. 博达交换机S2528PB常用配置命令
  4. 获奖结果公布|2020腾讯犀牛鸟云开发校园技术布道师养成计划
  5. Error 1924.Could not update environment variable FNL_LICENSE_NUMBER.  Verify that you have sufficien
  6. sketch清理缓存文件,sketch清理运行内存工具
  7. ibm aix_IBM AIX SAN Volume Controller更新和迁移
  8. python实现小游戏-猜年龄
  9. 评联想收购IBM PC
  10. 输入法遮挡EditText输入框的问题