惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
Microsoft Azure Blog
Microsoft Azure Blog
MongoDB | Blog
MongoDB | Blog
小众软件
小众软件
Apple Machine Learning Research
Apple Machine Learning Research
O
OpenAI News
酷 壳 – CoolShell
酷 壳 – CoolShell
The GitHub Blog
The GitHub Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
博客园 - 聂微东
Engineering at Meta
Engineering at Meta
W
WeLiveSecurity
Hacker News: Ask HN
Hacker News: Ask HN
大猫的无限游戏
大猫的无限游戏
Vercel News
Vercel News
D
Docker
F
Full Disclosure
AI
AI
罗磊的独立博客
博客园 - 【当耐特】
U
Unit 42
S
SegmentFault 最新的问题
Stack Overflow Blog
Stack Overflow Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
P
Palo Alto Networks Blog
博客园_首页
H
Help Net Security
量子位
月光博客
月光博客
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - 司徒正美
F
Fortinet All Blogs
D
DataBreaches.Net
B
Blog RSS Feed
Webroot Blog
Webroot Blog
TaoSecurity Blog
TaoSecurity Blog
S
Secure Thoughts
爱范儿
爱范儿
I
InfoQ
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Attack and Defense Labs
Attack and Defense Labs
Application and Cybersecurity Blog
Application and Cybersecurity Blog
C
CERT Recently Published Vulnerability Notes
Martin Fowler
Martin Fowler
Blog — PlanetScale
Blog — PlanetScale
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
Securelist

VictoriaMetrics: Simple & Reliable Monitoring for Everyone on VictoriaMetrics

Operator now has Long-Term Support (LTS) version Multi-tiered Observability: A Practical Way to Handle Diverse Workloads VictoriaMetrics April 2026 Ecosystem Updates Not All Telemetry Requires Premium Pricing VictoriaMetrics at KubeCon Amsterdam: Community Highlights What's new in VictoriaMetrics Anomaly Detection (Q1 2026) What's New in VictoriaMetrics Cloud Q1 2026? Logs, MCP Server, Better Alerting, and... a Secret Project VictoriaMetrics at KubeCon: Optimizing Tail Sampling in OpenTelemetry with Retroactive Sampling VictoriaMetrics March 2026 Ecosystem Updates Observability Lessons From OpenAI Benchmarking Kubernetes Log Collectors: vlagent, Vector, Fluent Bit, OpenTelemetry Collector, and more VictoriaMetrics February 2026 Ecosystem Updates VictoriaMetrics at FOSDEM, Cloud Native Days France, and CfgMgmtCamp Ghent VictoriaLogs in VictoriaMetrics Cloud: Fast, Cost-Effective Log Management is Here What’s new in VictoriaMetrics Anomaly Detection (2025) VictoriaMetrics January 2026 Ecosystem Updates VictoriaLogs Basics: What You Need to Know, with Examples & Visuals What's New in VictoriaMetrics Cloud Q4 2025? New tiers, more deployment options, IaC and alerting rules. Vibe coding tools observability with VictoriaMetrics Stack and OpenTelemetry How a US Software Provider Improved Traffic Alerting with VictoriaMetrics Anomaly Detection VictoriaMetrics 2025 Developer Experience: A Year in Review Spotify’s performance & control across large monitoring environments with VictoriaMetrics VictoriaMetrics Achieves Red Hat OpenShift Operator Certification Our latest updates across the VictoriaMetrics Observability ecosystem New Capacity Tiers in VictoriaMetrics Cloud Announcing 1B+ Downloads & Product Development With Logs, Traces, Metrics AI Agents Observability with OpenTelemetry and the VictoriaMetrics Stack Discarding gRPC-Go: The Story Behind OTLP/gRPC Support in VictoriaTraces What's New in VictoriaMetrics Cloud Q3 2025? From new region in Asia to proactive alerts How DreamHost Slashed Memory Usage by 80% and Scaled to 76 Million Time Series Upcoming Conferences & Meetups: Where to Meet Our Team VictoriaMetrics Long-Term Support (LTS): H2 2025 Update Creating a Sustainable Open Source Business Model - Introduction Full-Stack Observability with VictoriaMetrics in the OTel Demo Alerting Best Practices vmanomaly Deep Dive: Smarter Alerting with AI (Tech Talk Companion) VictoriaLogs Practical Ingestion Guide for Message, Time and Streams Monotonic and Wall Clock Time in the Go time package Hello Singapore! VictoriaMetrics Cloud Expands to Asia Pacific MCP Server Integration & Much More: What's New in VictoriaMetrics Cloud Q2 2025 FIPS 140-3 Compatible Builds for VictoriaMetrics Enterprise Components VictoriaLogs Unleashed: Cluster Version Now Available for Exceptional, Linear Scaling Integrations made easy with VictoriaMetrics Cloud Developer's Note: Research on Distributed Tracing, Comparing With Tempo and ClickHouse vmagent: Key Features Explained in Under 15 Minutes Go synctest: Solving Flaky Tests vmalert: Maximize Your Monitoring (Tech Talk Companion) Celebrating 14K Stars on GitHub: Spring Update vmalert: Maximize Your Monitoring VictoriaMetrics Connects with the Open Source Community at LinuxFest Northwest 2025 Graceful Shutdown in Go: Practical Patterns VictoriaLogs: Gaps, Gains & Growth Prometheus Monitoring: Functions, Subqueries, Operators, and Modifiers VictoriaMetrics Cloud: What's New in Q1 2025? Don’t default to microservices: You’ll thank us later! Container CPU Requests & Limits Explained with GOMAXPROCS Tuning gRPC in Go: Streaming RPCs, Interceptors, and Metadata From Chaos to Clarity with VictoriaLogs Prometheus Alerting 101: Rules, Recording Rules, and Alertmanager Heading to London: Meet Our Team at KubeCon Europe 2025 Inside vmselect: The Query Processing Engine of VictoriaMetrics Meet Our Team at Scale 22x Practical Protobuf - From Basic to Best Practices VictoriaLogs Status Update: Heading Towards the Cluster Version 24th of February 2025 Statement: VictoriaMetrics Stands with Ukraine! Prometheus Metrics Explained: Counters, Gauges, Histograms & Summaries Prometheus Monitoring: Instant Queries and Range Queries Explained 300%+ Growth in 2024: Join Our Team in 2025! FOSDEM 2025 recap How Protobuf Works—The Art of Data Encoding OpenTelemetry, Prometheus, and More: Which Is Better for Metrics Collection and Propagation? How vmstorage Handles Query Requests From vmselect How vmstorage's IndexDB Works VictoriaMetrics Tech Talk Stream: A Deep Dive into Blackbox Monitoring How HTTP/2 Works and How to Enable It in Go VictoriaMetrics Cloud: What's New in Q4 2024? How vmstorage Processes Data: Retention, Merging, Deduplication,... How vmstorage Handles Data Ingestion From vminsert When Metrics Meet vminsert: A Data-Delivery Story From net/rpc to gRPC in Go Applications Piros | VictoriaMetrics Partner Allenta | VictoriaMetrics Partner CloudRaft | VictoriaMetrics Partner Sensedia & VictoriaMetrics: API-compatible Efficient Storage Scalable Prometheus: Why DSV Chose VictoriaMetrics Sensor Factory | VictoriaMetrics Partner Erythix | VictoriaMetrics Partner Groove X & VictoriaMetrics: Faster Device Health Monitoring Scaled & Performant Monitoring at Spotify with VictoriaMetrics Grammarly & VictoriaMetrics: 10× Lower Costs & Direct Access Zelarsoft | VictoriaMetrics Partner DFKI & VictoriaMetrics: Efficient Long-Term Metric Storage Niubits | VictoriaMetrics Partner Megazone Cloud | VictoriaMetrics Partner Cogito Software | VictoriaMetrics Partner Bajau | VictoriaMetrics Partner Find Out Why Dig Security Chose VictoriaMetrics! Ness | VictoriaMetrics Partner Alpha Data | VictoriaMetrics Partner SIOS Technology | VictoriaMetrics Partner
Performance optimization techniques in time series databases: sync.Pool for CPU-bound operations
Roman Khavronenko / Aliaksandr Valialkin · 2023-12-08 · via VictoriaMetrics: Simple & Reliable Monitoring for Everyone on VictoriaMetrics

This blog post is also available as a recorded talk with slides.

Table of Contents

Performance optimization techniques in time series databases:


Internally, VictoriaMetrics makes heavy use of sync.Pool, a data structure built into Go’s standard library. sync.Pool is intended to store temporary, fungible objects for reuse to relieve pressure on the garbage collector. If you are familiar with free lists, you can think of sync.Pool as a data structure that allows you to implement them in a thread-safe way.

A good example of where sync.Pool comes in handy is reusing bytes.Buffer objects. bytes.Buffer objects are great for scenarios where you have to read some raw data and temporarily store it in the memory. For example, VictoriaMetrics makes heavy use of bytes.Buffer objects when decompressing data from the database, and when parsing metric metadata on scrapes.

Importantly, bytes.Buffer objects are little more than allocated sections of memory with some capacity and availability tracking built in. They are temporary helper objects, and one bytes.Buffer can be easily swapped in for another — they are fungible.

To avoid unnecessary allocations of bytes.Buffer objects, and to ease pressure on the garbage collector, VictoriaMetrics has an internal data structure that uses sync.Pool to maintain a life-cycle of already allocated bytes.Buffer objects:

type ByteBufferPool struct {
    p sync.Pool
}

func (bbp *ByteBufferPool) Get() *ByteBuffer {
    bbv := bbp.p.Get()
    if bbv == nil {
        return &ByteBuffer{}
    }
    return bbv.(*ByteBuffer)
}

func (bbp *ByteBufferPool) Put(bb *ByteBuffer) {
    bb.Reset()
    bbp.p.Put(bb)
}

The code above defines a type ByteBufferPool that contains a sync.Pool for storing bytes.Buffer objects. It defines two methods on this type:

  • Get either returns a bytes.Buffer from the pool, or allocates and returns a new object.
  • Put resets a bytes.Buffer and then adds it back into the pool, so it can be reused via Get later.

ByteBufferPool is used a lot in the VictoriaMetrics codebase, and it significantly reduces the number of new allocations it needs to perform. There are a couple of things to be aware of when using sync.Pool in this way, however.

Object stealing

#

Internally, sync.Pool is implemented using per-processor local pools. When goroutine is scheduled to run on a specific thread associated with specific processor and will try to retrieve an object from the pool, sync.Pool will first look in the current processor local pool. If it can’t find an object there it will try to “steal” an object from another processor pool. Stealing an object from another pool takes time because of inter-CPU synchronization. If it can’t steal it, a new object will be allocated.

Due to these local pools, the ideal scenario for using sync.Pool is where objects are retrieved and released in the same goroutine, so these objects will belong to the same local processor pool where goroutine runs.
This heavily reduces context switches between retrieving the object from the pool and returning it to the pool. It prevents object stealing and reduces the number of objects that are allocated overall, further relieving pressure on the garbage collector.

A less ideal, but still viable use case for sync.Pool is where objects are synchronously processed by a single goroutine at a time. For example, an object is retrieved from the pool in one goroutine, and then passed to another goroutine, which uses the object and returns it to the pool.

The problem with the synchronous processing case is that there’s a higher chance for different goroutines to get scheduled on different threads, which means the object will be retrieved from one local pool of one processor and returned to the pool of another processor. This increases the chances that sync.Pool needs to steal an object, reducing performance.

I/O-bound tasks

#

Using sync.Pool to reuse objects in I/O-bound tasks is much less efficient than reusing objects for CPU-bound tasks.

I/O can be slow and sporadic, meaning that there is a high degree of randomness to how long an I/O operation will take. This can lead to the number of calls to Get and Put becoming unbalanced, resulting in suboptimal reuse of objects:

  • if I/O operation hangs, the objects in sync.Pool are just sitting there occupying the memory;
  • when I/O operation finally returns a result, the objects in sync.Pool could be already removed by garbage collector resulting in new allocations.

Example ByteBufferPool usage

#

One use case VictoriaMetrics has for ByteBufferPool is decompressing stored data. Decompression is a CPU-bound operation that allocates some temporary memory, which makes it a perfect candidate to use ByteBufferPool. The code below illustrates the use of ByteBufferPool during a decompression operation:

bb := bbPool.Get() // acquire buffer from pool
// perform decompressing in acquired buffer 
bb.B, err = DecompressZSTD(bb.B[:0], src) 
if err != nil {
    return nil, fmt.Errorf("cannot decompress: %w", err)
}
// unmarshal from temporary buffer to destination buffer
dst, err = unmarshalInt64NearestDelta(dst, bb.B)
bbPool.Put(bb) // release buffer to the pool, so it can be reused

The above code gets a bytes.Buffer from bbPool, a ByteBufferPool type. It then decompresses a block that has already been read from disk and places the result into the retrieved bytes.Buffer. It’s important to note that the block has already been read from disk, as this makes the decompression operation entirely CPU-bound and maintains the balance between Get and Put calls.

Once the code has decompressed the block, it writes the result to a destination, dst, and returns the buffer to the pool, so it can be reused. It is important not to return bb or store references to it, as it can be acquired and modified by another goroutine at any moment after being returned to pool.

Leveled bytes.Buffer pools

#

So far, the examples in this article have assumed that all bytes.Buffers are interchangeable. While this is technically true, in the real world buffers come in a wide range of sizes. This can lead to inefficient memory usage if code that uses a small amount of memory receives a large buffer from the pool and vice versa.

As an example, one target for metrics scraping might expose 100 metrics while another might expose 10,000. The vmagent goroutine that scrapes each target would need a different buffer size. In the ByteBufferPool implementation above, calling code has no control over the size of the buffer it receives. So the scraping goroutine can retrieve a smaller buffer than needed and spend extra time on expanding it. This would slowly replace small buffers in the pool with bigger buffers, increasing the overall memory usage.

You can improve the efficiency of ByteBufferPool by splitting it into multiple levels, or buckets.

An example of objects sorting to different buckets An example of objects sorting to different buckets

Each level contains a different range of buffer sizes and requests to the pool can request a certain size based on the expected requirement.

// pools contains pools for byte slices of various capacities.
//
// pools[0] is for capacities from 0 to 8
// pools[1] is for capacities from 9 to 16
// pools[2] is for capacities from 17 to 32
// ...
// pools[n] is for capacities from 2^(n+2)+1 to 2^(n+3)
//
// Limit the maximum capacity to 2^18, since there are no performance benefits
// in caching byte slices with bigger capacities.
var pools [17]sync.Pool

The above code snippet shows how these levels are represented in VictoriaMetrics’ leveledbytebufferpool package. The maximum capacity of a cached pool is limited to 2^18 bytes as we’ve found that the RAM cost of storing buffers larger than this limit is not worth the savings of not recreating those buffers.

Adding levels to a pool of buffers changes the Get method to require an expected size. This enables the pool to return a buffer of the appropriate size. See the code snippet below for how this is used in the vmagent scraping example:

func (sw *scrapeWork) scrape() {
    body := leveledbytebufferpool.Get(sw.previousResponseBodyLength)
    body.B = sw.ReadData(body.B[:0])
    sw.processScrapedData(body)
    leveledbytebufferpool.Put(body)
}

The above code snippet gets a buffer based on the size needed for the last request to this scrape target. The number of metrics a scrape target exposes doesn’t change much on each scrape and so this is a decent guess at how large a buffer will be needed this time. The Put function signature hasn’t changed since the function can figure out how large the buffer is itself.

Conclusion and other resources

#

Optimizations are always situation-dependent, but I hope that this series of articles has given you more tools that you can use to optimize your own applications. If this article has interested you in time series databases and the work we do at VictoriaMetrics, please check out our GitHub org. All our code is proudly open source and welcomes contributions.

If you’re interested in performance, I’d strongly recommend VictoriaMetrics: scaling to 100 million metrics per second, a talk given by our CTO in 2022 that is packed with technical details. You can also check out more talks from VictoriaMetrics team members in this YouTube playlist.

This article was originally a talk at GopherCon Europe 2023. You can watch the talk on YouTube, or read the slides.