再见,ELK( 四 )

  • 数据类型 3:用于根据日志流 ID 查询底层存储 Chunk 的 ID 。
  • 除了采用分表外 , Loki 还采用分桶、分片的方式优化索引查询速度 。
    分桶:
    再见,ELK

    文章插图
     
    以天分割:bucketID = timestamp / secondsInDay 。
    以小时分割:bucketID = timestamp / secondsInHour 。
    分片:将不同日志流的索引分散到不同分片 , shard = seriesID% 分片数 。
    Chunk 状态:Chunk 作为在 Ingester 中重要的数据单元 , 其在内存中的生命周期内分如下四种状态:
    • Writing:正在写入新数据 。
    • Waiting flush:停止写入新数据 , 等待写入到存储 。
    • Retain:已经写入存储 , 等待销毁 。
    • Destroy:已经销毁 。
    四种状态之间的转换以 writing→waiting flush→retain→destroy 顺序进行 。
    状态转换时机:
    • 协作触发:有新的数据写入请求 。
    • 定时触发:刷写周期触发将 chunk 写入存储 , 回收周期触发将 chunk 销毁 。
    writing 转为 waiting flush:chunk 初始状态为 writing , 标识正在接受数据的写入 , 满足如下条件则进入到等待刷写状态:
    • chunk 空间满(协作触发) 。
    • chunk 的存活时间(首末两条数据时间差)超过阈值 (定时触发) 。
    • chunk 的空闲时间(连续未写入数据时长)超过设置 (定时触发) 。
    waiting flush 转为 etain:Ingester 会定时的将等待刷写的 chunk 写到底层存储 , 之后这些 chunk 会处于”retain“状态 , 这是因为 ingester 提供了对最新数据的搜索服务 , 需要在内存里保留一段时间 , retain 状态则解耦了数据的刷写时间以及在内存中的保留时间 , 方便视不同选项优化内存配置 。
    destroy , 被回收等待 GC 销毁:总体上 , Loki 由于针对日志的使用场景 , 采用了顺序追加方式写入 , 只索引元信息 , 极大程度上简化了它的数据结构和处理逻辑 , 这也为 Ingester 能够应对高速写入提供了基础 。
    Querier:查询服务的执行组件 , 其负责从底层存储拉取数据并按照 LogQL 语言所描述的筛选条件过滤 。它可以直接通过 API 提供查询服务 , 也可以与 queryFrontend 结合使用实现分布式并发查询 。
    ⑥查询类型
    查询类型如下:
    • 范围日志查询
    • 单日志查询
    • 统计查询
    • 元信息查询
    在这些查询类型中 , 范围日志查询应用最为广泛 , 所以下文只对范围日志查询做详细介绍 。
    并发查询:对于单个查询请求 , 虽然可以直接调用 Querier 的 API 进行查询 , 但很容易会由于大查询导致 OOM , 为应对此种问题 querier 与 queryFrontend 结合一起实现查询分解与多 querier 并发执行 。
    再见,ELK

    文章插图
     
    每个 querier 都与所有 queryFrontend 建立 grpc 双向流式连接 , 实时从 queryFrontend 中获取已经分割的子查询求 , 执行后将结果发送回 queryFrontend 。
    具体如何分割查询及在 querier 间调度子查询将在 queryFrontend 环节介绍 。
    ⑧查询流程
    先解析 logQL 指令 , 然后查询日志流 ID 列表 。
    Loki 根据不同的标签选择器语法使用了不同的索引查询逻辑 , 大体分为两种:
    = , 或多值的正则匹配=~ , 工作过程如下:
    以类似下 SQL 所描述的语义查询出标签选择器里引用的每个标签键值对所对应的日志流 ID(seriesID)的集合 。
    SELECT * FROM Table_N WHERE hash=? AND range>=?    AND value=https://www.isolves.com/it/cxkf/bk/2020-12-23/labelValue hash 为租户 ID(userID)、分桶(bucketID)、标签名(labelName)组合计算的哈希值;range 为标签值(labelValue)计算的哈希值 。
    将根据标签键值对所查询的多个 seriesID 集合取并集或交集求最终集合 。
    比如 , 标签选择器{file="app.log", level=~"debug|error"}的工作过程如下: