sqliteplus

package module
v0.0.0-...-06445e3 Latest Latest
Warning

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

Go to latest
Published: Aug 21, 2024 License: MIT Imports: 27 Imported by: 0

README

sqliteplus

  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/sqliteplus/cmd/sqliteplus        [no test files]
?       github.com/bingoohuang/sqliteplus/sqlrun        [no test files]
ok      github.com/bingoohuang/sqliteplus       2.438s
$ 

资料

———— 避免过大的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 IndexN

func IndexN(s, sep string, n int) int

IndexN 返回字符串 s 中第 n 个子字符串 sub 的索引。 如果 n 是正数,则从左到右查找第 n 个 sub 的位置。 如果 n 是负数,则从右到左查找第 -n 个 sub 的位置。 如果找不到,返回 -1

func JitterInterval

func JitterInterval(interval, jitter time.Duration) time.Duration

func MultiInsertBinds

func MultiInsertBinds(columnsNum, rowsNum int) string

func OnConflictGen

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

OnConflictGen 生成 on conflict 子语句函数

func ParseTime

func ParseTime(tm string) (t time.Time, err error)

ParseTime 解析时间字符串 格式1(绝对时间): RFC3339 "2006-01-02T15:04:05Z07:00" 格式2(偏移间隔): -10d 10天前的此时

func ParseTimeMilli

func ParseTimeMilli(tm string) (unixMilli int64, err error)

func ParseTimeWindow

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

func QuoteSlice

func QuoteSlice(ss []string) []string

func RemoveFilesPrefix

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

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

func SortMap

func SortMap(m map[string]any) (keys []string, values []any)

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

	// 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 DebugDB

type DebugDB struct {
	DB *sql.DB

	// DSN 连接字符串
	DSN string

	*Config
	// contains filtered or unexported fields
}

func NewDebugDB

func NewDebugDB(dsn string, maxOpenConns int, c *Config) (*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 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 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 SqlitePlus

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

SqlitePlus SqlitePlus 结构体对象

func New

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

New 创建 *SqlitePlus 对象

func (*SqlitePlus) Close

func (q *SqlitePlus) Close() error

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

func (*SqlitePlus) ListDiskTables

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

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

func (*SqlitePlus) Read

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

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

func (*SqlitePlus) Recycle

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

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

func (*SqlitePlus) TableFileBase

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

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

func (*SqlitePlus) TableFilePath

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

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

func (*SqlitePlus) WriteMetric

func (q *SqlitePlus) 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
sqliteplus command

Jump to

Keyboard shortcuts

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