1. 互联网分层架构的本质:
1).互联网分层架构的本质,是数据的移动
2).互联网分层架构中,数据的传输格式(协议)与数据在各层次的形态很重要
3).互联网分层架构演进的核心原则与方法:封装与复用
a.让上游更高效的获取与处理数据,复用
b.让下游能屏蔽数据的获取细节,封装
2. 互联网分层架构是一个很有意思的问题,服务化的引入,并不是越早越好:
1).请求处理时间可能会增加
2).运维可能会更加复杂
3).定位问题可能会更加麻烦
3. 当业务越来越复杂,垂直拆分的系统越来越多,基础数据服务越来越多,底层数据获取复杂性成为通用痛点的时候,就应该抽象出通用业务服务,简化数据获取过程,提高数据获取效率,向上游屏蔽底层的复杂性。
这样的好处是:
复杂的从基础服务获取数据代码,只有在通用业务service处写了一次,没有代码拷贝
底层基础数据service接口发生变化,只有通用业务service一处需要升级修改
如果有bug,不管是底层基础数据service的bug,还是通用业务service的bug,都只有一处需要升级修改
业务web-server获取数据更便捷,获取所有数据,只需一个RPC接口调用
4. 为啥要前后端分离
1).一点点展现的改动,需要Java工程师们重新编译,打包,上线,重启tomcat,效率极低
2).原先Java工程师负责所有MVC的研发工作,现在分为Java和FE两块,需要等前端和后端都完成研发,才能一起调试整体效果,不仅增加了沟通成本,任何一块出问题,都可能导致项目延期
当业务越来越复杂,端上的产品越来越多,展现层的变化越来越快越来越多,站点层存在大量代码拷贝,数据获取复杂性成为通用痛点的时候,就应该进行前后端分离分层抽象,简化数据获取过程,提高数据获取效率,向上游屏蔽底层的复杂性。
这样的好处是:
复杂的业务逻辑与数据生成,只有在站点数据层处写了一次,没有代码拷贝
底层service接口发生变化,只有站点数据层一处需要升级修改
底层service如果有bug,只有站点数据层一处需要升级修改
站点展现层可以根据产品的不同形态,传入不同的参数,调用不同的站点数据层接口
除此之外:
产品追求绚丽的效果,并对设备兼容性要求高,不再困扰Java工程师,由更专业的FE对接
一点点展现的改动,不再需要Java工程师们重新编译,打包,上线,重启tomcat
约定好json接口后,Java和FE分开开发,FE可以用mock的接口自测,不再等待一起联调
5. 分库需求
高并发大流量的互联网架构,一般通过服务层来访问数据库,随着数据量的增大,数据库需要进行水平切分,分库后将数据分布到不同的数据库实例(甚至物理机器)上,以达到降低数据量,增加实例数的扩容目的。
一旦涉及分库,逃不开“分库依据”patition key的概念,使用哪一个字段来水平切分数据库呢:大部分的业务场景,会使用业务主键id。
确定了分库依据patition key后,接下来要确定的是分库算法:大部分的业务场景,会使用业务主键id取模的算法来分库,这样即能够保证每个库的数据分布是均匀的,又能够保证每个库的请求分布是均匀的,
实在是简单实现负载均衡的好方法,此法在互联网架构中应颇多。
6. 究竟为什么要引入数据库中间件
1). partition key上的单行查询
典型场景:通过uid查询user
场景特点:通过patition key查询;每次只返回一行记录
解决方案:base-service层通过patition key来进行库路由
2).非patition key上的单行查询
典型场景:通过login_name查询user
场景特点:通过非patition key查询;每次只返回一行记录
解决方案1:base-service层访问所有库
解决方案2:base-service先查mapping库,再通过patition key路由
a.新建mapping库,记录login_name到uid的映射关系
b.当有非 patition key的查询时,先通过login_name查询uid
c.再通过patition key进行路由,最终返回一条记录
解决方案3:基因法
3).patition key上的批量查询:
典型场景:用户列表uid上的IN查询
场景特点:通过patition key查询;每次返回多行记录
解决方案1:base-service层访问所有库,结果集到base-service合并
解决方案2:base-service分析路由规则,按需访问
a.base-service根据路由规则分析,判断出有些数据落在库1,有些数据落在库2
b.base-service按需访问相关库,而不是访问全库
c.base-service合并结果集,返回列表数据
4).非patition key上的跨库分页需求
关于分库后,跨库分页的查询需求,详见《业界难题,跨库分页的四种方案》。
5).本文写到这里,上述一、二、三、四、五其实都不是重点,base-service层通过各种各样的奇技淫巧,能够解决db水平切分后的数据访问问题,只不过:
base-service层的复杂度提高了; 数据的获取效率降低了
底层的复杂性会扩散到各个base-service,所有的base-service都要关注:
patition key路由;非patition key查询,先mapping,再路由;先全库,再合并;先分析,再按需路由;跨库分页处理…
这个架构图是不是看上去很别扭?如何让数据的获取更加高效快捷呢?
数据库中间件的引入,势在必行。
这是“基于服务端”的数据库中间件架构图:
base-service层,就像访问db一样,访问db-proxy,高效获取数据
所有底层的复杂性,都屏蔽在db-proxy这一层
这是“基于客户端”的数据库中间件架构图:
base-service层,通过db-proxy.jar,高效获取数据
所有底层的复杂性,都屏蔽在db-proxy.jar这一层
结论:
当数据库水平切分,base-service层获取db数据过于复杂,成为通用痛点的时候,就应该抽象出数据库中间件,简化数据获取过程,提高数据获取效率,向上游屏蔽底层的复杂性。