dbx

package module
v0.0.0-...-855a56e Latest Latest
Warning

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

Go to latest
Published: May 13, 2021 License: MIT Imports: 18 Imported by: 0

README

dbx: A database library for MySQL/SQLite/Cassandra/Scylladb by golang.

What is dbx?

dbx = DB + Cache

It is a golang database library that supports transparent caching of all table data. When the memory is large enough, caching services such as Memcached and Redis are no longer needed.

Moreover, the speed of reading cache is quite fast, and the local test QPS reaches 3.5 million + / seconds, which can effectively simplify the application-side business logic code.

It supports MySQL/Sqlite3 and free nesting of structures.

Its implementation principle is to automatically scan the table structure, determine the primary key and self-adding column, and cache data according to the row by the primary key, manage the cache transparently according to the row, the upper layer only needs to operate according to the ordinary ORM style API.

Supporting caching, high performance read KV cached full table data

After a simple test (with small data), Sqlite3 can be queried directly at a speed of 3w+/s, and 350 w+/s after opening the cache is much faster than Redis (because it has no network IO). It then supports caching (generally for small tables, ensuring that memory can be opened when it can be put down)

db.Bind("user", &User{}, true)
db.Bind("group", &Group{}, true)
db.EnableCache(true)

Support nesting, avoid inefficient reflection

Golang is a static language. Reflections are often used to implement more complex functions, but they slow down severely when they are not used properly. Practice has found that we should try our best to use digital index instead of string index, such as Field () performance is about 50 times that of FieldByName (). Most DB libraries do not support nesting because reflection is slow and complex, especially when there are too many nested layers. Fortunately, through hard work, DBX effectively implements the nesting support of unlimited layers, and the performance is good.

type Human struct {
	Age int64     `db:"age"`
}
type User struct {
	Human
	Uid        int64     `db:"uid"`
	Gid        int64     `db:"gid"`
	Name       string    `db:"name"`
	CreateDate time.Time `db:"createDate"`
}

API overview

by golang's reflective feature, it can achieve the convenience close to script language level. As follows:


// Open database
db, err = dbx.Open("mysql", "root@tcp(localhost)/test?parseTime=true&charset=utf8")

// insert one
db.Table("user").Insert(u1)

// find one
db.Table("user").Where("uid=?", 1).One(&user)

// find one by primary key
db.Table("user").WherePK(1).One(u2)

// update one by primary key
db.Table("user").Update(u2)

// delete one by primary key
db.Table("user").WherePK(1).Delete()

// find multi rows
db.Table("user").Where("uid>?", 1).All(&userList)

// find multi rows by IN()
db.Table("user").Where("uid IN(?)", []int{1, 2, 3}).All(&userList)

Log output to specified stream

Free redirection of log data streams.

// output error information generated by DB to standard output (console)
db.Stderr = os.Stdout

// output the error information generated by DB to the specified file
db.Stderr = dbx.OpenFile("./db_error.log")

// default: redirect the output of DB (mainly SQL statements) to "black hole" (no information such as executed SQL statements is output)
db.Stdout = ioutil.Discard

// default: Output from DB (mainly SQL statements) to standard output (console)
db.Stdout = os.Stdout

Compatible with native methods

Sometimes we need to call the native interface to achieve more complex purposes.

// customize complex SQL to get a single result (native)
var uid int64
err = db.QueryRow("SELECT uid FROM user WHERE uid=?", 2).Scan(&uid)
if err != nil {
	panic(err)
}
fmt.Printf("uid: %v\n", uid)
db.Table("user").LoadCache() // Customization requires manual refresh of the cache

Cassandra/Scylladb Use case

db, err = dbx.Open("cql", "root@tcp(192.168.0.129:9042)/btc")
dbx.Check(err)
defer db.Close()
more: example/test_cql/main.go

MySQL/SQLite Use case

package main

import (
	"github.com/xiuno/dbx"
	"fmt"
	"os"
	"time"
)

type User struct {
	Uid        int64     `db:"uid"`
	Gid        int64     `db:"gid"`
	Name       string    `db:"name"`
	CreateDate time.Time `db:"createDate"`
}

func main() {

	var err error
	var db *dbx.DB

	// db, err = dbx.Open("sqlite3", "./db1.db?cache=shared&mode=rwc&parseTime=true&charset=utf8") // sqlite3
	db, err = dbx.Open("mysql", "root@tcp(localhost)/test?parseTime=true&charset=utf8")            // mysql
	dbx.Check(err)
	defer db.Close()

	// output to
	db.Stdout = os.Stdout // output sql to os.Stdout
	db.Stderr = dbx.OpenFile("./db_error.log") // output error to specified file
	// db.Stdout = ioutil.Discard // default: output sql to black hole

	// argument setting:
	db.SetMaxIdleConns(10)
	db.SetMaxOpenConns(10)
	// db.SetConnMaxLifetime(time.Second * 5)

	// create table
	_, err = db.Exec(`DROP TABLE IF EXISTS user;`)
	_, err = db.Exec(`CREATE TABLE user(
		uid        INT(11) PRIMARY KEY AUTO_INCREMENT,
		gid        INT(11) NOT NULL DEFAULT '0',
		name       VARCHAR(255)     DEFAULT '',
		createDate DATETIME         DEFAULT CURRENT_TIMESTAMP
		);
	`)
	dbx.Check(err)

	// enable cache, optional, usually only for small tables to enable the cache, more than 10W rows, not recommended to open!
	db.Bind("user", &User{}, true)
	db.EnableCache(true)

	// insert one
	u1 := &User{1, 1, "jack", time.Now()}
	_, err = db.Table("user").Insert(u1)
	dbx.Check(err)

	// read one
	u2 := &User{}
	err = db.Table("user").WherePK(1).One(u2)
	dbx.Check(err)
	fmt.Printf("%+v\n", u2)

	// read one, check exists
	err = db.Table("user").WherePK(1).One(u2)
	dbx.Check(err)
	if dbx.NoRows(err) {
		panic("not found.")
	}
	fmt.Printf("%+v\n", u2)

	// update one
	u2.Name = "jack.ma"
	_, err = db.Table("user").Update(u2)
	dbx.Check(err)

	// delete one
	_, err = db.Table("user").WherePK(1).Delete()
	dbx.Check(err)

	// Where condition + update
	_, err = db.Table("user").WhereM(dbx.M{{"uid", 1}, {"gid", 1}}).UpdateM(dbx.M{{"Name", "jet.li"}})
	dbx.Check(err)

	// insert multi
	for i := int64(0); i < 5; i++ {
		u := &User{
			Uid: i,
			Gid: i,
			Name: fmt.Sprintf("name-%v", i),
			CreateDate: time.Now(),
		}
		_, err := db.Table("user").Insert(u)
		dbx.Check(err)
	}

	// fetch multi
	userList := []*User{}
	err = db.Table("user").Where("uid>?", 1).All(&userList)
	dbx.Check(err)
	for _, u := range userList {
		fmt.Printf("%+v\n", u)
	}

	// multi update
	_, err = db.Table("user").Where("uid>?", 3).UpdateM(dbx.M{{"gid", 10}})
	dbx.Check(err)

	// multi delete
	_, err = db.Table("user").Where("uid>?", 3).Delete()
	dbx.Check(err)

	// count()
	n, err := db.Table("user").Where("uid>?", -1).Count()
	dbx.Check(err)
	fmt.Printf("count: %v\n", n)

	// sum()
	n, err = db.Table("user").Where("uid>?", -1).Sum("uid")
	dbx.Check(err)
	fmt.Printf("sum(uid): %v\n", n)

	// max()
	n, err = db.Table("user").Where("uid>?", -1).Max("uid")
	dbx.Check(err)
	fmt.Printf("max(uid): %v\n", n)

	// min()
	n, err = db.Table("user").Where("uid>?", -1).Min("uid")
	dbx.Check(err)
	fmt.Printf("min(uid): %v\n", n)

	// Customize complex SQL to get a single result (native)
	var uid int64
	err = db.QueryRow("SELECT uid FROM user WHERE uid=?", 2).Scan(&uid)
	dbx.Check(err)
	fmt.Printf("uid: %v\n", uid)

	// db.Table("user").LoadCache() // Customization requires manual refresh of the cache

	// Customize complex SQL to get multiple (native)
	var name string
	rows, err := db.Query("SELECT `uid`, `name` FROM `user` WHERE 1 ORDER BY uid DESC")
	dbx.Check(err)
	defer rows.Close()
	for rows.Next() {
		rows.Scan(&uid, &name)
		fmt.Printf("uid: %v, name: %v\n", uid, name)
	}
	db.Table("user").LoadCache() // Customization requires manual refresh of the cache

	return
}

中文文档

Documentation

Index

Constants

View Source
const (
	ACTION_SELECT_ONE int = iota
	ACTION_SELECT_ALL
	ACTION_UPDATE
	ACTION_UPDATE_M
	ACTION_DELETE
	ACTION_INSERT
	ACTION_INSERT_IGNORE
	ACTION_REPLACE
	ACTION_COUNT
	ACTION_SUM
)

sql action:

View Source
const (
	DRIVER_MYSQL int = iota
	DRIVER_SQLITE
	DRIVER_CQL
)
View Source
const KEY_SEP string = "-"

Variables

View Source
var ErrNoRows = sql.ErrNoRows

Functions

func Check

func Check(err error)

func IndexN

func IndexN(str string, c byte, n int) int

查找字符 c 出现在 str 中第 n 次出现的位置(偏移量)

func IsDup

func IsDup(err error) bool

func NewCQLSession

func NewCQLSession(hosts []string, keySpace string) (*gocql.Session, error)

func NoRows

func NoRows(err error) bool

func Now

func Now() string

func OpenFile

func OpenFile(filePath string) *os.File

Types

type Col

type Col struct {
	ColName     string // 列名: id
	FieldName   string // 结构体中的名字:Id
	FieldPos    []int  // 在结构体中的位置,支持嵌套 [1,0,-1,-1,-1]
	FieldStruct reflect.StructField
}

type ColFieldMap

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

func NewColFieldMap

func NewColFieldMap() *ColFieldMap

func (*ColFieldMap) Add

func (c *ColFieldMap) Add(col *Col)

func (*ColFieldMap) Exists

func (c *ColFieldMap) Exists(key string) bool

func (*ColFieldMap) GetByColName

func (c *ColFieldMap) GetByColName(colName string) *Col

func (*ColFieldMap) GetByFieldName

func (c *ColFieldMap) GetByFieldName(fieldName string) *Col

type DB

type DB struct {
	*sql.DB

	CQLSession *gocql.Session
	CQLMeta    *gocql.KeyspaceMetadata
	DriverType int
	DbName     string
	Stdout     io.Writer
	Stderr     io.Writer
	// contains filtered or unexported fields
}

func Open

func Open(driverName string, dataSourceNames ...string) (*DB, error)

func (*DB) Bind

func (db *DB) Bind(tableName string, ifc interface{}, enableCache bool)

ifc 如果不是指针类型,则 new 出指针类型,方便使用 type / &type

func (*DB) Close

func (db *DB) Close() error

func (*DB) DebugCache

func (db *DB) DebugCache()

func (*DB) EnableCache

func (db *DB) EnableCache(b bool)

func (*DB) ErrorLog

func (db *DB) ErrorLog(s string, args ...interface{})

func (*DB) ErrorSQL

func (db *DB) ErrorSQL(s string, sql1 string, args ...interface{})

func (*DB) Exec

func (db *DB) Exec(sql1 string, args ...interface{}) (n int64, err error)

func (*DB) LoadCache

func (db *DB) LoadCache()

func (*DB) Log

func (db *DB) Log(s string, args ...interface{})

func (*DB) LogSQL

func (db *DB) LogSQL(s string, args ...interface{})

func (*DB) Panic

func (db *DB) Panic(s string, args ...interface{})

func (*DB) SetReadOnly

func (db *DB) SetReadOnly(b bool)

func (*DB) Table

func (db *DB) Table(name string) *Query

type M

type M []Map

type Map

type Map struct {
	Key   string
	Value interface{}
}

type Query

type Query struct {
	*DB
	// contains filtered or unexported fields
}

func (*Query) All

func (q *Query) All(arrListIfc interface{}) (err error)

func (*Query) AllFromCache

func (q *Query) AllFromCache() *syncmap.Map

func (*Query) And

func (q *Query) And(str string, args ...interface{}) *Query

func (*Query) Bind

func (q *Query) Bind(ifc interface{}, enableCache bool)

func (*Query) CQLQuery

func (q *Query) CQLQuery(sql1 string, args ...interface{}) (rows *gocql.Iter, err error)

func (*Query) Count

func (q *Query) Count() (n int64, err error)

Cassandra Count() 可能会超时,可以通过 COPY tablename TO '/dev/null' 来查看行数

func (*Query) Delete

func (q *Query) Delete() (n int64, err error)

ispk, cacheok iscql

func (*Query) Fields

func (q *Query) Fields(fields ...string) *Query

func (*Query) Insert

func (q *Query) Insert(ifc interface{}) (insertId int64, err error)

ifc 最好为 &struct

func (*Query) InsertIgnore

func (q *Query) InsertIgnore(ifc interface{}) (insertId int64, err error)

ifc 最好为 &struct

func (*Query) Limit

func (q *Query) Limit(limitStart int64, limitEnds ...int64) *Query

Limit(10) Limit(0, 10) // Cassandra 不支持 // Cassandra 只能"下一页"的方式进行翻页,默认按照主键排序 SELECT * FROM user WHERE token(uid)>token(300) LIMIT 1; SELECT * FROM user WHERE token(id) > token(xx-xx-xx-xx-xx) AND regdate >= '2019-01-01' LIMIT 10 ALLOW FILTERING;

func (*Query) LoadCache

func (q *Query) LoadCache()

func (*Query) Max

func (q *Query) Max(colName string) (n int64, err error)

针对某一列

func (*Query) Min

func (q *Query) Min(colName string) (n int64, err error)

针对某一列

func (*Query) One

func (q *Query) One(arrIfc interface{}) (err error)

func (*Query) Or

func (q *Query) Or(str string, args ...interface{}) *Query

func (*Query) QueryRowScanX

func (q *Query) QueryRowScanX(sql1 string, args ...interface{}) (n int64, err error)

func (*Query) Replace

func (q *Query) Replace(ifc interface{}) (insertId int64, err error)

ifc 最好为 &struct

func (*Query) SQLQuery

func (q *Query) SQLQuery(sql1 string, args ...interface{}) (rows *sql.Rows, err error)

func (*Query) Sort

func (q *Query) Sort(colName string, order int) *Query

func (*Query) SortM

func (q *Query) SortM(m M) *Query

func (*Query) Sum

func (q *Query) Sum(colName string) (n int64, err error)

针对某一列

func (*Query) Truncate

func (q *Query) Truncate() (err error)

func (*Query) Update

func (q *Query) Update(ifc interface{}) (affectedRows int64, err error)

根据主键更新一条数据

func (*Query) UpdateM

func (q *Query) UpdateM(m M) (affectedRows int64, err error)

func (*Query) Where

func (q *Query) Where(str string, args ...interface{}) *Query

db.Table("table1").Where("id IN (?)", ids).One(&row)

func (*Query) WhereM

func (q *Query) WhereM(m M) *Query

func (*Query) WherePK

func (q *Query) WherePK(args ...interface{}) *Query

type TableStruct

type TableStruct struct {
	ColFieldMap   *ColFieldMap
	PrimaryKey    []string
	PrimaryKeyPos [][]int
	AutoIncrement string
	Type          reflect.Type
	EnableCache   bool
}

func NewTableStruct

func NewTableStruct(db *DB, tableName string, pointerType reflect.Type) *TableStruct

pointerType 必须为约定值 &struct

Directories

Path Synopsis
example
lib

Jump to

Keyboard shortcuts

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