又有报错了
收到用户反馈:
这个列表是坏了嘛
我今天给一下午都点不开
在本地复现了下:也打不开. 看到有报错:
{
"datas": null,
"message": "request timeout",
"path": "/v1/waitInvoicePage",
"status": 500,
"timestamp": 1659010004445
}
# 查询原因
去日志中查了下,看是查询TableStore报错了:
content:2022-07-28 20:06:44[60a9e7e165900997904910398fe22 ][http-nio-8080-exec-5] ERROR c.z.common.dto.ApiResponse - failure :{"datas":null,"message":"request timeout","path":"/customerStatementDetail/v1/waitInvoicePage","status":500,"timestamp":1659010004445}
Caused by: com.alicloud.openservices.tablestore.TableStoreException: request timeout
com.alicloud.openservices.tablestore.TableStoreException:request timeout
TableStore是
根据日志中的traceId去全链路跟踪平台上查看下整体情况:
异常:com.alicloud.openservices.tablestore.TableStoreException:request timeout
at com.alicloud.openservices.tablestore.core.CallbackImpledFuture.getResultWithoutLock(CallbackImpledFuture.java:107)
at com.alicloud.openservices.tablestore.core.CallbackImpledFuture.get(CallbackImpledFuture.java:89)
at com.alicloud.openservices.tablestore.SyncClient.waitForFuture(SyncClient.java:502)
at com.alicloud.openservices.tablestore.SyncClient.search(SyncClient.java:467)
就是查tableStore失败了,看下TableStore的查询参数. 因为查询参数太长,日志平台已经展示不了,直接进行了截断:
{
"dataSize": 26,
"type": "STRING",
"value": "2103241740451151094605"
},
{"dataSize": 26, "type": "STRING", "value": "3241741331950505477"
},
{
"dataSize": 26, "type": "STRING", "valu收起
看日志中的查询参数并不多,但在对TableStore进行查询时,却传了这么多参数,应该是一些别的业务逻辑。
梳理下这个接口的业务逻辑,这个业务域主要是一个Header表和一个Item表。一条完整的业务数据至少包含一条Header表中的数据和一条Item表中的数据。
这个接口提供的服务是返回数据需要满足以下特征:
- Header表中的状态是完成
- Item表中的记录要满足指定的筛选条件
上面的需求翻译成sql就是这样:
select header.filed_name1,item.filed_name2,item.filed_name3 from header ,item
where header.id=item.headerId and header.status='Finished'
and item.filed_name_x='指定的筛选条件'
and item.filed_name_y='指定的筛选条件'
。。。其它筛选条件 。。。
查到此处,是不是有小朋友有疑问了,看就是一个sql解决问题的事,为什么报错的是TableStore,而不是MySql?这是个好问题!
如果header表和item表中数据量在10万以下,直接用上面的SQL就可以把这个需求摆平了。
数据量是百万级时,估计就会慢得像蜗牛。
对数据库有了解的同学,肯定会说sql查询慢,可以通过加索引来解决!
是的,sql查询慢时,要考虑加索引。 如果查询条件有19个,并且都是选填项,这个索引怎么加。
要想解决速度问题,至少要加15+个索引。一些区分度低的字段,就不适合加索引
此处需要配上不能超过9个索引
这也是没有查MySql,而是查询存在TableStore上的数据。
在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。
索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。
ua环境和线上,这两个环境上的数据量级是不同的。uat的数据不到2万,线上的数据是百万级,接近千万。
这也是为什么测试环境是ok的,到了线上,功能就坏了的原因。
业务场景&解决的办法
经过上面的分析,如果从数据角度拆解这个需求,就是一个两个张表,这两张表中是1:n的关系。
这个需求中的过滤条件,Header表中有,Item表中也有。
当前的情况
- 把满足条件的Header表中的Id出来, headder_id_list
- 查Item表中的数据,where item.header_id in ($headder_id_list)
问题:headder_id_list 在线上的数量是50万+,并且随着业务开展还会持续增加,直接in这么多数据,tableStore也抗不住,直接尥蹶子了
com.alicloud.openservices.tablestore.TableStoreException:request timeout
解决方案
根据上面的分析,可以得到以下结论:
- 不能直接查mysql。
数据量目前是百万级,随着业务开展,数据量还会持续增加。- 两表关联查询查库,有性能问题
- 筛选条件多,19个。需要的索引多。索引会占用额外的空间,索引过多也会增加MySql性能优化器的负担,对查询速度有影响。
- 不能通过MySql+TableStore的方式来进行数据筛选。
MySql中Header表中的数据量在50万+,TableStore直接in这么数据,也搞不定
复盘下,到底是什么原因,导致这个功能坏了?
- 数据模型建错了?
为什么使用Header表和Item表两张表来承载这些业务数据?从业务上看有一个总的描述,譬如整体上这项事务有没有完成,就像金字塔原理中讲的结论先行、以上统下、归类分组。
如果是使用这种由多张能互相联接的二维行列表格组成的关系型数据库来承载这个1:n的业务概念,必然是一张Header表来承载总体描述,一张Item表来承诺1+条明细信息。 - 技术方案错了?
因为查询条件多且可以随意组合,使用MySql是不合适的,使用TableStore的这处技术中间件是对的。
那么,问题出在哪了?
消费数据的维度与持久化数据的维度不一致。
存数据时,是结论先行、以上统下。拆成Header表、Item表
消费数据时,是以局部看整体。Item上的数据体现了一个具体业务的整体情况。
这个不一致,引发这个奢求落地时的各种坑。
解决方案:
数据异构。建立一个Item维度的一个异构数据,或者理解为CQRS模型中的查询Model也可以。
这个异构数据包含了业务需求中涉及到的所有数据,数据查询都来查这个Model。
什么是异构?简单的说就是指一个整体中包含有不同的成分的特性,即这个整体由多个不同的成分构成。
什么是数据异构?按照不同查询维度建立表结构,这样就可以按照这种不同维度进行查询。数据异构有查询维度异构、聚合数据异构等。
大数据量的异构处理?
通过MQ机制接收数据变更,然后原子化存储到合适的存储引擎,如Redis、ES或持久化KV存储。数据闭环和数据异构其实是一个概念,目的都是实现数据的自我控制,当其它系统出问题时不影响自己的系统,或者自己出问题时不影响其它系统。一般通过消息队列来实现数据分发。
https://www.cnblogs.com/softidea/p/7306007.html
CQRS模式【命令查询的责任分离,Command Query Responsibility Segregation】:通过将数据存储和数据查询这两个关注点分离。通过分离读写操作,可以实现数据异构,进而提升查询性能。C(Command)端负责数据存储,Q(Query)端负责数据查询,Q端的数据通过C端产生的Event来同步。
优化后的效果
优化前: 报错或者耗时24s+
优化后:
没有报错了。最少耗时在33ms
小结
在数据量大、筛选条件多或访问量大时,使用数据异构是非常有效的。不过数据异构也增加了架构的复杂度,需要根据各自团队的技术积累,慎重权衡。异构时可以通过订阅MQ或者解析Binlog来实现业务数据存储和构建异构数据这件事的解耦。~~~~
相逢一笑泯恩仇。
政治是政治,私交是私交。
我可以与你的政治主张不同,但我可以跟你是好朋友。
逆旅行人:外任十多年,苏东坡吃尽了变法派的苦头。当司马光决定废除新法时,他又唱起了反调。
满肚子不合时宜,哈哈哈哈
九死南荒吾不恨,兹游奇绝冠平生
按不同的维度重新构建数据,可以提升大数据量、多筛选条件、高访问量场景下的查询数据的性能