前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >分库分表下,多维度查询问题

分库分表下,多维度查询问题

原创
作者头像
Joseph_青椒
发布2023-08-04 20:10:35
5351
发布2023-08-04 20:10:35
举报
文章被收录于专栏:java_josephjava_joseph

这篇来深入理解一下,分库分表下:多维度查询问题如何解决

这个问题,可能好多人连问题都理解不了,现在来看一下

注意这篇文章要结合上一篇文章,数据迁移问题分库分表下,扩容数据免迁移方案-腾讯云开发者社区-腾讯云 (tencent.com)

问题抛出

读懂我上一篇文章的伙伴,应该知道,分库分表,短链是按照拼装库表位来实现库表的路由的,用户想通过短链跳转长链,要查库,找到url,查库的时候,如何定位到哪个库,就是按照短链码的库表位,进行路由到对应的库表,创建短链的时候,是商家创建的,

商家创建短链,要先创建对应的groupId,然后再创建短链,但是短链的入库时库表位,那么商家如何去查询?

如果直接通过accountNo和groupId去查询的话,就会有很大的问题,sql执行的时候,并不知的对应accountNo和gruopId对应的行在哪个库,哪个表,这样就导致了全库表路由,这是很影响性能的。

添加描述

这是短链多维度查询的问题,

我们再看下其他场景

添加描述

同样,如何做???分片键只有userId,招聘者,如何去查看自己面试过的人员?这样搞的话,只能去全表路由。

等等场景,比如常见的电商,

分库分表的话,user_id作为订单分库分表的分片键,那么商家就满足不了了。

业界常用解决方案

字段解析配置

这个方案就挺适合短链的业务,这里先说一下短链码的生成组件

这里的param,是这样的,1232413412&xxx.net这样的格式,就是雪花id&原始url,

为何这样设计,目的是为了保证通过url能生成不同的短链码,进行数据的统计分析,

比如bai.com要生成1w条短链码,就通过拼装雪花id来完成,但是有发生冲突的可能,那就如何冲突,就让雪花id自增,再生成短链码,直到不冲突为止、

然后,拼装路由key,也就是库表位,要保证一致,

这里说的是意思是,123&baidu.com 124&baidu.com

123&baidu.com生成的短链码,必须是唯一的,randomDBprefix,对这个短链码来说,一定要唯一,不然就会出现,短链码与url=n:m

我们的需求是n:1

所以这里的随机前缀后缀不能和上篇博客那样,用随机函数

可以通过murmurhash出来的短链码hash值取模操作来保证

好,现在来说一下,字段解析配置,怎么玩儿

能够满足商家端的查询需求

这里做的就是新生成一个表

通过accountNO进行hash,得到一个值,库表位确定下来,对于我这个账号,生成的短链,能保证在库中

比如a,b这个例子,商家1,生成的短链肯定在这两个库,4个表就行了,也就是查询的时候,只需要在这4个库笛卡尔积查询,不需要全库表笛卡尔积路由了

那么 就是在拼装库表位的前缀这里,固定下来,生成的组件,就需要在这个表里找到对应的库表位置,再生成短链码。而短链码的解析和入库时候分片键是不变的

商家想查询自己某个分组下的短链的时候,就需要select code from shrot_link where accountNO=xxx and groupId = XXX SQL1

假设字段解析配置出来的是test表,先查询select 库,表, from test where 账号= hash(accountNO)

我想问的是,找出来具体的库表之后,怎么结合SQL1路由特定的库表,而不是全表路由?

这里需要和大家探讨,一起想出方案,但是基本上都是冗余双写的方案。

Nosql方案

这个方案就是用canl-server监听数据库的变动,kafka缓冲,冗余到es中,这种方案架构复杂,切多了许多链路,链路长,性能就会受到影响。

冗余双写方案

这是个空间还时间的方案,就是将数据再冗余一份到数据库中,当然可以冗余部分字段,和全部字段,这个看业务需求即可

代码语言:javascript
复制
##---------- 组+短链码mapping表,策略:分库+分表--------------
# 先进行水平分库,然后再水平分表, 水平分库策略,行表达式分片
spring.shardingsphere.sharding.tables.group_code_mapping.database-strategy.inline.sharding-column=account_no
spring.shardingsphere.sharding.tables.group_code_mapping.database-strategy.inline.algorithm-expression=ds$->{account_no % 2}
# 分表策略+行表达式分片
spring.shardingsphere.sharding.tables.group_code_mapping.actual-data-nodes=ds$->{0..1}.group_code_mapping_$->{0..1}
spring.shardingsphere.sharding.tables.group_code_mapping.table-strategy.inline.sharding-column=group_id
spring.shardingsphere.sharding.tables.group_code_mapping.table-strategy.inline.algorithm-expression=group_code_mapping_$->{group_id % 2}

这个方案就很简单了,就是行表达式,并且也没有免迁移方案

当然冗余双写会有一些列问题,

冗余双写也是页内最常用的方案,我也会以这个为主去讲解,

冗余双写方案的问题

冗余双写是空间换时间的操作,双写,要涉及不同的库的插入,这就涉及到了分布式事物问题,

分布式事物问题的解决

这个就涉及到强一致弱一致了,强一致就容易了,直接引入seata框架,在生成短链接口上,增加@globalTracational就可以了

但是这个性能是很差劲的,都涉及分库分表了,数据量这么大,当然不能用seata强一致

那就采用,最终一致的方案

采用MQ的方案,只需要保证消息的投递,以及幂等性的处理

这种方案,就是通过mq来保证C端B端的处理,都处理完即可,弱一致,消费者消费失败,需要额外的接口来回滚之前插入的,(这里指的式B端写入,但是C端写入失败,就需要额外的接口回滚B端的写入)

这里我们采用rabbitMq这个消息队列

这个问题其实并无影响,只要保证消息队列的稳定,业务不影响是不会有问题的

我们只需要设置容错机制,通过republishMessageReverer实现异常交换机,通知业务人员处理就可以了

好,这里分布式问题就解决了,但是诞生了一个新的问题,

并发下,短链码并发冲突时,C端,B端用户不一致问题,

现在看一个场景

用户1,生成短链码,AABBCC

用户2,生成短链码,AABBCC

用户1,C端插入短链库a中,B端还未插入

用户2,C端还未插入,B端插入了端口库n中

此时,再进行插入的话,比如用户1,想插入B库,发现以及被用户2插入了,用户2,想插入A库,发现已经被用户1插入了

这个出现了 ,短链码并发冲突时,导致的C端B端不一致问题,同一个短链码在B端C端有不同的用户,且每个用户都不能正常使用

此时两个用户都会失败,

这就涉及到了,加锁

但是加锁在这里是有说法的,

这个要看生成短链的时机是在mq的上游还是下游,也就是加到mq的生产者端还是消费者端

生产者端

这里的话,要在生成者端加锁,锁粒度是短链码,去数据库中判断存在不存在,不存在的话,再去创建短链,然后发消息,让B端和C端的消费者都插入到数据库,再释放锁,本来就是弱一致,在这里加锁的话,还不如seata框架的强一致呢,对用户来说很不友好

消费者端

重申一下,锁粒度是短链码,加锁的目的是为了防止短链码冲突的情况,accountNO是不一样的

生产者端,是一个线程,只需要保证这个线程拿到锁就好了

但是放到消费者端,C端消费者,和B端消费者,就要涉及到锁重入的问题了,因为对于一个短链码加锁,C端,B端都应该可以去拿到这个锁,而不是只能拿到一个,这就是可重入锁的逻辑。

所以我们要设计可重入锁,锁粒度是短链码,但是要对accountNO一致的线程,达到可重入

代码语言:javascript
复制
//加锁
//key1是短链码,ARGV[1]是accountNo,ARGV[2]是过期时间
String script = "if redis.call('EXISTS',KEYS[1])==0 then redis.call('set',KEYS[1],ARGV[1]); redis.call('expire',KEYS[1],ARGV[2]); return 1;" +
        " elseif redis.call('get',KEYS[1]) == ARGV[1] then return 2;" +
        " else return 0; end;";


Long result = redisTemplate.execute(new
        DefaultRedisScript<>(script, Long.class), Arrays.asList(shortLinkCode), accountNo,100);

总而言之,冗余双写架构,需要考虑分布式事物问题,采用mq的方案,要考虑短链码并发冲突下,B端C端不一致的问题

这里的解决,需要加锁,分生产者端和消费者端,这里的问题考虑到用户的体验,下沉到消费者端,加锁就要涉及可重入锁。

关于分布式锁的详细讲解,在这篇文章我有提到分布式锁-腾讯云开发者社区-腾讯云 (tencent.com)

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题抛出
  • 业界常用解决方案
    • 字段解析配置
      • Nosql方案
        • 冗余双写方案
        • 冗余双写方案的问题
          • 分布式事物问题的解决
          相关产品与服务
          云数据库 MySQL
          腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档