jsonDB

package module
v0.0.0-...-81d908b Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 7, 2024 License: Apache-2.0 Imports: 14 Imported by: 0

README

jsonDB: 一个轻量级的JSON文档数据库

目录

  1. 简介
  2. 主要特性
  3. 包结构
  4. 核心组件
  5. 基本操作
  6. 索引管理
  7. 查询操作
  8. 并发控制
  9. 持久化和恢复
  10. 日志系统
  11. 性能优化
  12. 使用示例
  13. 注意事项和限制
  14. 未来改进方向

简介

jsonDB 是一个用 Go 语言编写的轻量级 JSON 文档数据库。它提供了简单而高效的方式来存储、检索和管理 JSON 格式的数据。这个数据库设计用于嵌入式应用程序,支持基本的 CRUD 操作、索引、并发访问以及持久化存储。

主要特性

  • 支持 JSON 文档的存储和检索
  • 提供单字段和复合字段索引
  • 支持基本查询、范围查询和模糊查询
  • 并发安全的操作
  • 支持持久化存储和从崩溃中恢复
  • 使用 Write-Ahead Logging (WAL) 确保数据一致性
  • 可配置的日志系统
  • 高性能的内存中操作

包结构

jsonDB 包含以下主要文件:

  • db.go: 定义了 Database 结构体和核心数据库操作
  • document.go: 实现了文档的 CRUD 操作
  • indexs.go: 处理索引相关的功能
  • query.go: 实现查询操作
  • complexquery.go: 实现高级查询功能(如范围查询和模糊查询)
  • write.go: 处理数据持久化和 WAL
  • utils.go: 包含常量和工具函数
  • logger.go: 实现可配置的日志系统
  • trie.go: 实现支持模糊查询的 Trie 结构

核心组件

主要的结构体包括 DatabaseDocumentIndexCompositeIndex。这些组件共同构成了 jsonDB 的核心架构。

基本操作

jsonDB 支持以下基本操作:

  • 创建数据库
  • 插入文档
  • 更新文档
  • 删除文档
  • 查询文档

每个操作都有详细的使用说明和代码示例。

索引管理

jsonDB 支持创建单字段索引和复合索引,以加速查询操作。

查询操作

jsonDB 提供了多种查询方式:

  1. 基本查询:使用 Query 方法进行单字段精确匹配查询。
  2. 复合查询:使用 QueryComposite 方法进行多字段组合查询。
  3. 范围查询:使用 RangeQuery 方法进行范围查询,支持数值和时间类型。
  4. 模糊查询:使用 FuzzyQuery 方法进行模糊匹配,支持通配符 *
模糊查询示例
// 查询名字以"John"开头的所有文档
results := db.FuzzyQuery("name", "John*")

// 查询邮箱包含"example"的所有文档
results := db.FuzzyQuery("email", "*example*")
范围查询示例
// 查询年龄在25到30之间的文档
results := db.RangeQuery("age", 25, 30)

// 查询特定日期范围内的文档
startDate := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
endDate := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
results := db.RangeQuery("date", startDate, endDate)

并发控制

jsonDB 使用多种机制确保并发安全,包括使用 sync.Map、读写锁、原子操作等。

持久化和恢复

数据持久化通过数据文件和 WAL (Write-Ahead Log) 实现,确保数据的一致性和可恢复性。

日志系统

jsonDB 提供了可配置的日志系统,支持不同的日志级别和自定义输出。

性能优化

jsonDB 采用了多种性能优化策略,包括使用 MessagePack 进行序列化、实现索引加速查询、使用工作池控制并发等。

使用示例

test.go提供了一个完整的使用示例,展示了如何创建数据库、插入文档、查询数据等操作。

注意事项和限制

  1. jsonDB 主要设计用于嵌入式应用和中小型数据集。
  2. 所有数据都存储在内存中,因此数据库大小受到可用内存的限制。
  3. 不支持复杂的查询操作,如范围查询或全文搜索。
  4. 不支持事务操作。
  5. 恢复大型数据库可能需要较长时间。

未来改进方向

  1. 实现更复杂的查询功能。
  2. 添加事务支持。
  3. 实现数据压缩以减少磁盘使用。
  4. 支持分布式部署和数据分片。
  5. 添加数据备份和恢复功能。
  6. 实现更高效的索引结构,如B树或LSM树。
  7. 提供HTTP API接口,便于其他语言调用。

Documentation

Index

Constants

View Source
const (
	// 数据库文件名
	DataFileName = "data.db"
	WALFileName  = "wal.log"

	// 文件权限
	DBDirPerm  = 0755
	DBFilePerm = 0644

	// 操作类型
	OperationInsert = "INSERT"
	OperationUpdate = "UPDATE"
	OperationDelete = "DELETE"

	// 文件打开模式
	FileOpenModeRW  = os.O_RDWR | os.O_CREATE
	FileOpenModeWAL = os.O_RDWR | os.O_CREATE | os.O_TRUNC
)

Variables

This section is empty.

Functions

This section is empty.

Types

type CompositeIndex

type CompositeIndex struct {
	// contains filtered or unexported fields
}

CompositeIndex 结构体定义了复合索引

type Database

type Database struct {
	// contains filtered or unexported fields
}

Database 结构体定义了数据库的核心结构

func NewDatabase

func NewDatabase(primaryKey, dbPath string, numWorkers int) (*Database, error)

NewDatabase 创建一个新的数据库实例

func (*Database) Close

func (db *Database) Close() error

Close 关闭数据库,确保所有写操作完成并关闭文件句柄

func (*Database) Count

func (db *Database) Count() int64

Count 返回数据库中的文档总数

func (*Database) CreateCompositeIndex

func (db *Database) CreateCompositeIndex(fields []string)

CreateCompositeIndex 方法用于创建复合索引

介绍: CreateCompositeIndex 是一个高级索引创建方法,用于为多个字段的组合创建索引。复合索引在需要 同时满足多个条件的查询中特别有用,如"查找特定年龄范围内且工资超过某个值的员工"。

复合索引的工作原理是将多个字段的值组合成一个唯一的键。这允许数据库在一次查找中匹配多个条件, 大大提高了复杂查询的效率,尤其是在大型数据集上。

这个方法不仅为新文档创建复合索引,还会遍历所有现有文档并为它们建立索引。这确保了索引的完整性, 但在大型数据集上可能会是一个耗时的操作。

需要注意的是,复合索引的字段顺序很重要。查询时必须使用相同的字段顺序才能利用到这个索引。

参数: - fields: 一个字符串切片,包含要创建复合索引的字段名

注意: 这个方法没有返回值,但会在日志中记录索引创建的结果

func (*Database) CreateIndex

func (db *Database) CreateIndex(field string)

CreateIndex 方法用于创建单字段索引

介绍: CreateIndex 是一个关键的数据库操作,用于为指定的字段创建索引。索引是提高查询性能的重要机制, 特别是在大型数据集上。通过创建索引,数据库可以快速定位满足特定条件的文档,而无需扫描整个集合。

这个方法不仅为新文档创建索引,还会遍历现有的所有文档并为它们建立索引。这确保了索引的完整性, 但也意味着在大型数据集上创建索引可能是一个耗时的操作。

索引的实现使用了 sync.Map 来存储索引数据,这提供了良好的并发性能。此外,还使用了 Trie 数据 结构来支持模糊查询,这对于文本搜索等场景非常有用。

需要注意的是,虽然索引可以显著提升读取性能,但会略微降低写入性能,因为每次插入或更新操作都 需要维护索引。因此,应该只为经常在查询中使用的字段创建索引。

参数: - field: 要创建索引的字段名

注意: 这个方法没有返回值,但会在日志中记录索引创建的结果

func (*Database) Delete

func (db *Database) Delete(id string) error

Delete 方法用于从数据库中删除指定ID的文档

介绍: Delete 方法是一个关键的数据操作函数,用于从数据库中移除特定的文档。 这个方法不仅从内存中删除文档,还会更新相关的索引,并记录删除操作到WAL(Write-Ahead Log)中。

实现细节: 1. 使用 sync.Map 的 LoadAndDelete 方法原子性地删除文档。 2. 更新所有相关索引以保持数据一致性。 3. 使用 WAL 记录删除操作,确保数据持久性和可恢复性。 4. 使用原子操作更新文档计数,保证并发安全。

参数: - id: 要删除的文档的唯一标识符

返回值: - error: 如果删除过程中发生错误,返回相应的错误信息;如果删除成功或文档不存在,返回 nil

func (*Database) FuzzyQuery

func (db *Database) FuzzyQuery(field, pattern string) []map[string]interface{}

FuzzyQuery 执行模糊查询 field: 要查询的字段名 pattern: 查询模式,支持 '*' 作为通配符 返回匹配的文档列表

func (*Database) Get

func (db *Database) Get(id string) (map[string]interface{}, bool)

Get 方法用于从数据库中获取指定ID的文档

介绍: Get 方法是一个基本的数据检索函数,用于根据文档ID获取完整的文档内容。 这个方法直接从内存中读取数据,因此速度很快,但同时也确保了并发安全。

实现细节: 1. 使用 sync.Map 的 Load 方法安全地获取文档。 2. 使用读锁确保在读取过程中文档不会被修改。 3. 返回文档的直接引用,而不是副本,以提高性能。但这要求调用者不应修改返回的数据。

参数: - id: 要获取的文档的唯一标识符

返回值: - map[string]interface{}: 如果文档存在,返回文档内容 - bool: 表示文档是否存在

func (*Database) GetAll

func (db *Database) GetAll() []map[string]interface{}

GetAll 方法用于获取数据库中的所有文档

介绍: GetAll 是一个全量查询方法,它返回数据库中存储的所有文档。 这个方法对于需要处理或分析整个数据集的场景非常有用,比如数据导出、全局统计或批量操作。

实现细节: 1. 该方法使用 sync.Map 的 Range 方法遍历所有存储的文档。 2. 为了保证并发安全,在访问每个文档时都会使用读锁。 3. 方法会创建每个文档的深拷贝,以防止在返回后对原始数据的意外修改。 4. 使用日志记录操作的开始和结束,包括获取的文档总数,有助于监控和调试。

性能考虑: 对于大型数据库,这个方法可能会消耗大量内存和时间。在处理大量数据时, 应考虑使用分页或流式处理的替代方法。

返回值: - []map[string]interface{}: 包含所有文档的切片,每个文档表示为一个 map

func (*Database) Insert

func (db *Database) Insert(docData interface{}) error

Insert 方法用于向数据库中插入新文档

介绍: Insert 是 jsonDB 的核心方法之一,用于将新文档添加到数据库中。该方法支持两种输入格式: 1. map[string]interface{} 类型的文档数据 2. JSON 格式的字符串

该方法执行以下主要步骤: - 解析和验证输入数据 - 检查文档的唯一性(基于主键) - 将操作记录到 WAL(Write-Ahead Log) - 将文档存储在内存中 - 更新所有相关索引 - 异步将文档写入持久化存储

Insert 方法在整个过程中都采取了必要的并发控制措施,确保了数据的一致性和完整性。 同时,该方法还实现了详细的日志记录,有助于监控和调试。

参数: - docData: 要插入的文档数据,可以是 map[string]interface{} 或 JSON 字符串

返回值: - error: 如果插入过程中发生错误,将返回相应的错误信息;如果插入成功,则返回 nil

func (*Database) LogLevelOff

func (db *Database) LogLevelOff()

LogLevelOff 关闭日志

func (*Database) PrintIndexContent

func (db *Database) PrintIndexContent(field string)

PrintIndexContent 方法用于打印指定字段的索引内容

介绍: PrintIndexContent 是一个调试和诊断工具,用于可视化展示数据库中特定字段的索引结构。 这个方法对于理解索引的内部结构、验证索引的正确性,以及调试查询性能问题非常有用。

该方法会遍历指定字段的整个索引结构,并以层级形式打印出每个索引键及其对应的文档ID。 这样可以清晰地看到索引是如何组织数据的,以及哪些文档与特定的索引值相关联。

注意,这个方法主要用于开发和调试目的。在生产环境中,特别是对于大型索引,应谨慎使用, 因为它可能会产生大量的输出并消耗较多的资源。

参数: - field: 要打印索引内容的字段名

注意: 这个方法没有返回值,所有的输出都通过日志系统记录

func (*Database) Query

func (db *Database) Query(field string, value interface{}) []map[string]interface{}

Query 方法用于在数据库中查询符合特定条件的文档

介绍: Query 方法是 jsonDB 的核心查询功能,它允许用户根据指定的字段和值在数据库中搜索匹配的文档。 该方法支持两种查询模式: 1. 索引查询: 如果查询的字段已建立索引,则使用索引进行快速查询 2. 全表扫描: 如果查询的字段没有索引,则遍历所有文档进行匹配

该方法在查询过程中考虑了并发安全性,使用了适当的锁机制来保护数据访问。 为了处理可能的类型不匹配问题(例如整数和浮点数的比较),该方法使用 toFloat64 函数将值转换为统一的浮点数类型进行比较。

参数: - field: 要查询的字段名 - value: 要匹配的值

返回值: - []map[string]interface{}: 包含所有匹配文档的切片,每个文档表示为一个 map

func (*Database) QueryComposite

func (db *Database) QueryComposite(fields []string, values []interface{}) []map[string]interface{}

QueryComposite 方法用于根据复合索引查询文档

介绍: QueryComposite 是一个高效的查询方法,专门用于处理多字段组合查询。它利用预先创建的复合索引来 快速定位符合多个条件的文档,而无需遍历整个数据集。这种方法特别适用于需要同时满足多个条件的 查询场景,如"查找年龄在25-30之间且工资超过50000的员工"。

复合索引的工作原理是将多个字段的值组合成一个唯一的键,这样可以在一次查找中匹配多个条件。 这种方法大大提高了查询效率,尤其是在大型数据集上执行复杂查询时。

使用复合索引查询时,字段的顺序很重要,必须与创建索引时的顺序一致。这是因为索引键是按照 字段顺序生成的。

参数: - fields: 一个字符串切片,包含要查询的字段名,顺序必须与创建复合索引时的顺序一致 - values: 一个接口切片,包含与fields对应的查询值,顺序必须与fields一致

返回值: - []map[string]interface{}: 包含所有匹配文档的切片,每个文档表示为一个map

func (*Database) RangeQuery

func (db *Database) RangeQuery(field string, min, max interface{}) []map[string]interface{}

RangeQuery 执行范围查询,返回字段值在指定范围内的所有文档

介绍: RangeQuery 是一个强大的查询功能,允许用户在指定字段上执行范围搜索。 它支持各种数据类型,包括整数、浮点数和日期时间。 该函数首先检查是否存在相关的索引。如果存在,它会利用索引进行快速查询; 否则,它会执行全表扫描。

实现细节: - 使用了 toComparableValue 函数将输入值转换为可比较的类型 - 利用 compareValues 函数进行值的比较,确保不同类型间的正确比较 - 对于索引查询和全表扫描,使用相同的比较逻辑确保结果一致性 - 使用读写锁保证并发安全 - 通过日志记录查询过程,便于调试和性能分析

参数: - field: 要查询的字段名 - min: 范围的最小值 - max: 范围的最大值

返回值: - []map[string]interface{}: 包含所有匹配文档的切片

func (*Database) SetLogLevel

func (db *Database) SetLogLevel(level LogLevel)

SetLogLevel 设置日志级别

func (*Database) SetLogOutput

func (db *Database) SetLogOutput(output io.Writer)

SetLogOutput 设置日志输出

func (*Database) Update

func (db *Database) Update(id string, updates map[string]interface{}) error

Update 方法用于更新数据库中指定ID的文档

介绍: Update 方法是一个关键的数据操作函数,用于修改数据库中已存在的文档。 这个方法实现了原子性更新,确保在高并发环境下的数据一致性。 它不仅更新内存中的文档,还会更新相关的索引,并记录更新操作到WAL(Write-Ahead Log)中。

实现细节: 1. 使用乐观锁策略(Compare-and-Swap)来处理并发更新。 2. 创建文档的新版本,而不是直接修改原文档,以支持原子性更新。 3. 更新所有相关索引以保持数据一致性。 4. 使用WAL记录更新操作,确保数据持久性和可恢复性。 5. 异步写入数据文件,提高性能。

参数: - id: 要更新的文档的唯一标识符 - updates: 包含要更新的字段和其新值的映射

返回值: - error: 如果更新过程中发生错误,返回相应的错误信息;如果更新成功,返回nil

type DefaultLogger

type DefaultLogger struct {
	// contains filtered or unexported fields
}

DefaultLogger 是默认的日志实现 它封装了标准库的 log.Logger,并添加了日志级别控制

func NewDefaultLogger

func NewDefaultLogger() *DefaultLogger

NewDefaultLogger 创建一个新的默认日志器 它初始化日志级别为 Info,并将输出设置为标准输出

func (*DefaultLogger) Debug

func (l *DefaultLogger) Debug(v ...interface{})

Debug 记录调试级别的日志

func (*DefaultLogger) Error

func (l *DefaultLogger) Error(v ...interface{})

Error 记录错误级别的日志

func (*DefaultLogger) Info

func (l *DefaultLogger) Info(v ...interface{})

Info 记录信息级别的日志

func (*DefaultLogger) SetLevel

func (l *DefaultLogger) SetLevel(level LogLevel)

SetLevel 设置日志记录的级别 这允许在运行时动态调整日志的详细程度

func (*DefaultLogger) SetOutput

func (l *DefaultLogger) SetOutput(output io.Writer)

SetOutput 设置日志输出的目标 这允许将日志重定向到不同的输出,如文件或网络流

func (*DefaultLogger) Warn

func (l *DefaultLogger) Warn(v ...interface{})

Warn 记录警告级别的日志

type Document

type Document struct {
	// contains filtered or unexported fields
}

Document 结构体表示数据库中的一个文档

type DocumentData

type DocumentData map[string]interface{}

DocumentData 关联 map[string]interface{}

type Index

type Index struct {
	// contains filtered or unexported fields
}

Index 结构体定义了单字段索引

type LogLevel

type LogLevel int

LogLevel 定义日志级别的枚举类型

const (
	// LogLevelOff 表示完全关闭日志
	LogLevelOff LogLevel = iota
	// LogLevelError 只记录错误日志
	LogLevelError
	// LogLevelWarn 记录警告和错误日志
	LogLevelWarn
	// LogLevelInfo 记录信息、警告和错误日志
	LogLevelInfo
	// LogLevelDebug 记录所有级别的日志,包括调试信息
	LogLevelDebug
)

type Logger

type Logger interface {
	// Info 记录一般信息日志
	Info(v ...interface{})
	// Warn 记录警告日志
	Warn(v ...interface{})
	// Error 记录错误日志
	Error(v ...interface{})
	// Debug 记录调试信息
	Debug(v ...interface{})
	// SetLevel 设置日志记录的级别
	SetLevel(level LogLevel)
	// SetOutput 设置日志输出的目标
	SetOutput(output io.Writer)
}

Logger 接口定义了日志系统应该实现的方法 这个接口允许我们在将来轻松地替换日志实现,而不影响其他代码

type Trie

type Trie struct {
	// contains filtered or unexported fields
}

Trie 结构体表示整个Trie树

func NewTrie

func NewTrie() *Trie

NewTrie 函数创建并返回一个新的Trie实例

func (*Trie) FuzzySearch

func (t *Trie) FuzzySearch(pattern string) *sync.Map

FuzzySearch 在 Trie 中执行模糊搜索

func (*Trie) Insert

func (t *Trie) Insert(word string, docID string)

Insert 方法向Trie中插入一个单词和对应的文档ID word: 要插入的单词 docID: 与该单词关联的文档ID

func (*Trie) Remove

func (t *Trie) Remove(word string, docID string)

Remove 方法从Trie中删除一个单词和对应的文档ID word: 要删除的单词 docID: 要删除的文档ID

func (*Trie) Search

func (t *Trie) Search(pattern string) *sync.Map

Search 方法在Trie中搜索匹配给定模式的所有文档ID pattern: 搜索模式,可以包含通配符(*) 返回一个sync.Map,包含所有匹配的文档ID

type TrieNode

type TrieNode struct {
	// contains filtered or unexported fields
}

TrieNode 结构体表示Trie中的一个节点

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL