IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    时序数据查询接口

    forhot2000发表于 2024-01-17 20:44:07
    love 0

    最近做了一个数据收集的项目,需要对外提供数据查询接口,将收集到所有的数据提供给数据分析程序调用,收集到的数据量很大,需要注意查询接口的性能问题。

    方法一

    提到 API,最先想到的当然就是提供 RESTful 的查询接口,接受时间查询条件、分页大小、当前页码,返回json数据。

    GET /api/message?start_time={start_time}&end_time={end_time}&size={page_size}&page={page_index}

    对应的查询sql如下

    select * 
    from message 
    where createtime >= @start_time -- 如果有 start_time
    and createtime < @end_time -- 如果有 end_time
    order by createtime, id -- 排序中添加id解决createtime重复的问题
    limit @page_size
    offset @offset -- offset = page_size * (page_index - 1)

    优点

    • 简单通用,适合普通前端页面调用

    缺点

    • 数据量很大的情况下,查询后面的分页会越来越慢,直接会拖垮服务器
    • 查询下一页的时候,如果前面的数据发生了变化,将会出现与上一页重复的数据,或者遗漏部分数据

    方法二

    始终根据时间查询,能够高效的利用时间字段的索引,能够保证下一页的数据紧跟上一页,不会出现重复数据或遗漏数据,适合连续获取分页数据的场景,比如数据分析服务拉取数据。

    查询第一页,同方法一

    GET /api/message?start_time={start_time}&end_time={end_time}&size={page_size}

    对应的查询sql如下

    select * 
    from message 
    where createtime >= @start_time -- 如果有 start_time
    and createtime < @end_time -- 如果有 end_time
    order by createtime, id -- 排序中添加id解决createtime重复的问题
    limit @page_size

    从第二页开始,根据上一页的最后一个结果的 createtime 和 id 来查询,在原来的 URL 后增加 &last_time={last_time}&last_id={last_id}

    GET /api/message?start_time={start_time}&end_time={end_time}&size={page_size}&last_time={last_time}&last_id={last_id}

    为了保护 last_time 和 last_id,可以在服务器端缓存 last_time 和 last_id,仅告诉客户端一个 next_token,客户端请求下一页的时候直接传入服务器端发下的 next_token,在原来的 URL 后增加 &next_token={next_token}

    /api/message?start_time={start_time}&end_time={end_time}&size={page_size}&next_token={next_token}

    对应的查询sql如下

    select *
    from message
    where createtime < {end_time} -- 如果有 end_time
    and createtime > {last_time} -- 覆盖了 createtime >= @start_time
    and id > {last_id}
    order by createtime, id -- 排序中添加id解决createtime重复的问题
    limit {page_size}

    优点

    • 不会因为页码太大导致查询速度变慢
    • 下一页始终紧跟上一页结果,不会出现重复数据或遗漏数据

    缺点

    • 没有页码,前端无法显示当前页页码

    方法三

    在方法二的基础上,提供 websocket 的接口,利用 websocket 的长链接特性,将 next_token 存在服务器端,客户端每次只要发送 next 命令即可接收下一页的数据。

    客户端 websocket 的消息队列如下

    ↑ {"cmd":"query","data":{"page_size":$page_size,"where":{"start_time":"$start_time","end_time":"$end_time"}}}
    ↓ {"data":[...]}
    ↑ {"cmd":"next"}
    ↓ {"data":[...]}
    ↑ {"cmd":"next"}
    ↓ {"data":[...]}
    注意:始终应该由客户端发起 next 命令,防止服务器端发送数据过快,导致客户端来不及消费数据的情况。

    优点

    • 减少了 HTTP 的握手次数,提高了性能
    • 对用户屏蔽了 next_token,简化了调用方的逻辑

    缺点

    • 需要额外处理网络不稳定导致 websocket 断开的情况


沪ICP备19023445号-2号
友情链接