大家好,这里记录,我每周读到的技术书籍、专栏、文章以及遇到的工作上的技术经历的思考,不见得都对,但开始思考总是好的。
1、
软件进化是指在软件工程中,软件在第一次开发后,不断被更新、改进的过程。
这种进化是演进出来的的,而演进又是一种过程。
在该过程中,商用计算机程序需要随着时间的推移不断进行更新,维护和改进,以使其保持可行的产品。在这方面,软件的发展是由外部业务和消费者需求驱动的,这些需求随着其他软件和技术的发展而变化。
我们用《演进式架构》这本书里面的两幅图来看下这种变化。
图1
图1是早期我们做BS系统的一般常用结构,这个早期有多早呢,比如在2005年前后的样子,这样的结构应对当时的业务形态的需求,就已经足够了。
图2
图2是后期我们再进行BS系统开发的时候的大体结构的样子,这个后期是什么时间,大约2015年前后。
从图1到图2这十年间发生了什么。
发生了很多事情,JDK升级了6、7个版本,Spring框架被继承涌现出了SpringBoot,各种数据库、消息队列等中间件技术层出不穷,微服务、DDD已成为日常,还有很多,很多。
他们都是为了一个最终目标:让技术框架能够服务不断变化且复杂的需求,让业务研发人员能够更专注到业务开发中去。
雷曼的软件进化定律:在最初的设计中充分考虑未来的变化,避免因为频繁变化导致软件复杂度的增加和质量的下降。
我们始终追求让系统保持小而美的状态,但是大多数成功的系统都难以做到这一点。雷曼软件进化定律,特别是持续增长法则,反映了随着系统的使用、不断涌现出新需求和机会,给软件能力扩展带来了巨大的压力。为了应对这种日益增加的复杂性,并且从中受益,需要极大地提升双模设计领域的重要性:系统设计及创建和迭代这些系统的组织设计。
关于如何做好系统设计,这方面的系统架构资料和书籍已经非常多了,在往期的技术思考系列中我们也谈到过很多种方法。
那么,关于如何做好组织设计,大家可以看下《高效能团队模式》这本书。书中介绍了四种团队模式,流动式团队、复杂子系统团队、平台团队和赋能团队。
2、
构建和运行这些高度复杂、相互连接的软件系统是一种团队活动,需要不同平台和技术领域的人才共同努力。
团队与团队之间有三种依赖关系,知识依赖,任务依赖和资源依赖。
业务研发团队使用了共享技术部的基础设施,比如线上代码评审工具系统,往往需要这些基础设施部门的初次知识分享与培训。商品业务研发团队要依赖库存业务研发团队,进行商品库存数据的展示及处理。跨团队协作研发的时候还可能依赖人员资源,也可能依赖机器资源。
团队与团队之间有三种合作关系,协作,服务和促进。
协作:与另一个团队密切合作;两个团队拥有不同的技能且在一起工作;
服务:使用或提供某种服务,而尽量减少协作;团队之间一般通过API来交互;
促进:为提供或者寻求其他团队的帮助而清除障碍;
图3自《高效能团队模式》2.5章节
3、
什么时候分库。
当一个数据库的表的数量太多的时候,我们就开始分库了,也就是把这个库的一些表拆分出去。
其实这个有点类似我们做微服务,当我们拆分服务的时候有领域边界的问题。实际上,拆分库的时候也有类似的概念,比如我们把用户相关、个人爱好相关、行为分析相关的表放在一个库里面;把订单相关、商品相关的表放在另外一个库里面,我们也常常把这种分库的方法称为垂直分库。
有有时候垂直分库是跟拆分微服务相向而行或者说是配套进行的,垂直拆分的关注点在业务领域。
图4垂直分库
另外,还有一种拆分方法叫做水平拆分,如果说垂直拆分关注点在业务领域上,那么水平拆分的关注点则是在数据规则上。比如把单表 1 亿的数据按 Hash 取模拆分到 10 个相同结构的表中,每个表 1 千万的数据,这就是一种按照一定规则来拆分。
图5水平分库
那么,回答刚才的问题,多少算多,就是观察我们的业务情况,如果也遇到类似微服务那种边界混合,需要割裂开来进行维护的时候,就是多到了一个量级。
还有一种情况,当数据库面临写压力到瓶颈的时候,也是到了拆库的时机了。
单一服务中心的数据库的访问压力随着业务的增长,某一天势必会达到单机数据库的承载上限。比如,以MySQL数据库为例,出现了‘too many connections’这样的错误,当然,我们也会提前识别这种错误,是否是因为数据库的配置不合理导致。
4、
什么时候分表。
也是达到了一个多的程度,才会决定分表,什么多呢。
比如字段多,一张表的字段数多达100以上(有一个不成文的规定,一般不超过50个字段,常被写入数据库规范中)。
还有,数据字段内容较大、长度较长,遇见这样的情况也要拆分到扩展表里面去。这种分法,我们常常称为垂直拆分。
再有一种多,就是数据行数多,比如,还是以MySQL数据库为例,我们在 InnoDB 存储引擎下创建的索引都是基于 B+ 树实现的,那么当我们查询数据的时候的I/O次数一定程度上取决于这棵树的高度的,随着 B+ 树的树高增高,I/O 次数增加,查询性能也就越差。
以上,举例的这样的情况,都需要考虑分表。
按照一个数据库读写的角度来分类,我们一共可以分为四类,分别是,写多读多,写少读少,写多读少,写少读多。写多读多和写多读少,就直接分库分表了,减轻主库的压力;写少读少,保持原先不变,适当优化大查询,慢sql语句等,写少读多,就采取读写分离机制;
5、
再总结一下分库分表的时机。
分库使用时机:
1、业务不复杂,但整体数据量已影响了数据库的性能。
2、业务复杂,需要分模块由不同开发团队负责开发,这个时候使用分库可以减少团队间交流。
分表时机:
1、单个表中的字段数过多,维护成本高,另外影响查询性能。
2、单个数据表数据量太大,拖慢了SQL操作性能。
注:
数据库中表的数据是以行的形式存储在数据页上的,MySQL中innodb的页块大小默认是16k,如果我们的表的字段数越多,那么,一个数据页上可以存储的行数就少,以全表扫描为例,扫一个页用一次IO,当表的字段数较少的时候,可能取2个页就可以把整个表的数据读完了,但是,如果我们的字段数过多,可能就需要5个页才能读完。
6、
留两个个问题:
问题1:分库分表后必然会遇见运营人员实时报表查询的问题。单库时,查询实现起来比较简单。分库之后,查询就变得复杂了,请问有没有比较好的解决思路呢?
问题2:按照ID来分库的话会遇到访问热点问题,有没有好的解决思路呢?
7、
主从延迟怎么办。
主从复制延迟不可避免,还是以MySQL数据库为例,它的主从复制原理,是通过binlog机制实现的,主要有三步。
1、主库 A 执行完成一个事务,写入 binlog,我们把这个时刻记为 T1;
2、之后传给备库 B,我们把备库 B 接收完这个 binlog 的时刻记为 T2;
3、备库 B 执行完成这个事务,我们把这个时刻记为 T3;
那么这个过程所花费的时间为:T3-T1。
这个环境里面,如果从库的机器性能比主库差,或者从库的压力大,有大量的查询请求到从库上,或者是主库上执行了一个大事务的操作,或者是主从之间网络有延时,这些都可以造成延迟。
所以,我们说主从复制延迟不可避免。
如何应对这种延迟呢。大致有如下三种方法。
1、 写操作后的读操作指定发给数据库主服务器;
2、读从机失败后再读一次主机;
3、 关键业务读写操作全部指向主机,非关键业务采用读写分离;
8、
本周读到一句话,分享给你。
生活中其实很少有一剑封喉的所谓“绝招”,真正的高手,是把人人都会的简单招数练到极致。
恭喜你,又完成一次思考。
参考资料:
https://zhuanlan.zhihu.com/p/84224499 MySQL分库分表方案
https://time.geekbang.org/column/article/76795 林晓斌.MySQL实战45讲
https://time.geekbang.org/column/article/8269 李云华.从0开始学架构
https://www.netinbag.com/cn/internet/what-is-software-evolution.html