victoriaMetrics之byteBuffer

简介: victoriaMetrics之byteBuffer

victoriaMetrics之byteBuffer

VictoriaMetrics经常会处理数目庞大的指标,在处理的过程中会涉及指标的拷贝,如果在指标拷贝时都进行内存申请的话,其内存消耗和性能损耗都非常大。victoriaMetrics使用byteBuffer来复用内存,提升性能,其核心就是用了sync.pool。下面主要看下它是如何结合sync.pool运作的。


ByteBuffer的结构体如下,只包含一个切片:

type ByteBuffer struct {
  // B is the underlying byte slice.
  B []byte
}

ByteBufferPool的用法

为了服用ByteBuffer,victoriaMetrics用到了ByteBufferPool,与常见的sync.Pool用法相同,包含一个Get和一个Put函数。

// ByteBufferPool is a pool of ByteBuffers.
type ByteBufferPool struct {
  p sync.Pool
}
// Get obtains a ByteBuffer from bbp.
func (bbp *ByteBufferPool) Get() *ByteBuffer {
  bbv := bbp.p.Get()
  if bbv == nil {
    return &ByteBuffer{}
  }
  return bbv.(*ByteBuffer)
}
// Put puts bb into bbp.
func (bbp *ByteBufferPool) Put(bb *ByteBuffer) {
  bb.Reset()
  bbp.p.Put(bb)
}

Put函数用于将ByteBuffer返回给资源池,为了防止下次使用的时候出现无效数据,在返回给sync.Pool之前需要清空切片内存,其使用的Reset函数如下,bb.B = bb.B[:0]也是一种常见的清空切片内容的方式:

func (bb *ByteBuffer) Reset() {
  bb.B = bb.B[:0]
}

ByteBuffer实现了io.Writerio.ReaderFrom接口。

Writer接口实现

实现的write接口如下,比较简单,只是简单地将入参数据添加到byteBuffer中。在append的时候会增加切片的容量。

func (bb *ByteBuffer) Write(p []byte) (int, error) {
  bb.B = append(bb.B, p...)
  return len(p), nil
}

ReaderFrom接口实现

ReaderFrom中比较有意思的是看它是如何预分配容量,以及在容量不足的情况下,如何进行扩容。其核心思想是使用make预先申请一块内存,而不是通过append来让底层自动扩容。


  1. 首先获取b的长度,表示切片中已有的数据长度
  2. 由于ByteBuffer可能来自ByteBufferPool.Get,因此,其切片容量可能无法满足数据读取的需要,此时用到了ResizeWithCopyMayOverallocateResizeWithCopyMayOverallocate确保切片的容量不小于n字节,如果容量足够,则返回长度为n的子切片,否则申请新的切片,并返回长度为n的子切片。roundToNearestPow2会找出最接近n的2的整倍数的数值,以此作为新切片的容量。
// ResizeNoCopyMayOverallocate resizes b to minimum n bytes and returns the resized buffer (which may be newly allocated).
//
// If newly allocated buffer is returned then b contents isn't copied to it.
func ResizeNoCopyMayOverallocate(b []byte, n int) []byte {
  if n <= cap(b) {
    return b[:n]
  }
  nNew := roundToNearestPow2(n)
  bNew := make([]byte, nNew)
  return bNew[:n]
}
// roundToNearestPow2 rounds n to the nearest power of 2
//
// It is expected that n > 0
func roundToNearestPow2(n int) int {
  pow2 := uint8(bits.Len(uint(n - 1)))
  return 1 << pow2
}
  1. 将b的长度等于容量
  2. 设置offset为b中已有的数据偏移量
  3. 获取剩余的容量free,如果剩余的容量不足一半(free < offset),则将容量翻倍
  4. 将数据读取到offset之后的存储中,并增加偏移量
  5. Read操作返回错误时,将ByteBuffer中的切片长度设置为b,如果返回错误为EOF,则视为数据读取完成。
// ReadFrom reads all the data from r to bb until EOF.
func (bb *ByteBuffer) ReadFrom(r io.Reader) (int64, error) {
  b := bb.B
  bLen := len(b)//1
  b = ResizeWithCopyMayOverallocate(b, 4*1024) //2
  b = b[:cap(b)]//3
  offset := bLen//4
  for {
    if free := len(b) - offset; free < offset {//5
      n := len(b)
      b = append(b, make([]byte, n)...)
    }
    n, err := r.Read(b[offset:])//6
    offset += n
    if err != nil {//7
      bb.B = b[:offset]
      if err == io.EOF {
        err = nil
      }
      return int64(offset - bLen), err//9
    }
  }
}

如果无需从io.Reader中获取数据,则可以使用如下Write方法写入sync.pool中。

package util
import (
  "sync"
)
// ByteBuffer implements a simple byte buffer.
type ByteBuffer struct {
  // B is the underlying byte slice.
  B []byte
}
// Reset resets bb.
func (bb *ByteBuffer) Reset() {
  bb.B = bb.B[:0]
}
// Resize resizes b to n bytes and returns b (which may be newly allocated).
func resize(b []byte, n int) []byte {
  if nn := n - cap(b); nn > 0 {
    b = append(b[:cap(b)], make([]byte, nn)...)
  }
  return b[:n]
}
// ReadFrom reads all the data from r to bb until EOF.
func (bb *ByteBuffer) Write(data []byte) {
  bb.B = resize(bb.B, len(data))
  copy(bb.B, data)
}
// ByteBufferPool is a pool of ByteBuffers.
type ByteBufferPool struct {
  p sync.Pool
}
// Get obtains a ByteBuffer from bbp.
func (bbp *ByteBufferPool) Get() *ByteBuffer {
  bbv := bbp.p.Get()
  if bbv == nil {
    return &ByteBuffer{}
  }
  return bbv.(*ByteBuffer)
}
// Put puts bb into bbp.
func (bbp *ByteBufferPool) Put(bb *ByteBuffer) {
  bb.Reset()
  bbp.p.Put(bb)
}

总结

后续可以使用该库来满足从io.Reader中读取数据,而不用担心buffer不足,借助ByteBufferPool可以有效地复用buffer。

目录
相关文章
|
机器学习/深度学习 运维 算法
梯度&散度&旋度&峰度&偏度你分得清楚吗?驻点&鞍点你分得清楚吗?曲率&斜率你分得清楚吗?
本文介绍了四种常见的物理量:加速度,速度,位移和力学功。详细介绍了它们的定义、计算以及在物理学和工程学领域中的应用。此外,本文还介绍了四种与物理量相关的概念:向量、标量、质量和密度。 数学,物理,机器学习领域常见概念区分
3116 0
|
Prometheus 监控 关系型数据库
Linux监控之夜莺
Linux监控之夜莺
1430 0
|
11天前
|
SQL 安全 网络协议
常见的网络攻击
常见网络攻击包括恶意软件、网络钓鱼、中间人攻击、DDoS攻击、SQL注入、零日漏洞和DNS隧道等。攻击者利用漏洞或社会工程手段入侵系统,窃取数据、破坏服务或控制设备。防范需加强安全意识与技术防护。
|
5月前
|
存储 缓存 Apache
Apache Iceberg数据湖高级特性及性能调优
性能调优涵盖索引优化、排序策略与元数据管理。通过布隆过滤器、位图索引等提升查询效率,结合文件内/间排序优化I/O与压缩,辅以Z-Order实现多维数据聚集。同时,合理配置元数据缓存与清单合并,加速查询规划。适用于点查、全表扫描及高并发写入场景,显著提升系统性能与资源利用率。
|
9月前
|
数据采集 JSON 数据可视化
JSON数据解析实战:从嵌套结构到结构化表格
在信息爆炸的时代,从杂乱数据中提取精准知识图谱是数据侦探的挑战。本文以Google Scholar为例,解析嵌套JSON数据,提取文献信息并转换为结构化表格,通过Graphviz制作技术关系图谱,揭示文献间的隐秘联系。代码涵盖代理IP、请求头设置、JSON解析及可视化,提供完整实战案例。
599 4
JSON数据解析实战:从嵌套结构到结构化表格
zdl
|
消息中间件 运维 大数据
大数据实时计算产品的对比测评:实时计算Flink版 VS 自建Flink集群
本文介绍了实时计算Flink版与自建Flink集群的对比,涵盖部署成本、性能表现、易用性和企业级能力等方面。实时计算Flink版作为全托管服务,显著降低了运维成本,提供了强大的集成能力和弹性扩展,特别适合中小型团队和业务波动大的场景。文中还提出了改进建议,并探讨了与其他产品的联动可能性。总结指出,实时计算Flink版在简化运维、降低成本和提升易用性方面表现出色,是大数据实时计算的优选方案。
zdl
571 56
|
Linux C++
Linux C/C++之IO多路复用(poll,epoll)
这篇文章详细介绍了Linux下C/C++编程中IO多路复用的两种机制:poll和epoll,包括它们的比较、编程模型、函数原型以及如何使用这些机制实现服务器端和客户端之间的多个连接。
505 0
Linux C/C++之IO多路复用(poll,epoll)
|
应用服务中间件 Linux 网络安全
使用Certbot申请/续签ssl证书
使用Certbot申请/续签ssl证书
2394 8
|
JSON 关系型数据库 MySQL
MySQL全文搜索与JSON支持:高效检索与灵活数据处理
本文深入探讨了MySQL数据库中的全文搜索与JSON支持,通过详细的代码示例,阐述了全文搜索的原理、全文索引的创建,以及JSON数据类型的使用与操作。全文搜索在数据库中的重要性日益凸显,MySQL提供了全文索引来实现高效的文本数据检索,通过MATCH AGAINST语句,可以轻松地进行全文搜索操作。此外,MySQL的JSON支持为半结构化数据的存储和查询提供了灵活的解决方案,您可以存储JSON对象、数组等数据,并使用JSON函数来查询和修改数据。
1552 0
Python成员属性的内存特性与底层内存优化方案
这篇博客主要分享一下python成员属性的内存特性,也就是python底层节约内存的优化方案

热门文章

最新文章