tiedot:一个go语言实现的文档型数据库

tiedot 是一个go语言实现的文档型数据库,使用JSON存储和查询。可以嵌入你的程序,或者独立运行,使用http的api查询。

特性

  • 两种运行方式,可嵌入程序,或者独立提供服务。
  • 容错的数据结构,首先保证数据安全。
  • 始终不忘记,性能与扩展性。
  • 使用 JSON 语法进行查询。

安装

配置好Go环境后,运行

1
go get github.com/HouzuoGuo/tiedot

入门

使用有2种方式,使用HTTP做接口,适用任何语言;使用嵌入式,使用Go语言,这里介绍使用Go语言,数据库的嵌入模式,足以应付百万请求/天了。

项目自带了演示代码,通过下面的命令执行

1
./tiedot -mode=example

性能评估命令

1
2
./tiedot -mode=bench   # 对40万数据进行增删改查
./tiedot -mode=bench2  # 还没仔细看

项目中,example.go 文件是Go语言的使用示例

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// It is very important to initialize random number generator seed!
	rand.Seed(time.Now().UTC().UnixNano())
	// Create and open database创建并打开数据库
	dir := "/tmp/MyDatabase"
	os.RemoveAll(dir)
	defer os.RemoveAll(dir)

	myDB, err := db.OpenDB(dir)
	if err != nil {
		panic(err)
	}

	// Create two collections Feeds and Votes创建2张表
	// "2" means collection data and indexes are divided into two halves, allowing concurrent access from two threads
	if err := myDB.Create("Feeds", 2); err != nil {
		panic(err)
	}
	if err := myDB.Create("Votes", 2); err != nil {
		panic(err)
	}

	// What collections do I now have?查询都有哪些表
	for name := range myDB.StrCol {
		fmt.Printf("I have a collection called %s\n", name)
	}

	// Rename collection "Votes" to "Points"把表"Votes"重命名为"Points"
	if err := myDB.Rename("Votes", "Points"); err != nil {
		panic(err)
	}

	// Drop (delete) collection "Points"删除表"Points"
	if err := myDB.Drop("Points"); err != nil {
		panic(err)
	}

	// Scrub (repair and compact) "Feeds"修复并压缩表"Feeds"
	myDB.Scrub("Feeds")

	// ****************** Document Management ******************
	// Start using a collection使用表"Feeds"
	feeds := myDB.Use("Feeds")

	// Insert document (document must be map[string]interface{})插入数据
	docID, err := feeds.Insert(map[string]interface{}{
		"name": "Go 1.2 is released",
		"url":  "golang.org"})
	if err != nil {
		panic(err)
	}

	// Read document根据id查询数据
	var readBack interface{}
	feeds.Read(docID, &readBack) // pass in document's physical ID
	fmt.Println(readBack)

	// Update document (document must be map[string]interface{})改数据
	err = feeds.Update(docID, map[string]interface{}{
		"name": "Go is very popular",
		"url":  "google.com"})
	if err != nil {
		panic(err)
	}

	// Delete document删除数据
	feeds.Delete(docID)

	// Delete document
	feeds.Delete(123) // An ID which does not exist does no harm

	// ****************** Index Management ******************索引管理
	// Secondary indexes assist in many types of queries
	// Create index (path leads to document JSON attribute)建索引
	if err := feeds.Index([]string{"author", "name", "first_name"}); err != nil {
		panic(err)
	}
	if err := feeds.Index([]string{"Title"}); err != nil {
		panic(err)
	}
	if err := feeds.Index([]string{"Source"}); err != nil {
		panic(err)
	}

	// What indexes do I have on collection A?查询有哪些索引
	for path := range feeds.SecIndexes {
		fmt.Printf("I have an index on path %s\n", path)
	}

	// Remove index删索引
	if err := feeds.Unindex([]string{"author", "name", "first_name"}); err != nil {
		panic(err)
	}

	// ****************** Queries ******************查询表
	// Let's prepare a number of docments for a start
	feeds.Insert(map[string]interface{}{"Title": "New Go release", "Source": "golang.org", "Age": 3})
	feeds.Insert(map[string]interface{}{"Title": "Kitkat is here", "Source": "google.com", "Age": 2})
	feeds.Insert(map[string]interface{}{"Title": "Good Slackware", "Source": "slackware.com", "Age": 1})

	queryStr := `[{"eq": "New Go release", "in": ["Title"]}, {"eq": "slackware.com", "in": ["Source"]}]`
	var query interface{}
	json.Unmarshal([]byte(queryStr), &query)查询条件

	queryResult := make(map[uint64]struct{}) // query result (document IDs) goes into map keys保存查询结果的变量

	if err := db.EvalQuery(query, feeds, &queryResult); err != nil {执行查询
		panic(err)
	}

	// Query results are physical document IDs打印查询结果
	for id := range queryResult {
		fmt.Printf("Query returned document ID %d\n", id)
	}

	// To use the document itself, simply read it back
	for id := range queryResult {
		feeds.Read(id, &readBack)
		fmt.Printf("Query returned document %v\n", readBack)
	}

	// Gracefully close database关闭数据库
	myDB.Close()
updatedupdated2021-10-222021-10-22