{"id":13411858,"url":"https://github.com/tidwall/buntdb","last_synced_at":"2025-05-12T22:45:20.467Z","repository":{"id":37420040,"uuid":"63731386","full_name":"tidwall/buntdb","owner":"tidwall","description":"BuntDB is an embeddable, in-memory key/value database for Go with custom indexing and geospatial support","archived":false,"fork":false,"pushed_at":"2024-09-10T03:42:04.000Z","size":418,"stargazers_count":4694,"open_issues_count":32,"forks_count":298,"subscribers_count":108,"default_branch":"master","last_synced_at":"2025-05-12T22:45:06.073Z","etag":null,"topics":["database","geospatial","golang","in-memory","key-value"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tidwall.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-07-19T22:11:40.000Z","updated_at":"2025-05-11T14:54:18.000Z","dependencies_parsed_at":"2022-07-09T15:01:41.824Z","dependency_job_id":"e9c4ab17-91ec-4720-9f72-35c5e39cdb9c","html_url":"https://github.com/tidwall/buntdb","commit_stats":{"total_commits":147,"total_committers":15,"mean_commits":9.8,"dds":"0.12925170068027214","last_synced_commit":"4ac2e321b1d80fb568b4c96d336b667373f10450"},"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tidwall%2Fbuntdb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tidwall%2Fbuntdb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tidwall%2Fbuntdb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tidwall%2Fbuntdb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tidwall","download_url":"https://codeload.github.com/tidwall/buntdb/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253837387,"owners_count":21971981,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database","geospatial","golang","in-memory","key-value"],"created_at":"2024-07-30T20:01:17.685Z","updated_at":"2025-05-12T22:45:20.415Z","avatar_url":"https://github.com/tidwall.png","language":"Go","readme":"\u003cp align=\"center\"\u003e\n\u003cimg\n    src=\"logo.png\"\n    width=\"307\" height=\"150\" border=\"0\" alt=\"BuntDB\"\u003e\n\u003cbr\u003e\n\u003ca href=\"https://godoc.org/github.com/tidwall/buntdb\"\u003e\u003cimg src=\"https://img.shields.io/badge/go-documentation-blue.svg?style=flat-square\" alt=\"Godoc\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/tidwall/buntdb/blob/master/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/tidwall/buntdb.svg?style=flat-square\" alt=\"LICENSE\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nBuntDB is a low-level, in-memory, key/value store in pure Go.\nIt persists to disk, is ACID compliant, and uses locking for multiple\nreaders and a single writer. It supports custom indexes and geospatial\ndata. It's ideal for projects that need a dependable database and favor\nspeed over data size.\n\nFeatures\n========\n\n- In-memory database for [fast reads and writes](#performance)\n- Embeddable with a [simple API](https://godoc.org/github.com/tidwall/buntdb)\n- [Spatial indexing](#spatial-indexes) for up to 20 dimensions; Useful for Geospatial data\n- Index fields inside [JSON](#json-indexes) documents\n- [Collate i18n Indexes](#collate-i18n-indexes) using the optional [collate package](https://github.com/tidwall/collate)\n- Create [custom indexes](#custom-indexes) for any data type\n- Support for [multi value indexes](#multi-value-index); Similar to a SQL multi column index\n- [Built-in types](#built-in-types) that are easy to get up \u0026 running; String, Uint, Int, Float\n- Flexible [iteration](#iterating) of data; ascending, descending, and ranges\n- [Durable append-only file](#append-only-file) format for persistence\n- Option to evict old items with an [expiration](#data-expiration) TTL\n- ACID semantics with locking [transactions](#transactions) that support rollbacks\n\n\nGetting Started\n===============\n\n## Installing\n\nTo start using BuntDB, install Go and run `go get`:\n\n```sh\n$ go get -u github.com/tidwall/buntdb\n```\n\nThis will retrieve the library.\n\n\n## Opening a database\n\nThe primary object in BuntDB is a `DB`. To open or create your\ndatabase, use the `buntdb.Open()` function:\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\n\t\"github.com/tidwall/buntdb\"\n)\n\nfunc main() {\n\t// Open the data.db file. It will be created if it doesn't exist.\n\tdb, err := buntdb.Open(\"data.db\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tdefer db.Close()\n\n\t...\n}\n```\n\nIt's also possible to open a database that does not persist to disk by using `:memory:` as the path of the file.\n\n```go\nbuntdb.Open(\":memory:\") // Open a file that does not persist to disk.\n```\n\n## Transactions\nAll reads and writes must be performed from inside a transaction. BuntDB can have one write transaction opened at a time, but can have many concurrent read transactions. Each transaction maintains a stable view of the database. In other words, once a transaction has begun, the data for that transaction cannot be changed by other transactions.\n\nTransactions run in a function that exposes a `Tx` object, which represents the transaction state. While inside a transaction, all database operations should be performed using this object. You should never access the origin `DB` object while inside a transaction. Doing so may have side-effects, such as blocking your application.\n\nWhen a transaction fails, it will roll back, and revert all changes that occurred to the database during that transaction. There's a single return value that you can use to close the transaction. For read/write transactions, returning an error this way will force the transaction to roll back. When a read/write transaction succeeds all changes are persisted to disk.\n\n### Read-only Transactions\nA read-only transaction should be used when you don't need to make changes to the data. The advantage of a read-only transaction is that there can be many running concurrently.\n\n```go\nerr := db.View(func(tx *buntdb.Tx) error {\n\t...\n\treturn nil\n})\n```\n\n### Read/write Transactions\nA read/write transaction is used when you need to make changes to your data. There can only be one read/write transaction running at a time. So make sure you close it as soon as you are done with it.\n\n```go\nerr := db.Update(func(tx *buntdb.Tx) error {\n\t...\n\treturn nil\n})\n```\n\n## Setting and getting key/values\n\nTo set a value you must open a read/write transaction:\n\n```go\nerr := db.Update(func(tx *buntdb.Tx) error {\n\t_, _, err := tx.Set(\"mykey\", \"myvalue\", nil)\n\treturn err\n})\n```\n\n\nTo get the value:\n\n```go\nerr := db.View(func(tx *buntdb.Tx) error {\n\tval, err := tx.Get(\"mykey\")\n\tif err != nil{\n\t\treturn err\n\t}\n\tfmt.Printf(\"value is %s\\n\", val)\n\treturn nil\n})\n```\n\nGetting non-existent values will cause an `ErrNotFound` error.\n\n### Iterating\nAll keys/value pairs are ordered in the database by the key. To iterate over the keys:\n\n```go\nerr := db.View(func(tx *buntdb.Tx) error {\n\terr := tx.Ascend(\"\", func(key, value string) bool {\n\t\tfmt.Printf(\"key: %s, value: %s\\n\", key, value)\n\t\treturn true // continue iteration\n\t})\n\treturn err\n})\n```\n\nThere is also `AscendGreaterOrEqual`, `AscendLessThan`, `AscendRange`, `AscendEqual`, `Descend`, `DescendLessOrEqual`, `DescendGreaterThan`, `DescendRange`, and `DescendEqual`. Please see the [documentation](https://godoc.org/github.com/tidwall/buntdb) for more information on these functions.\n\n\n## Custom Indexes\nInitially all data is stored in a single [B-tree](https://en.wikipedia.org/wiki/B-tree) with each item having one key and one value. All of these items are ordered by the key. This is great for quickly getting a value from a key or [iterating](#iterating) over the keys. Feel free to peruse the [B-tree implementation](https://github.com/tidwall/btree).\n\nYou can also create custom indexes that allow for ordering and [iterating](#iterating) over values. A custom index also uses a B-tree, but it's more flexible because it allows for custom ordering.\n\nFor example, let's say you want to create an index for ordering names:\n\n```go\ndb.CreateIndex(\"names\", \"*\", buntdb.IndexString)\n```\n\nThis will create an index named `names` which stores and sorts all values. The second parameter is a pattern that is used to filter on keys. A `*` wildcard argument means that we want to accept all keys. `IndexString` is a built-in function that performs case-insensitive ordering on the values\n\nNow you can add various names:\n\n```go\ndb.Update(func(tx *buntdb.Tx) error {\n\ttx.Set(\"user:0:name\", \"tom\", nil)\n\ttx.Set(\"user:1:name\", \"Randi\", nil)\n\ttx.Set(\"user:2:name\", \"jane\", nil)\n\ttx.Set(\"user:4:name\", \"Janet\", nil)\n\ttx.Set(\"user:5:name\", \"Paula\", nil)\n\ttx.Set(\"user:6:name\", \"peter\", nil)\n\ttx.Set(\"user:7:name\", \"Terri\", nil)\n\treturn nil\n})\n```\n\nFinally you can iterate over the index:\n\n```go\ndb.View(func(tx *buntdb.Tx) error {\n\ttx.Ascend(\"names\", func(key, val string) bool {\n\tfmt.Printf(buf, \"%s %s\\n\", key, val)\n\t\treturn true\n\t})\n\treturn nil\n})\n```\nThe output should be:\n```\nuser:2:name jane\nuser:4:name Janet\nuser:5:name Paula\nuser:6:name peter\nuser:1:name Randi\nuser:7:name Terri\nuser:0:name tom\n```\n\nThe pattern parameter can be used to filter on keys like this:\n\n```go\ndb.CreateIndex(\"names\", \"user:*\", buntdb.IndexString)\n```\n\nNow only items with keys that have the prefix `user:` will be added to the `names` index.\n\n\n### Built-in types\nAlong with `IndexString`, there is also `IndexInt`, `IndexUint`, and `IndexFloat`.\nThese are built-in types for indexing. You can choose to use these or create your own.\n\nSo to create an index that is numerically ordered on an age key, we could use:\n\n```go\ndb.CreateIndex(\"ages\", \"user:*:age\", buntdb.IndexInt)\n```\n\nAnd then add values:\n\n```go\ndb.Update(func(tx *buntdb.Tx) error {\n\ttx.Set(\"user:0:age\", \"35\", nil)\n\ttx.Set(\"user:1:age\", \"49\", nil)\n\ttx.Set(\"user:2:age\", \"13\", nil)\n\ttx.Set(\"user:4:age\", \"63\", nil)\n\ttx.Set(\"user:5:age\", \"8\", nil)\n\ttx.Set(\"user:6:age\", \"3\", nil)\n\ttx.Set(\"user:7:age\", \"16\", nil)\n\treturn nil\n})\n```\n\n```go\ndb.View(func(tx *buntdb.Tx) error {\n\ttx.Ascend(\"ages\", func(key, val string) bool {\n\tfmt.Printf(buf, \"%s %s\\n\", key, val)\n\t\treturn true\n\t})\n\treturn nil\n})\n```\n\nThe output should be:\n```\nuser:6:age 3\nuser:5:age 8\nuser:2:age 13\nuser:7:age 16\nuser:0:age 35\nuser:1:age 49\nuser:4:age 63\n```\n\n## Spatial Indexes\nBuntDB has support for spatial indexes by storing rectangles in an [R-tree](https://en.wikipedia.org/wiki/R-tree). An R-tree is organized in a similar manner as a [B-tree](https://en.wikipedia.org/wiki/B-tree), and both are balanced trees. But, an R-tree is special because it can operate on data that is in multiple dimensions. This is super handy for Geospatial applications.\n\nTo create a spatial index use the `CreateSpatialIndex` function:\n\n```go\ndb.CreateSpatialIndex(\"fleet\", \"fleet:*:pos\", buntdb.IndexRect)\n```\n\nThen `IndexRect` is a built-in function that converts rect strings to a format that the R-tree can use. It's easy to use this function out of the box, but you might find it better to create a custom one that renders from a different format, such as [Well-known text](https://en.wikipedia.org/wiki/Well-known_text) or [GeoJSON](http://geojson.org/).\n\nTo add some lon,lat points to the `fleet` index:\n\n```go\ndb.Update(func(tx *buntdb.Tx) error {\n\ttx.Set(\"fleet:0:pos\", \"[-115.567 33.532]\", nil)\n\ttx.Set(\"fleet:1:pos\", \"[-116.671 35.735]\", nil)\n\ttx.Set(\"fleet:2:pos\", \"[-113.902 31.234]\", nil)\n\treturn nil\n})\n```\n\nAnd then you can run the `Intersects` function on the index:\n\n```go\ndb.View(func(tx *buntdb.Tx) error {\n\ttx.Intersects(\"fleet\", \"[-117 30],[-112 36]\", func(key, val string) bool {\n\t\t...\n\t\treturn true\n\t})\n\treturn nil\n})\n```\n\nThis will get all three positions.\n\n### k-Nearest Neighbors\n\nUse the `Nearby` function to get all the positions in order of nearest to farthest :\n\n```go\ndb.View(func(tx *buntdb.Tx) error {\n\ttx.Nearby(\"fleet\", \"[-113 33]\", func(key, val string, dist float64) bool {\n\t\t...\n\t\treturn true\n\t})\n\treturn nil\n})\n```\n\n### Spatial bracket syntax\n\nThe bracket syntax `[-117 30],[-112 36]` is unique to BuntDB, and it's how the built-in rectangles are processed. But, you are not limited to this syntax. Whatever Rect function you choose to use during `CreateSpatialIndex` will be used to process the parameter, in this case it's `IndexRect`.\n\n- **2D rectangle:** `[10 15],[20 25]`\n*Min XY: \"10x15\", Max XY: \"20x25\"*\n\n- **3D rectangle:** `[10 15 12],[20 25 18]`\n*Min XYZ: \"10x15x12\", Max XYZ: \"20x25x18\"*\n\n- **2D point:** `[10 15]`\n*XY: \"10x15\"*\n\n- **LonLat point:** `[-112.2693 33.5123]`\n*LatLon: \"33.5123 -112.2693\"*\n\n- **LonLat bounding box:** `[-112.26 33.51],[-112.18 33.67]`\n*Min LatLon: \"33.51 -112.26\", Max LatLon: \"33.67 -112.18\"*\n\n**Notice:** The longitude is the Y axis and is on the left, and latitude is the X axis and is on the right.\n\nYou can also represent `Infinity` by using `-inf` and `+inf`.\nFor example, you might have the following points (`[X Y M]` where XY is a point and M is a timestamp):\n```\n[3 9 1]\n[3 8 2]\n[4 8 3]\n[4 7 4]\n[5 7 5]\n[5 6 6]\n```\n\nYou can then do a search for all points with `M` between 2-4 by calling `Intersects`.\n\n```go\ntx.Intersects(\"points\", \"[-inf -inf 2],[+inf +inf 4]\", func(key, val string) bool {\n\tprintln(val)\n\treturn true\n})\n```\n\nWhich will return:\n\n```\n[3 8 2]\n[4 8 3]\n[4 7 4]\n```\n\n## JSON Indexes\nIndexes can be created on individual fields inside JSON documents. BuntDB uses [GJSON](https://github.com/tidwall/gjson) under the hood.\n\nFor example:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/tidwall/buntdb\"\n)\n\nfunc main() {\n\tdb, _ := buntdb.Open(\":memory:\")\n\tdb.CreateIndex(\"last_name\", \"*\", buntdb.IndexJSON(\"name.last\"))\n\tdb.CreateIndex(\"age\", \"*\", buntdb.IndexJSON(\"age\"))\n\tdb.Update(func(tx *buntdb.Tx) error {\n\t\ttx.Set(\"1\", `{\"name\":{\"first\":\"Tom\",\"last\":\"Johnson\"},\"age\":38}`, nil)\n\t\ttx.Set(\"2\", `{\"name\":{\"first\":\"Janet\",\"last\":\"Prichard\"},\"age\":47}`, nil)\n\t\ttx.Set(\"3\", `{\"name\":{\"first\":\"Carol\",\"last\":\"Anderson\"},\"age\":52}`, nil)\n\t\ttx.Set(\"4\", `{\"name\":{\"first\":\"Alan\",\"last\":\"Cooper\"},\"age\":28}`, nil)\n\t\treturn nil\n\t})\n\tdb.View(func(tx *buntdb.Tx) error {\n\t\tfmt.Println(\"Order by last name\")\n\t\ttx.Ascend(\"last_name\", func(key, value string) bool {\n\t\t\tfmt.Printf(\"%s: %s\\n\", key, value)\n\t\t\treturn true\n\t\t})\n\t\tfmt.Println(\"Order by age\")\n\t\ttx.Ascend(\"age\", func(key, value string) bool {\n\t\t\tfmt.Printf(\"%s: %s\\n\", key, value)\n\t\t\treturn true\n\t\t})\n\t\tfmt.Println(\"Order by age range 30-50\")\n\t\ttx.AscendRange(\"age\", `{\"age\":30}`, `{\"age\":50}`, func(key, value string) bool {\n\t\t\tfmt.Printf(\"%s: %s\\n\", key, value)\n\t\t\treturn true\n\t\t})\n\t\treturn nil\n\t})\n}\n```\n\nResults:\n\n```\nOrder by last name\n3: {\"name\":{\"first\":\"Carol\",\"last\":\"Anderson\"},\"age\":52}\n4: {\"name\":{\"first\":\"Alan\",\"last\":\"Cooper\"},\"age\":28}\n1: {\"name\":{\"first\":\"Tom\",\"last\":\"Johnson\"},\"age\":38}\n2: {\"name\":{\"first\":\"Janet\",\"last\":\"Prichard\"},\"age\":47}\n\nOrder by age\n4: {\"name\":{\"first\":\"Alan\",\"last\":\"Cooper\"},\"age\":28}\n1: {\"name\":{\"first\":\"Tom\",\"last\":\"Johnson\"},\"age\":38}\n2: {\"name\":{\"first\":\"Janet\",\"last\":\"Prichard\"},\"age\":47}\n3: {\"name\":{\"first\":\"Carol\",\"last\":\"Anderson\"},\"age\":52}\n\nOrder by age range 30-50\n1: {\"name\":{\"first\":\"Tom\",\"last\":\"Johnson\"},\"age\":38}\n2: {\"name\":{\"first\":\"Janet\",\"last\":\"Prichard\"},\"age\":47}\n```\n\n## Multi Value Index\nWith BuntDB it's possible to join multiple values on a single index.\nThis is similar to a [multi column index](http://dev.mysql.com/doc/refman/5.7/en/multiple-column-indexes.html) in a traditional SQL database.\n\nIn this example we are creating a multi value index on \"name.last\" and \"age\":\n\n```go\ndb, _ := buntdb.Open(\":memory:\")\ndb.CreateIndex(\"last_name_age\", \"*\", buntdb.IndexJSON(\"name.last\"), buntdb.IndexJSON(\"age\"))\ndb.Update(func(tx *buntdb.Tx) error {\n\ttx.Set(\"1\", `{\"name\":{\"first\":\"Tom\",\"last\":\"Johnson\"},\"age\":38}`, nil)\n\ttx.Set(\"2\", `{\"name\":{\"first\":\"Janet\",\"last\":\"Prichard\"},\"age\":47}`, nil)\n\ttx.Set(\"3\", `{\"name\":{\"first\":\"Carol\",\"last\":\"Anderson\"},\"age\":52}`, nil)\n\ttx.Set(\"4\", `{\"name\":{\"first\":\"Alan\",\"last\":\"Cooper\"},\"age\":28}`, nil)\n\ttx.Set(\"5\", `{\"name\":{\"first\":\"Sam\",\"last\":\"Anderson\"},\"age\":51}`, nil)\n\ttx.Set(\"6\", `{\"name\":{\"first\":\"Melinda\",\"last\":\"Prichard\"},\"age\":44}`, nil)\n\treturn nil\n})\ndb.View(func(tx *buntdb.Tx) error {\n\ttx.Ascend(\"last_name_age\", func(key, value string) bool {\n\t\tfmt.Printf(\"%s: %s\\n\", key, value)\n\t\treturn true\n\t})\n\treturn nil\n})\n\n// Output:\n// 5: {\"name\":{\"first\":\"Sam\",\"last\":\"Anderson\"},\"age\":51}\n// 3: {\"name\":{\"first\":\"Carol\",\"last\":\"Anderson\"},\"age\":52}\n// 4: {\"name\":{\"first\":\"Alan\",\"last\":\"Cooper\"},\"age\":28}\n// 1: {\"name\":{\"first\":\"Tom\",\"last\":\"Johnson\"},\"age\":38}\n// 6: {\"name\":{\"first\":\"Melinda\",\"last\":\"Prichard\"},\"age\":44}\n// 2: {\"name\":{\"first\":\"Janet\",\"last\":\"Prichard\"},\"age\":47}\n```\n\n## Descending Ordered Index\nAny index can be put in descending order by wrapping it's less function with `buntdb.Desc`.\n\n```go\ndb.CreateIndex(\"last_name_age\", \"*\",\n    buntdb.IndexJSON(\"name.last\"),\n    buntdb.Desc(buntdb.IndexJSON(\"age\")),\n)\n```\n\nThis will create a multi value index where the last name is ascending and the age is descending.\n\n## Collate i18n Indexes\n\nUsing the external [collate package](https://github.com/tidwall/collate) it's possible to create\nindexes that are sorted by the specified language. This is similar to the [SQL COLLATE keyword](https://msdn.microsoft.com/en-us/library/ms174596.aspx) found in traditional databases.\n\nTo install:\n\n```\ngo get -u github.com/tidwall/collate\n```\n\nFor example:\n\n```go\nimport \"github.com/tidwall/collate\"\n\n// To sort case-insensitive in French.\ndb.CreateIndex(\"name\", \"*\", collate.IndexString(\"FRENCH_CI\"))\n\n// To specify that numbers should sort numerically (\"2\" \u003c \"12\")\n// and use a comma to represent a decimal point.\ndb.CreateIndex(\"amount\", \"*\", collate.IndexString(\"FRENCH_NUM\"))\n```\n\nThere's also support for Collation on JSON indexes:\n\n```go\ndb.CreateIndex(\"last_name\", \"*\", collate.IndexJSON(\"CHINESE_CI\", \"name.last\"))\n```\n\nCheck out the [collate project](https://github.com/tidwall/collate) for more information.\n\n## Data Expiration\nItems can be automatically evicted by using the `SetOptions` object in the `Set` function to set a `TTL`.\n\n```go\ndb.Update(func(tx *buntdb.Tx) error {\n\ttx.Set(\"mykey\", \"myval\", \u0026buntdb.SetOptions{Expires:true, TTL:time.Second})\n\treturn nil\n})\n```\n\nNow `mykey` will automatically be deleted after one second. You can remove the TTL by setting the value again with the same key/value, but with the options parameter set to nil.\n\n## Delete while iterating\nBuntDB does not currently support deleting a key while in the process of iterating.\nAs a workaround you'll need to delete keys following the completion of the iterator.\n\n```go\nvar delkeys []string\ntx.AscendKeys(\"object:*\", func(k, v string) bool {\n\tif someCondition(k) == true {\n\t\tdelkeys = append(delkeys, k)\n\t}\n\treturn true // continue\n})\nfor _, k := range delkeys {\n\tif _, err = tx.Delete(k); err != nil {\n\t\treturn err\n\t}\n}\n```\n\n## Append-only File\n\nBuntDB uses an AOF (append-only file) which is a log of all database changes that occur from operations like `Set()` and `Delete()`.\n\nThe format of this file looks like:\n```\nset key:1 value1\nset key:2 value2\nset key:1 value3\ndel key:2\n...\n```\n\nWhen the database opens again, it will read back the aof file and process each command in exact order.\nThis read process happens one time when the database opens.\nFrom there on the file is only appended.\n\nAs you may guess this log file can grow large over time.\nThere's a background routine that automatically shrinks the log file when it gets too large.\nThere is also a `Shrink()` function which will rewrite the aof file so that it contains only the items in the database.\nThe shrink operation does not lock up the database so read and write transactions can continue while shrinking is in process.\n\n### Durability and fsync\n\nBy default BuntDB executes an `fsync` once every second on the [aof file](#append-only-file). Which simply means that there's a chance that up to one second of data might be lost. If you need higher durability then there's an optional database config setting `Config.SyncPolicy` which can be set to `Always`.\n\nThe `Config.SyncPolicy` has the following options:\n\n- `Never` - fsync is managed by the operating system, less safe\n- `EverySecond` - fsync every second, fast and safer, this is the default\n- `Always` - fsync after every write, very durable, slower\n\n## Config\n\nHere are some configuration options that can be use to change various behaviors of the database.\n\n- **SyncPolicy** adjusts how often the data is synced to disk. This value can be Never, EverySecond, or Always. Default is EverySecond.\n- **AutoShrinkPercentage** is used by the background process to trigger a shrink of the aof file when the size of the file is larger than the percentage of the result of the previous shrunk file. For example, if this value is 100, and the last shrink process resulted in a 100mb file, then the new aof file must be 200mb before a shrink is triggered. Default is 100.\n- **AutoShrinkMinSize** defines the minimum size of the aof file before an automatic shrink can occur. Default is 32MB.\n- **AutoShrinkDisabled** turns off automatic background shrinking. Default is false.\n\nTo update the configuration you should call `ReadConfig` followed by `SetConfig`. For example:\n\n```go\n\nvar config buntdb.Config\nif err := db.ReadConfig(\u0026config); err != nil{\n\tlog.Fatal(err)\n}\nif err := db.SetConfig(config); err != nil{\n\tlog.Fatal(err)\n}\n```\n\n## Performance\n\nHow fast is BuntDB?\n\nHere are some example [benchmarks](https://github.com/tidwall/raft-buntdb#raftstore-performance-comparison) when using BuntDB in a Raft Store implementation.\n\nYou can also run the standard Go benchmark tool from the project root directory:\n\n```\ngo test --bench=.\n```\n\n### BuntDB-Benchmark\n\nThere's a [custom utility](https://github.com/tidwall/buntdb-benchmark) that was created specifically for benchmarking BuntDB.\n\n*These are the results from running the benchmarks on a MacBook Pro 15\" 2.8 GHz Intel Core i7:*\n\n```\n$ buntdb-benchmark -q\nGET: 4609604.74 operations per second\nSET: 248500.33 operations per second\nASCEND_100: 2268998.79 operations per second\nASCEND_200: 1178388.14 operations per second\nASCEND_400: 679134.20 operations per second\nASCEND_800: 348445.55 operations per second\nDESCEND_100: 2313821.69 operations per second\nDESCEND_200: 1292738.38 operations per second\nDESCEND_400: 675258.76 operations per second\nDESCEND_800: 337481.67 operations per second\nSPATIAL_SET: 134824.60 operations per second\nSPATIAL_INTERSECTS_100: 939491.47 operations per second\nSPATIAL_INTERSECTS_200: 561590.40 operations per second\nSPATIAL_INTERSECTS_400: 306951.15 operations per second\nSPATIAL_INTERSECTS_800: 159673.91 operations per second\n```\n\nTo install this utility:\n\n```\ngo get github.com/tidwall/buntdb-benchmark\n```\n\n\n\n## Contact\nJosh Baker [@tidwall](http://twitter.com/tidwall)\n\n## License\n\nBuntDB source code is available under the MIT [License](/LICENSE).\n","funding_links":[],"categories":["Database","Go","开源类库","Misc","Databases","数据库","數據庫","Key/Value Databases","Open source library","Key-value Data Model","Geospatial Library","Generators","golang","\u003cspan id=\"数据库-database\"\u003e数据库 Database\u003c/span\u003e","Uncategorized","Data Integration Frameworks","数据库  `go语言实现的数据库`"],"sub_categories":["Advanced Console UIs","数据库","Databases Implemented in Go","Key-Value Databases","高级控制台界面","高級控制台界面","Go中实现的数据库","Database","Go","\u003cspan id=\"高级控制台用户界面-advanced-console-uis\"\u003e高级控制台用户界面 Advanced Console UIs\u003c/span\u003e","标准 CLI"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftidwall%2Fbuntdb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftidwall%2Fbuntdb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftidwall%2Fbuntdb/lists"}