sqliter

package module
v0.0.0-...-4282750 Latest Latest
Warning

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

Go to latest
Published: Dec 12, 2024 License: MIT Imports: 26 Imported by: 0

README

sqliter

  1. 一表一库:sqlite 依然按照一表一库文件(保证写性能)
  2. 按月拆分:数据库文件按照自然月来拆分,例如 (metric.t.cpu.202408.db, metric.t.cpu.202407.db)
  3. 读写分离:查询可以并发,写入控制单线程(读写使用不用的数据库句柄)
  4. 回收策略:不再使用 vacuum 回收空间(会产生磁盘IO突刺),直接删除过期的库文件
  5. 按月查询:UI 查询时间范围限制在一个自然月范围内
  6. 宽松保留:保留打点数据天数和保留打点文件最大大小,不从当前月库文件中删除数据
  7. 破坏删除: 对于由于断电等原因造成的库文件破坏,直接删除重建

示例图片

~/rigaga> ls rigaga.db* | sort-by -r modified                08/13/2024 06:00:06 PM
╭────┬─────────────────────────────────────────┬──────┬───────────┬────────────────╮
│  # │                  name                   │ type │   size    │    modified    │
├────┼─────────────────────────────────────────┼──────┼───────────┼────────────────┤
│  0 │ rigaga.db.processes.month.202408.db-wal │ file │   3.9 MiB │ now            │
│  1 │ rigaga.db.system.month.202408.db-wal    │ file │   3.9 MiB │ now            │
│  2 │ rigaga.db.net.month.202408.db-wal       │ file │   4.0 MiB │ now            │
│  3 │ rigaga.db.disk.month.202408.db-wal      │ file │   4.0 MiB │ now            │
│  4 │ rigaga.db.diskio.month.202408.db-wal    │ file │   4.0 MiB │ now            │
│  5 │ rigaga.db.mem.month.202408.db-wal       │ file │   4.0 MiB │ now            │
│  6 │ rigaga.db.cpu.month.202408.db-wal       │ file │   4.0 MiB │ 24 seconds ago │
│  7 │ rigaga.db.mem.month.202408.db           │ file │ 648.0 KiB │ 2 minutes ago  │
│  8 │ rigaga.db.processes.month.202408.db     │ file │ 460.0 KiB │ 3 minutes ago  │
│  9 │ rigaga.db.net.month.202408.db           │ file │   6.3 MiB │ 4 minutes ago  │
│ 10 │ rigaga.db.diskio.month.202408.db        │ file │   1.1 MiB │ 4 minutes ago  │
│ 11 │ rigaga.db.disk.month.202408.db          │ file │   4.4 MiB │ 6 minutes ago  │
│ 12 │ rigaga.db.cpu.month.202408.db           │ file │ 768.0 KiB │ 22 minutes ago │
│ 13 │ rigaga.db.system.month.202408.db        │ file │ 532.0 KiB │ 34 minutes ago │
│ 14 │ rigaga.db.cpu.month.202408.db-shm       │ file │  32.0 KiB │ 14 hours ago   │
│ 15 │ rigaga.db.processes.month.202408.db-shm │ file │  32.0 KiB │ 14 hours ago   │
│ 16 │ rigaga.db.mem.month.202408.db-shm       │ file │  32.0 KiB │ 14 hours ago   │
│ 17 │ rigaga.db.diskio.month.202408.db-shm    │ file │  32.0 KiB │ 14 hours ago   │
│ 18 │ rigaga.db.net.month.202408.db-shm       │ file │  32.0 KiB │ 14 hours ago   │
│ 19 │ rigaga.db.disk.month.202408.db-shm      │ file │  32.0 KiB │ 14 hours ago   │
│ 20 │ rigaga.db.system.month.202408.db-shm    │ file │  32.0 KiB │ 14 hours ago   │
╰────┴─────────────────────────────────────────┴──────┴───────────┴────────────────╯

注: 上述 ls 使用 nushell 执行。

test

$ go clean -testcache
$ go test ./...      
?   	github.com/bingoohuang/ngg/sqliter/cmd/sqliter	[no test files]
ok  	github.com/bingoohuang/ngg/sqliter	8.981s
ok  	github.com/bingoohuang/ngg/sqliter/influx	0.011s
$ 

资料

———— 避免过大的WAL文件

在正常情况下,新内容会追加到 WAL 文件中,直到 WAL 文件累积到大约 1000 页(因此大小约为 4MB),此时会自动运行检查点并回收 WAL 文件。检查点通常不会截断 WAL 文件(除非设置了 journal_size_limit pragma )。 相反,它只会导致 SQLite 从头开始​​覆盖 WAL 文件。这样做是因为覆盖现有文件通常比追加速度更快。当与数据库的最后一个连接关闭时,该连接会执行最后一个检查点,然后删除 WAL 及其关联的共享内存文件,以清理磁盘。

因此在绝大多数情况下,应用程序根本不需要担心 WAL 文件。 SQLite 会自动处理它。但有可能让 SQLite 进入WAL文件无限制增长的状态,导致磁盘空间使用过多和查询速度缓慢。以下要点列举了发生这种情况的一些方式以及如何避免它们。

Documentation

Index

Constants

View Source
const (
	// DefaultMaxIdle 默认最大数据库读写空闲时间,超期关闭数据库
	DefaultMaxIdle = 5 * time.Minute
	// DefaultBatchInterval 批量执行时间间隔
	DefaultBatchInterval = 10 * time.Second
	// DefaultBatchSize 批次大小
	DefaultBatchSize = 50

	// DefaultRecycleInterval 回收周期间隔
	DefaultRecycleInterval = 24 * time.Hour
)
View Source
const (
	DefaultKeySeqName = "keys.boltdb"
)

Variables

View Source
var (
	// DefaultTimeSeriesKeep 默认保留打点数据
	DefaultTimeSeriesKeep = TimeSpan{Value: 1, Unit: UnitMonth} // 1个月
)
View Source
var (
	ErrBadTimeSpan = errors.New("bad TimeSpan expr")
)
View Source
var (
	// ErrNotFound is returned when an entry is not found.
	ErrNotFound = errors.New("not found")
)
View Source
var ErrUnknownDividedString = errors.New("unknown divided string")

ErrUnknownDividedString 分割时间模式字符串无法识别

Functions

func CreateAlterTable

func CreateAlterTable(ti *tableMeta, metric influx.Metric, seq *BoltSeq, asTags Matcher) []string

CreateAlterTable 根据指标,生成添加字段、创建索引等 SQL 语句

func CreateCreateTable

func CreateCreateTable(metric influx.Metric, seq *BoltSeq, asTags Matcher) (tableColumns, scripts []string)

CreateCreateTable 创建 建表 SQL, 索引 SQL

func MultiInsertBinds

func MultiInsertBinds(columnsNum, rowsNum int) string

func OnConflictGen

func OnConflictGen(tags map[string]bool) func(columns []string) string

OnConflictGen 生成 on conflict 子语句函数

func ParseTimeWindow

func ParseTimeWindow(timeWindow string) (window time.Duration, err error)

func QuoteSlice

func QuoteSlice(ss []string) []string

func Tick

func Tick[T any](interval, jitter time.Duration, ch chan T, chItemFn func(T) error, tickFn func() error) error

func ToDBFieldValue

func ToDBFieldValue(x any) any

Types

type BoltSeq

type BoltSeq struct {
	DB     *bolt.DB
	Bucket []byte
}

func CreateSeqKeysDB

func CreateSeqKeysDB(prefix, seqKeysDBName string, seqKeysDB *BoltSeq) (*BoltSeq, string, error)

CreateSeqKeysDB 创建 keys 字符串转换为枚举数字的 boltdb 库名字(可以包括路径),默认 keyseq.bolt 为 sqlite 的 tag 字符串值生成唯一的对应序号(减少sqlite数据库文件大小而优化设计)

prefix 设定库文件的前缀(包括完整路径)
seqKeysDBName 库名,  "off" 表示不使用, "" 使用 DefaultKeySeqName
seqKeysDB 外部已经提前创建好的库,在 seqKeysDBName != "off" 时优先使用

func NewBoltSeq

func NewBoltSeq(name, bucket string) (*BoltSeq, error)

func (*BoltSeq) Close

func (c *BoltSeq) Close() error

func (*BoltSeq) Find

func (c *BoltSeq) Find(seq string) (key string, err error)

func (*BoltSeq) Get

func (c *BoltSeq) Get(key string) (seq uint64, err error)

func (*BoltSeq) Next

func (c *BoltSeq) Next(key string) (seq uint64, err error)

func (*BoltSeq) WalkSeq

func (c *BoltSeq) WalkSeq(walker func(seq uint64, str string)) (err error)

type Config

type Config struct {
	// DriverName 驱动名称, 例如 sqlite3
	DriverName string
	// Prefix 设定库文件的前缀(包括完整路径)
	Prefix string
	// WriteDsnOptions 连接字符串选项,比如 _journal=WAL
	WriteDsnOptions string
	// ReadDsnOptions 连接字符串选项,比如 _txlock=immediate
	ReadDsnOptions string
	// BadTableSubs 非法表名子串
	BadTableSubs []string

	// AllowDBErrors 允许的DB错误字眼,否则被认为库文件损坏
	AllowDBErrors []string
	// MaxIdle 最大数据库读写空闲时间,超期关闭数据库
	MaxIdle time.Duration

	// BatchInsertInterval 批量插入时间间隔
	BatchInsertInterval time.Duration
	// BatchInsertSize 批量插入大小
	BatchInsertSize int

	// SeqKeysDBName keys 字符串转换为枚举数字的 boltdb 库名字(可以包括路径),默认 keyseq.bolt
	// 为 sqlite 的 tag 字符串值生成唯一的对应序号(减少sqlite数据库文件大小而优化设计)
	SeqKeysDBName string

	// SeqKeysDB 是 SeqKeysDB 对应的对象
	SeqKeysDB *BoltSeq
	// Debug 是否开启 Debug 模式,打印 SQL 等
	Debug bool

	// AsTags 用来转换普通字段为索引字段的判断器
	AsTags Matcher

	// TimeSeriesKeep 保留打点数据时间, 默认 DefaultTimeSeriesKeep
	TimeSeriesKeep *TimeSpan
	// TimeSeriesMaxSize 保留打点文件最大大小, 默认0表示不限制
	TimeSeriesMaxSize int64

	// RecycleCron 回收时间间隔 Cron 表达式,优先级比 RecycleInterval 高
	// 示例:
	// 午夜: @midnight 或 @daily
	// 每5分钟: @every 5m
	// 每时: @hourly
	// 每周: @weekly
	// 带秒的cron表达式:
	// 每秒: * * * * * ?
	// 每5分钟: 0 5 * * * *", every5min(time.Local)}
	// 每天2点: 0 0 2 * * ?
	// cron 表达式帮助: https://tool.lu/crontab/
	// cron 表达式帮助: https://www.bejson.com/othertools/cron/
	// 代码帮助: https://github.com/robfig/cron/blob/master/parser.go
	RecycleCron string
	// RecycleInterval 回收时间间隔, 默认 DefaultRecycleInterval
	RecycleInterval time.Duration
	// DividedBy 按时间分库模式
	DividedBy
}

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig 创建默认配置

func (*Config) ValidateTable

func (c *Config) ValidateTable(table string) error

ValidateTable 校验表明是否合法

type ConfigFn

type ConfigFn func(*Config)

func WithAllowDBErrors

func WithAllowDBErrors(val []string) ConfigFn

func WithBadTableSubs

func WithBadTableSubs(val []string) ConfigFn

func WithConfig

func WithConfig(val *Config) ConfigFn

func WithDebug

func WithDebug(val bool) ConfigFn

func WithDividedBy

func WithDividedBy(val DividedBy) ConfigFn

func WithDriverName

func WithDriverName(val string) ConfigFn

func WithDsnOptions

func WithDsnOptions(val string) ConfigFn

func WithPrefix

func WithPrefix(val string) ConfigFn

func WithSeqKeysDB

func WithSeqKeysDB(val *BoltSeq) ConfigFn

func WithSeqKeysDBName

func WithSeqKeysDBName(val string) ConfigFn

type DbFile

type DbFile struct {
	// Table 表名, e.g. disk
	Table string
	// DividedBy 时间划分, e.g. month.202407
	DividedBy string

	// 主数据库文件, e.g. testdata/metric.t.disk.month.202407.db
	File File

	// Relatives 关联的文件,主要用于计算数据空间大小
	// e.g. testdata/metric.t.disk.month.202407.db 关联
	//     testdata/metric.t.disk.month.202407.db-shm
	//     testdata/metric.t.disk.month.202407.db-wal
	Relatives []File
}

DbFile 数据库文件对象

func (*DbFile) TotalSize

func (f *DbFile) TotalSize() int64

type DbStat

type DbStat struct {
	// DSN 数据源名字
	DSN string `json:"dsn"`
	// LastVisit 最后访问时间
	LastVisit time.Time `json:"lastVisit"`
	// DividedBy 时间划分字符串
	DividedBy string `json:"dividedBy"`
	// ReadOnly 是否只读
	ReadOnly bool `json:"readOnly"`
}

DbStat 库状态统计

type DebugDB

type DebugDB struct {
	DB *sql.DB

	// DSN 连接字符串
	DSN string

	*Config
	// contains filtered or unexported fields
}

func NewDebugDB

func NewDebugDB(dbFile, dsn string, maxOpenConns int, c *Config, mode ModeOpenDB) (*DebugDB, error)

func (*DebugDB) Close

func (d *DebugDB) Close()

func (*DebugDB) Query

func (d *DebugDB) Query(emptyStruct any, query string, vars ...any) (*sqlrun.Result, error)

Query 执行查询 emptyStruct 是 nil 时, result 中 Rows 格式是 [][]string emptyStruct 非 nil 时, result 中 Rows 格式是 []MyStruct, emptyStruct 传入例如 MyStruct{} 的空结构体实例

type DividedBy

type DividedBy int

DividedBy 分库文件的时间分割模式

const (
	// DividedByMonth 按月
	DividedByMonth DividedBy = iota
	// DividedByWeek 按周
	DividedByWeek
	// DividedByDay 按天
	DividedByDay
)

func ParseDivideString

func ParseDivideString(s string) (DividedBy, error)

ParseDivideString 解析分割时间模式字符串

func (DividedBy) CutoffDays

func (d DividedBy) CutoffDays(t time.Time, timeSpan TimeSpan) string

CutoffDays 根据时间 t, 以及保留天数 days, 计算切断时间点所在的划分时间值(如果等于当前时间划分值,则往前退一个时间划分)

func (DividedBy) DividedPrefix

func (d DividedBy) DividedPrefix() string

DividedPrefix 时间分区的前缀

func (DividedBy) DividedString

func (d DividedBy) DividedString(t time.Time) string

DividedString 生成时间 t 的分割字符串 按月: month.yyyyMM 按周: week.yyyyWW 按天: day.yyyyMMdd

type File

type File struct {
	Path string
	Size int64 // 文件名
}

type FileStat

type FileStat struct {
	Size int64
	Path string
}

func ListFiles

func ListFiles(prefix, suffix string) (files []FileStat, err error)

ListFiles 列表以特定前缀后缀的文件

func RemoveFilesPrefix

func RemoveFilesPrefix(prefix string, debug bool) (removeFiles []FileStat, totalSize int64)

RemoveFilesPrefix 删除以特定前缀开头的文件

type Insert

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

type LastError

type LastError struct {
	Error          error
	ErrorQuery     string
	ErrorQueryType QueryType
}

type Matcher

type Matcher interface {
	Match(string) bool
}

Matcher 匹配接口

type ModeOpenDB

type ModeOpenDB int
const (
	ModeRead ModeOpenDB
	ModeWrite
)

type Prepared

type Prepared struct {
	Debug bool
	// contains filtered or unexported fields
}

Prepared 预备语句

func (*Prepared) Close

func (d *Prepared) Close() error

func (*Prepared) Exec

func (d *Prepared) Exec(args ...any) (sql.Result, error)

type QueryType

type QueryType string
const (
	BatchPrepare QueryType = "batch-prepare"
	BatchExec    QueryType = "batch-exec"
	QueryExec    QueryType = "exec"
)

type RecycleResult

type RecycleResult struct {
	BeforeTime      time.Time `json:"beforeTime"`
	RecycledRecords int64     `json:"recycledRecords"`
	DirSize         int64     `json:"dirSize"`
	Error           string    `json:"error"`
}

type Sqliter

type Sqliter struct {
	*Config
	// contains filtered or unexported fields
}

Sqliter Sqliter 结构体对象

func New

func New(fns ...ConfigFn) (*Sqliter, error)

New 创建 *sqliter 对象

func (*Sqliter) Close

func (q *Sqliter) Close() error

Close 关闭 sqliter 所有操作,包括关闭库文件、退出回收协程等

func (*Sqliter) ListDiskTables

func (q *Sqliter) ListDiskTables() (map[string][]*DbFile, error)

ListDiskTables 列出磁盘上的所有数据库文件

func (*Sqliter) Read

func (q *Sqliter) Read(table, query string, dividedTime time.Time, emptyStruct any, args ...any) (*sqlrun.Result, error)

Read 执行查询 table 表名称 query 查询 SQL dividedTime 查询落在的时间划分(哪个时间分区库上) emptyStruct 从结果集映射到哪个结构体上

func (*Sqliter) Recycle

func (q *Sqliter) Recycle(before string) (rr RecycleResult)

Recycle 手动触发回收 before 设置为空时,按照系统配置的策略执行一次回收, 格式1(绝对时间): RFC3339 "2006-01-02T15:04:05Z07:00" 格式2(偏移间隔): -10d 10天前的此时

func (*Sqliter) StatDbs

func (q *Sqliter) StatDbs() (dbStats []DbStat)

StatDbs 统计读写库

func (*Sqliter) TableFileBase

func (q *Sqliter) TableFileBase(table, dividedBy string) string

TableFileBase 返回表文件的基础文件名前缀,例如: "disk.month.202408.db"

func (*Sqliter) TableFilePath

func (q *Sqliter) TableFilePath(table, dividedBy string) string

TableFilePath 返回表文件的完整前缀,例如: "testdata/metric.t.disk.month.202408.db"

func (*Sqliter) WriteMetric

func (q *Sqliter) WriteMetric(metric influx.Metric) error

WriteMetric 写入指标 此过程,会涉及到建库、建表/索引,或者已有库的修正表及索引

type TableIndexInfo

type TableIndexInfo struct {
	// Tags 表的索引字段
	Tags []string
	// HasPk 是否有主键
	HasPk bool
}

TableIndexInfo 表索引对象

func ParseTableIndexInfo

func ParseTableIndexInfo(db *DebugDB, table string) (*TableIndexInfo, error)

ParseTableIndexInfo 解析表的索引字段

type TimeSpan

type TimeSpan struct {
	Value int
	Unit  TimeSpanUnit
}

func ParseTimeSpan

func ParseTimeSpan(s string) (TimeSpan, error)

func (*TimeSpan) MinusBy

func (t *TimeSpan) MinusBy(c carbon.Carbon) carbon.Carbon

MinusBy 计算 c - t 的结果

func (*TimeSpan) String

func (t *TimeSpan) String() string

type TimeSpanUnit

type TimeSpanUnit int
const (
	UnitMonth TimeSpanUnit = iota
	UnitWeek
	UnitDay
)

func (TimeSpanUnit) Of

func (u TimeSpanUnit) Of(value int) TimeSpan

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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