TDengine 资深研发分享解决思路,长查询不再成为系统性能瓶颈!

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
云原生数据库 PolarDB PostgreSQL 版,企业版 4核16GB
推荐场景:
HTAP混合负载
简介: 本文探讨了如何应对和解决长查询问题,以提升 TDengine 在复杂查询场景下的表现。

长查询问题指的是在数据库写入和查询并存的日常应用场景中,存在处理数据量大且耗时很长的查询长时间占用系统资源,导致写入可能被阻塞的问题。有时,查询代码对于资源释放函数调用的遗忘也可能以长查询问题的形式表现出来。如何在数据写入不被阻塞同时,保证长查询的正确进行是一个具有挑战性的问题。


尽管在绝大多数时序数据使用场景下,用户不太可能遇到这个问题,但一旦出现,也会让人头疼不已。为了解决这一问题,TDengine 研发团队一直致力于不断优化系统,提高查询性能和响应速度。本文将深入剖析这一挑战,并探讨如何应对和解决长查询问题,以提升 TDengine 在复杂查询场景下的表现。


在分析长查询问题之前,我们首先需要为大家普及一下 TDengine 的写入/查询并发机制。

数据写入/读取机制

Vnode 是 TDengine 中存储和查询数据的基本单元,这里主要介绍 Vnode 的写入/读取及并发机制。

数据写入机制

  • 每个 Vnode 在创建时都会根据 DB 参数分配一定数量的内存
  • 这些内存在 Vnode 中被分为三个内存块
  • 每个 Vnode 只有一个线程写入
  • Vnode 在写入时,会从内存块的空闲列表 (free list) 中分配一个内存块(mem),供数据写入使用
  • 当内存块中数据写入超过一定量后,开始落盘(imem),同时分配一个新的内存块供数据写入使用
  • 当内存块全部用完,没有空闲内存块时,写入会被阻塞,一直等待有内存块释放出来

数据查询机制

  • 查询分多批次进行,每次返回部分数据,然后该查询等待下一次拉取数据的请求
  • 查询结果是内存(mem/imem)数据和硬盘数据合并的结果
  • 查询开始时,会先 take snapshot,引用(ref) mem/imem 以及硬盘文件
  • 查询结束时,unref mem/imem,如果内存块的引用计数变为 0,则内存块被回收到空闲链表中

长查询问题

时序数据绝大部分查询持续时间都比较短,如查询表/超级表的最后一条记录、做 count 或 sum 类的聚合查询等。这类查询持续时间较短,对于 mem/imem 的占用时间也较短,查询很快会释放 MemTable 供 Vnode 回收再次利用,从而不影响写入的进行。但是,如果存在一个持续时间很长的查询,如超过 1 小时或 1 天的查询,这时候就会出现此类问题。当然,只有一个长查询的情况下,问题也不大,因为 Vnode 的内存池默认被分成了 3 个内存块,一个长查询最多占用两个内存块,还剩余一个内存块可以用来进行持续写入。但是如果存在多个长查询,Vnode 中的所有内存块都可能被长时间占用,无法进行回收,从而导致写入停止的情况。


另外,如果查询部分的代码存在 BUG,忘记关闭查询句柄,也会导致 mem/imem 被长期占用,阻塞写入。这个问题在 TDengine 订阅和流计算功能中曾经就出现过。

长查询问题解决方案

我们需要一个方案,可以在长查询大量存在、或用户应用代码有问题没有及时关闭查询句柄、甚至产品代码有问题的情况下,也能达到既不阻塞写入且不让长查询失败。该方案如下:


  1. 查询在获取数据快照时,将查询句柄注册到它所占用的内存块上,同时注册一个 reseek 函数
  2. 当查询结束关闭句柄时,该查询将句柄从它所占用的所有内存块上注销
  3. 当写入发现没有可用内存块时,尝试回收已经 COMMIT 但是仍旧被查询占用的最老的内存块
  4. 写入线程回收内存块时,遍历所有注册在该内存块上的注册句柄,调用 reseek 函数
  5. reseek 函数会尝试锁查询句柄,如果锁句柄成功,则将查询句柄设置为 RESEEK 状态,同时将查询的状态保存下来并 untake snapshot,归还占用的所有内存块
  6. 查询线程在查询活跃周期开始时锁查询句柄,然后检查是否为 RESEEK 状态,如果为 RESEEK 状态,重新 take snapshot,并根据保存的查询状态,恢复各种查询变量,接着进行查询


从上述方案中我们可以看到:


  1. 写入在发现内存不足的条件下,可以主动回收非激活状态的查询占用的内存块,从而不会长时间阻塞写入
  2. 长查询在写入回收其所占用的内存块后,可以根据自己保存的查询状态,重新 take snapshot,继续查询,从而不会让长查询失败

答疑解惑

Q:如何解决死锁问题?

A:在写入回收内存块时,需要遍历查询句柄注册链表,因此需要对链表进行加锁操作,即需要锁定链表的锁。在调用 reseek 回调时,也需要锁定查询句柄的锁。这样就存在写入线程出现 lock(mutex_list)–>lock(mutex_qhandle) 的情况。而在查询结束时,需要将句柄从注册链表中移除,导致出现 lock(mutex_qhandle)–>lock(mutex_list) 的情况。如果两个线程不按照相同的顺序对两个锁进行加锁,就会产生死锁的问题。

为了解决这个问题,可以采用以下优化方案:

  1. 使用trylock替代lock:对于写入回收调用 reseek 函数的场景,可以尝试使用 trylock 而不是直接使用 lock 来获取查询句柄的控制权。通过 trylock 尝试获取锁,可以避免线程因等待锁而被阻塞,从而减少死锁的风险。
  2. 多次尝试机制:在使用 trylock 的基础上,可以结合多次尝试的机制。如果一次 trylock 未成功获取锁,可以进行多次尝试,直至成功获取锁或达到尝试次数上限为止。这样能够增加获取锁的机会,降低死锁风险。


Q:长查询会不会一直被写入 reseek,从而导致查询一直恢复?

A:不会存在这种情况。查询在每次打开句柄时,会给一个版本号,这个版本号是已经写入 Vnode 中的最新数据的版本号,查询只能看到版本号小于等于该版本号的数据。当 take snapshot 时,会根据 mem/imem 中的数据是否被查询版本号覆盖而决定 mem/imem 是否被该查询引用。随着数据的写入,新的 mem/imem 中的数据肯定都大于该查询创建时给的版本号,因此,新的 mem/imem 不会被长查询引用。这样一来,一个长查询最多会被写入 RESEEK 两次。

以上就是 TDengine 资深研发人员对解决长查询问题进行的深入探讨。如果你还有关于查询的更多问题想要讨论或交流,可以在下方留言区进行相关评论,静待回复即可~

目录
相关文章
|
1月前
|
存储 Cloud Native 物联网
数据库技术前沿探索:架构、优化与行业实践
一、引言 在信息化和数字化的浪潮中,数据库技术作为企业核心竞争力的关键要素,其重要性不言而喻
|
1月前
|
消息中间件 Kubernetes Kafka
AutoMQ 自动化持续测试平台技术内幕
Marathon 是一个针对流系统 AutoMQ 的自动化持续测试平台,旨在在模拟生产环境和各种故障场景中验证 SLA 的可靠性。设计原则包括易拓展、可观测和低成本。平台采用分布式架构,Controller 负责资源管理和任务编排,动态调整 Worker 数量和配置,而 Worker 是无状态的,用于生成负载和上报数据。系统基于 K8S,利用服务发现、事件总线和 Spot 实例降低成本并提高弹性。测试场景以代码形式描述,支持不同流量模型和断言,提供丰富的可观测性和告警功能。未来,Marathon 有望泛化为适用于各种分布式系统的测试平台。
31 0
AutoMQ 自动化持续测试平台技术内幕
|
2月前
|
存储 SQL 关系型数据库
掌握高性能SQL的34个秘诀🚀多维度优化与全方位指南
掌握高性能SQL的34个秘诀🚀多维度优化与全方位指南
|
10月前
|
存储 数据处理 数据库
TDengine 用户案例合集 | 智能环保项目的时序数据处理难点与优化实践
本篇文章汇总了三个典型的智能环保项目的数据架构升级实践,给有需要的企业参考。
181 1
|
2月前
|
存储 JSON Java
线上业务优化之案例实战
线上业务优化之案例实战
|
9月前
|
SQL 分布式计算 资源调度
大数据线上问题排查系列 - 同样的HQL,在CDH与TDH平台执行效率差异巨大的根本原因与业务侧应对方案
大数据线上问题排查系列 - 同样的HQL,在CDH与TDH平台执行效率差异巨大的根本原因与业务侧应对方案
|
存储 运维 负载均衡
【分布式技术专题】「架构实践于案例分析」总结和盘点目前常用分布式技术特别及问题分析
【分布式技术专题】「架构实践于案例分析」总结和盘点目前常用分布式技术特别及问题分析
274 0
【分布式技术专题】「架构实践于案例分析」总结和盘点目前常用分布式技术特别及问题分析
|
SQL 存储 Oracle
杨传辉:深挖 OceanBase 背后的技术逻辑,助力数据库核心系统升级
数据库是信息社会的基础设施,通过开放开源助力数据库技术的快速发展,构建新一代数据基础设施是大势所趋!在“2021云栖大会 . OceanBase 原生分布式数据库论坛” 上,OceanBase CTO 杨传辉为大家带来了一场主题为《OceanBase 一体化架构助力核心系统升级》的演讲。
213 0
杨传辉:深挖 OceanBase 背后的技术逻辑,助力数据库核心系统升级
|
测试技术 监控 弹性计算
阿里巴巴在应用性能测试场景设计和实现上的实践
提升性能前,先测试摸个底,找到性能瓶颈。 测试前,先设计好应用性能的测试场景,并实现它。 本文是《Performance Test Together》(简称PTT)系列专题分享的第5期,该专题将从性能压测的设计、实现、执行、监控、问题定位和分析、应用场景等多个纬度对性能压测的全过程进行拆解,以帮助大家构建完整的性能压测的理论体系,并提供有例可依的实战。
9080 1
|
SQL 监控 关系型数据库
历年双11实战经历者:我们是如何做数据库性能优化及运维-CloudDBA和天象
8月24日阿里云数据库技术峰会上,阿里云高级DBA专家玄惭带来面对超大规模的数据库集群,尤其是在每年像双11这样重大促销活动中,阿里云是如何进行运维和优化的。本文主要介绍了天象和CloudDBA两个产品,包括他们的起源、基于系统画像仓库的应用、产品化等,最后对RDS产品的可诊断性建设和可运维性建设作了补充。
10510 0