golang高性能自适应基数树实现插件库go-adaptive-radix-tree的使用

Golang高性能自适应基数树实现插件库go-adaptive-radix-tree的使用

介绍

这是一个Go语言实现的自适应基数树(Adaptive Radix Tree)库。

主要特性:

  • 查找性能超过高度优化的替代方案
  • 支持高效的插入和删除操作
  • 空间效率高
  • 性能可与哈希表媲美
  • 保持数据有序,支持范围扫描和前缀查找等额外操作
  • 搜索/插入/删除操作的时间复杂度为O(k),k是键的长度
  • 支持查找最小/最大值
  • 有序迭代
  • 基于前缀的迭代
  • 支持反向迭代
  • 支持包含空字节的键,任何字节数组都可以作为键

使用示例

package main

import (
	"fmt"
	art "github.com/plar/go-adaptive-radix-tree/v2"
)

func main() {
	// 初始化一个新的自适应基数树
	tree := art.New()

	// 向树中插入键值对
	tree.Insert(art.Key("apple"), "A sweet red fruit")
	tree.Insert(art.Key("banana"), "A long yellow fruit")
	tree.Insert(art.Key("cherry"), "A small red fruit")
	tree.Insert(art.Key("date"), "A sweet brown fruit")

	// 通过键搜索值
	if value, found := tree.Search(art.Key("banana")); found {
		fmt.Println("Found:", value)
	} else {
		fmt.Println("Key not found")
	}

	// 按升序遍历树
	fmt.Println("\nAscending order iteration:")
	tree.ForEach(func(node art.Node) bool {
		fmt.Printf("Key: %s, Value: %s\n", node.Key(), node.Value())
		return true
	})

	// 使用反向遍历按降序遍历树
	fmt.Println("\nDescending order iteration:")
	tree.ForEach(func(node art.Node) bool {
		fmt.Printf("Key: %s, Value: %s\n", node.Key(), node.Value())
		return true
	}, art.TraverseReverse)

	// 遍历具有特定前缀的键
	fmt.Println("\nIteration with prefix 'c':")
	tree.ForEachPrefix(art.Key("c"), func(node art.Node) bool {
		fmt.Printf("Key: %s, Value: %s\n", node.Key(), node.Value())
		return true
	})
}

// 预期输出:
// Found: A long yellow fruit
//
// Ascending order iteration:
// Key: apple, Value: A sweet red fruit
// Key: banana, Value: A long yellow fruit
// Key: cherry, Value: A small red fruit
// Key: date, Value: A sweet brown fruit
//
// Descending order iteration:
// Key: date, Value: A sweet brown fruit
// Key: cherry, Value: A small red fruit
// Key: banana, Value: A long yellow fruit
// Key: apple, Value: A sweet red fruit
//
// Iteration with prefix 'c':
// Key: cherry, Value: A small red fruit

从v1迁移到v2

  1. 更新import语句:
from `art "github.com/plar/go-adaptive-radix-tree"`
  to `art "github.com/plar/go-adaptive-radix-tree/v2"`
  1. 更新go模块依赖:
$ go get github.com/plar/go-adaptive-radix-tree/v2
$ go mod tidy

如果你实现了自己的Tree接口,需要更新以下方法以支持options:

	ForEachPrefix(keyPrefix Key, callback Callback, options ...int)

性能

性能基准测试使用了从不同项目中提取的数据集:

  • "Words"数据集包含235,886个英文单词
  • "UUIDs"数据集包含100,000个UUID
  • "HSK Words"数据集包含4,995个单词

性能对比表:

go-adaptive-radix-tree # Average time Bytes per operation Allocs per operation
Tree Insert Words 9 117,888,698 ns/op 37,942,744 B/op 1,214,541 allocs/op
Tree Search Words 26 44,555,608 ns/op 0 B/op 0 allocs/op
Tree Insert UUIDs 18 59,360,135 ns/op 18,375,723 B/op 485,057 allocs/op
Tree Search UUIDs 54 21,265,931 ns/op 0 B/op 0 allocs/op
go-art # Average time Bytes per operation Allocs per operation
Tree Insert Words 5 272,047,975 ns/op 81,628,987 B/op 2,547,316 allocs/op
Tree Search Words 10 129,011,177 ns/op 13,272,278 B/op 1,659,033 allocs/op
Tree Insert UUIDs 10 140,309,246 ns/op 33,678,160 B/op 874,561 allocs/op
Tree Search UUIDs 20 82,120,943 ns/op 3,883,131 B/op 485,391 allocs/op

要查看更多基准测试,可以运行:

$ ./make qa/benchmarks

更多关于golang高性能自适应基数树实现插件库go-adaptive-radix-tree的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang高性能自适应基数树实现插件库go-adaptive-radix-tree的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


go-adaptive-radix-tree 高性能自适应基数树实现

go-adaptive-radix-tree (简称 ART) 是一个 Golang 实现的高性能自适应基数树库,它是基数树的一种优化变体,特别适合需要高性能键值存储的场景。

特性概述

  • 自适应节点大小,根据子节点数量自动调整
  • 内存效率高,比传统基数树更节省空间
  • 支持并发安全操作
  • 提供前缀搜索、范围查询等高级功能
  • 性能优于标准哈希表和普通树结构

安装

go get github.com/plar/go-adaptive-radix-tree

基本使用示例

package main

import (
	"fmt"
	art "github.com/plar/go-adaptive-radix-tree"
)

func main() {
	// 创建新树
	tree := art.New()

	// 插入键值对
	tree.Insert(art.Key("Hello"), "World")
	tree.Insert(art.Key("Golang"), "Rocks")
	tree.Insert(art.Key("Radix"), "Tree")

	// 查找
	value, found := tree.Search(art.Key("Golang"))
	if found {
		fmt.Printf("Found: %v\n", value) // 输出: Found: Rocks
	}

	// 删除
	tree.Delete(art.Key("Hello"))

	// 遍历所有键值对
	tree.ForEach(func(node art.Node) bool {
		fmt.Printf("Key: %s, Value: %v\n", node.Key(), node.Value())
		return true
	})
}

高级功能

前缀搜索

// 插入一些测试数据
tree.Insert(art.Key("api:v1:users"), "usersV1")
tree.Insert(art.Key("api:v1:products"), "productsV1")
tree.Insert(art.Key("api:v2:users"), "usersV2")

// 搜索所有api:v1前缀的键
results := []string{}
tree.ForEachPrefix(art.Key("api:v1:"), func(node art.Node) bool {
	results = append(results, node.Value().(string))
	return true
})

fmt.Println(results) // 输出: [usersV1 productsV1]

范围查询

tree.Insert(art.Key("apple"), 1)
tree.Insert(art.Key("banana"), 2)
tree.Insert(art.Key("cherry"), 3)
tree.Insert(art.Key("date"), 4)

// 查询a-c之间的键
tree.ForEachRange(art.Key("a"), art.Key("d"), func(node art.Node) bool {
	fmt.Printf("%s: %v\n", node.Key(), node.Value())
	return true
})

并发安全使用

import "sync"

func main() {
	tree := art.New()
	var wg sync.WaitGroup

	// 并发插入
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			key := art.Key(fmt.Sprintf("key%d", i))
			tree.Insert(key, i)
		}(i)
	}

	wg.Wait()

	// 验证插入结果
	fmt.Println("Tree size:", tree.Size())
}

性能考虑

  1. 批量操作:对于批量插入,考虑预排序键可以提升性能
  2. 键设计:使用较短的公共前缀可以最大化ART的优势
  3. 内存使用:ART比标准哈希表更节省内存,特别是键有公共前缀时

实际应用场景

  1. 路由系统(如HTTP路由器)
  2. 数据库索引
  3. 内存键值存储
  4. 自动补全系统
  5. IP路由表

与标准map的对比

func benchmark() {
	// 测试数据
	keys := make([]string, 1000000)
	for i := range keys {
		keys[i] = fmt.Sprintf("key:%d:%d:%d", i, i*2, i*3)
	}

	// 标准map测试
	start := time.Now()
	m := make(map[string]interface{})
	for _, k := range keys {
		m[k] = true
	}
	fmt.Println("Map insert:", time.Since(start))

	// ART测试
	start = time.Now()
	tree := art.New()
	for _, k := range keys {
		tree.Insert(art.Key(k), true)
	}
	fmt.Println("ART insert:", time.Since(start))
}

在键具有公共前缀的情况下,ART通常表现出更好的性能,特别是随着数据量增长时。

注意事项

  1. 键必须是[]byte类型,使用art.Key()进行转换
  2. 默认情况下非并发安全,并发访问需要外部同步
  3. 对于完全随机的键,标准哈希表可能性能更好

go-adaptive-radix-tree是一个功能强大且高效的库,特别适合处理具有公共前缀的键的场景。它的设计平衡了内存使用和查询性能,是许多高性能应用的理想选择。

回到顶部