当数据库扼住系统性能咽喉?
大部分的软件架构、组件或解决方案,都是在解决一些问题的同时,会带来另外的问题。
数据库的分库分表,又可以分为垂直拆分和水平拆分(可能大家常说的分库分表主要指的是后者):
垂直拆分:这是一种比较常见的数据库设计方法,就是把一个字段比较多的大表,拆分成多个小表,特别是在现在分布式、微服务的架构下,可以把各个小表按照业务模型,划分到不同的数据库中,这样就可以利用多台数据库服务器的性能;但当被拆分出来小表的数据量不断增长,到了一个极限的时候,还是需要考虑水平拆分。
水平拆分:将表中的数据,按照一定的规则分布到不同的数据库中,比如对主键进行Hash和取模操作后,按照结果把数据路由到对应的数据库上;水平分库分表,可以降低每张表的数据量,这也是现在大部分公司所使用的方法。
数据库扩容问题上文中说到,水平拆分常用的手段是对主键进行Hash和取模操作后,按照结果把数据路由到对应的数据库上;但如果被拆分的子表,数据量也达到极限值以后,就要面对数据库扩容的问题,比如开始规划分成8个库,现在要扩到16个库;
路由规则发生变化:hash(id)%8 变成了 hash(id)%16,那么历史数据也就要面临迁移的问题;这种情况,要么做数据迁移,要么增加分表算法的复杂性,让算法可以兼容增加分表前后的数据路由。
复杂查询、关联查询、order by、group by等的问题在单库时代,复杂的关联查询是很容易实现的,但是数据库被拆分后,数据被保存在了不同的数据库服务器上,那么夸库的join就成了很大的问题。通常解决方案有:
如果是垂直拆分,那么可以考虑做一定程度的字段冗余,避免跨表关联;或者可以做数据同步,把需要的表同步到同一个库中,进行表关联;
代码层面组装,也就是把两边的数据都拿出来,然后在代码里面关联组装;或者先获取主表数据,再把其余字段补齐;但是从实际情况来看,这个方案在大多数场景下,实现起来都比较困难;
现在一个比较主流的做法,是引入ES或ES+HBase或solr+HBase,把部分字段的全量数据保存在同一个地方。
ID问题在水平拆分的场景下,一单一张表被拆分成多张表部署在多个数据库中,那么就不能使用数据库自身的主键生成机制了;这时候就需要由我们自己来考虑主键生成策略:
主键生成中心:可以利用数据库、Redis、MongoDB、zookeeper等组件实现,需要生产主键的时候,调用主键生成中心的接口;缺点也很明显,增加了网络开销,并且主键生成中心如果发生问题,后果会很严重。
UUID:本地生成,不需要第三方组件,生成比较简单,性能好;不过缺点也不少,长度长,不利于存储,并且没有排序,是个字符串,不利于查询。
一些唯一性ID的生成算法:比如Snowflake、UidGenerator、Leaf等等。
事务问题单库的时候,解决事务问题很简单,但是现在要保证跨库的事务问题,需要额外的成本;
这种场景下(性能要求高,一致性要求不是那么的高),大部分公司会放弃事务的【实时】一致性,只要在一定的时间内,事务【最终】一致即可。
我将持续分享Java开发、架构设计、程序员职业发展等方面的见解,希望能得到你的关注。Copyright © 广州京杭网络科技有限公司 2005-2024 版权所有 粤ICP备16019765号
广州京杭网络科技有限公司 版权所有