
ReIndexer是具有高级查询构建器接口的可嵌入,内存,面向文档的数据库。
Reindexer的目标是提供复杂的查询快速搜索。我们在Drestam上对Elasticsearch不满意,并创建了ReIndexer作为更具性能的替代方案。
核心用C ++编写,应用级别API在GO中。
该文档描述了GO连接器及其API。要获取有关ReIndexer Server和HTTP API的信息,请参阅ReIndexer文档
有两个LTS-REINDEXER可用:v3.xx和v4.xx
3.xx当前是我们的主流分支,4.xx(释放/4个分支)是Beta-version,具有实验Raft-Cluster和Sharding Support。这些版本之间的存储是兼容的,但是,复制配置是完全不同的。版本3和4正在获得所有相同的错误缀和功能(除了与复制相关的功能外)。
关键功能:
从一开始,性能就一直是我们的重中之重,我们认为我们设法变得相当不错。基准测试表明,ReIndexer的性能与典型的键值数据库相当。在一个CPU核心上,我们得到:
SELECT * FROM items WHERE id='?'SELECT * FROM items WHERE year > 2010 AND name = 'string' AND id IN (....)SELECT * FROM items WHERE year > 2010 AND name = 'string' JOIN subitems ON ...请参阅基准测试结果和更多细节。
Reindexer的目标是消耗尽可能少的内存;大多数查询都是根本没有任何内存分配的处理。
为此,在C ++和GO级别上都采用了几种优化:
文档和索引存储在密集的二进制C ++结构中,因此它们不会对Go的垃圾收集器施加任何负载。
字符串副本合并。
每个文档 +≈4-16字节每个搜索索引的内存开销约为32个字节。
在查询执行后产生的值得序列化文档的GO级别上有一个对象缓存。未来的查询使用预序列化的文档,该文档削减了重复的避难和分配成本
查询接口使用sync.Pool重复使用内部结构和缓冲区。这些技术的组合使Reindexer可以无需分配而处理大多数查询。
Reindexer具有内部全文搜索引擎。全文搜索用法文档和示例在这里
ReIndexer可以将文档存储到disk via leveldb的文档中。文档是在背景中自动将大批批量自动写入存储后端。
创建名称空间时,其所有文档都存储在RAM中,因此这些文档上的查询完全以内存模式运行。
这是基本驯服器用法的完整示例:
package main
// Import package
import (
"fmt"
"math/rand"
"github.com/restream/reindexer/v3"
// choose how the Reindexer binds to the app (in this case "builtin," which means link Reindexer as a static library)
_ "github.com/restream/reindexer/v3/bindings/builtin"
// OR use Reindexer as standalone server and connect to it via TCP or unix domain socket (if available).
// _ "github.com/restream/reindexer/v3/bindings/cproto"
// OR link Reindexer as static library with bundled server.
// _ "github.com/restream/reindexer/v3/bindings/builtinserver"
// "github.com/restream/reindexer/v3/bindings/builtinserver/config"
)
// Define struct with reindex tags. Fields must be exported - private fields can not be written into reindexer
type Item struct {
ID int64 `reindex:"id,,pk"` // 'id' is primary key
Name string `reindex:"name"` // add index by 'name' field
Articles [] int `reindex:"articles"` // add index by articles 'articles' array
Year int `reindex:"year,tree"` // add sortable index by 'year' field
}
func main () {
// Init a database instance and choose the binding (builtin)
db := reindexer . NewReindex ( "builtin:///tmp/reindex/testdb" )
// OR - Init a database instance and choose the binding (connect to server via TCP sockets)
// Database should be created explicitly via reindexer_tool or via WithCreateDBIfMissing option:
// If server security mode is enabled, then username and password are mandatory
// db := reindexer.NewReindex("cproto://user:[email protected]:6534/testdb", reindexer.WithCreateDBIfMissing())
// OR - Init a database instance and choose the binding (connect to server via unix domain sockets)
// Unix domain sockets are available on the unix systems only (socket file has to be explicitly set on the server's side with '--urpcaddr' option)
// Database should be created explicitly via reindexer_tool or via WithCreateDBIfMissing option:
// If server security mode is enabled, then username and password are mandatory
// db := reindexer.NewReindex("ucproto://user:pass@/tmp/reindexer.socket:/testdb", reindexer.WithCreateDBIfMissing())
// OR - Init a database instance and choose the binding (builtin, with bundled server)
// serverConfig := config.DefaultServerConfig ()
// If server security mode is enabled, then username and password are mandatory
// db := reindexer.NewReindex("builtinserver://user:pass@testdb",reindexer.WithServerConfig(100*time.Second, serverConfig))
// Create new namespace with name 'items', which will store structs of type 'Item'
db . OpenNamespace ( "items" , reindexer . DefaultNamespaceOptions (), Item {})
// Generate dataset
for i := 0 ; i < 100000 ; i ++ {
err := db . Upsert ( "items" , & Item {
ID : int64 ( i ),
Name : "Vasya" ,
Articles : [] int { rand . Int () % 100 , rand . Int () % 100 },
Year : 2000 + rand . Int () % 50 ,
})
if err != nil {
panic ( err )
}
}
// Query a single document
elem , found := db . Query ( "items" ).
Where ( "id" , reindexer . EQ , 40 ).
Get ()
if found {
item := elem .( * Item )
fmt . Println ( "Found document:" , * item )
}
// Query multiple documents
query := db . Query ( "items" ).
Sort ( "year" , false ). // Sort results by 'year' field in ascending order
WhereString ( "name" , reindexer . EQ , "Vasya" ). // 'name' must be 'Vasya'
WhereInt ( "year" , reindexer . GT , 2020 ). // 'year' must be greater than 2020
WhereInt ( "articles" , reindexer . SET , 6 , 1 , 8 ). // 'articles' must contain one of [6,1,8]
Limit ( 10 ). // Return maximum 10 documents
Offset ( 0 ). // from 0 position
ReqTotal () // Calculate the total count of matching documents
// Execute the query and return an iterator
iterator := query . Exec ()
// Iterator must be closed
defer iterator . Close ()
fmt . Println ( "Found" , iterator . TotalCount (), "total documents, first" , iterator . Count (), "documents:" )
// Iterate over results
for iterator . Next () {
// Get the next document and cast it to a pointer
elem := iterator . Object ().( * Item )
fmt . Println ( * elem )
}
// Check the error
if err := iterator . Error (); err != nil {
panic ( err )
}
}还有一些C ++的基本示例,然后去此处
作为查询构建器Reindexer的替代方案,提供了SQL兼容查询接口。这是SQL接口用法的示例:
...
iterator := db . ExecSQL ( "SELECT * FROM items WHERE name='Vasya' AND year > 2020 AND articles IN (6,1,8) ORDER BY year LIMIT 10" )
...请注意,查询构建器接口是优选的:它具有更多功能,并且比SQL接口更快
字符串文字应包含在单引号中。
复合索引应以双引号封闭。
SELECT * FROM items WHERE " field1+field2 " = ' Vasya '如果字段名称不以alpha开头,则“ _”或“#”必须以双引号(示例:
UPDATE items DROP " 123 " SELECT * FROM ns WHERE " 123 " = ' some_value ' SELECT * FROM ns WHERE " 123abc " = 123 DELETE FROM ns WHERE " 123abc123 " = 111可以通过默认的SQL语法完成简单的连接:
SELECT * FROM ns INNER JOIN ns2 ON ns2 . id = ns . fk_id WHERE a > 0在左图上与条件的加入必须使用类似子查询的语法:
SELECT * FROM ns WHERE a > 0 AND INNER JOIN ( SELECT * FROM ns2 WHERE b > 10 AND c = 1 ) ON ns2 . id = ns . fk_id子查询也可以成为权限的一部分:
SELECT * FROM ns WHERE ( SELECT * FROM ns2 WHERE id < 10 LIMIT 0 ) IS NOT NULL SELECT * FROM ns WHERE id = ( SELECT id FROM ns2 WHERE id < 10 ) SELECT * FROM ns WHERE ( SELECT COUNT ( * ) FROM ns2 WHERE id < 10 ) > 18 Reindexer可以以3种不同的模式运行:
embedded (builtin) Reindexer嵌入为静态库中,并且不需要单独的服务器进程。embedded with server (builtinserver) ReIndexer的嵌入式嵌入到应用程序中,作为静态库,然后启动服务器。在此模式下,其他客户端可以通过CPROTO,UCPROTO或HTTP连接到应用程序。standalone ReIndexer作为独立服务器运行,应用程序通过网络或UNIX域插座连接到ReIndexer。在此模式下,ReIndexer的Go Binding不取决于Reindexer的静态库。
获取ReIndexer Server的最简单方法是从Dockerhub撤回并运行Docker Image。
docker run -p9088:9088 -p6534:6534 -it reindexer/reindexerDockerfile
ReIndexer的Core用C ++ 17编写,并将LevelDB用作存储后端,因此CMAKE,C ++ 17工具链和LevelDB必须在安装ReIndexer之前安装。
要构建ReIndexer,需要G ++ 8+,Clang 7+或Mingw64。
在这些模式下,ReIndexer的Go绑定取决于ReIndexer的静态库(Core,Server和Resource)。
这种方式建议使用,并且适合大多数情况。
使用Go.mod的GO模块不允许在模块的目录中构建C ++库。 Go Binding将使用PKG-Config检测库的目录。
ReIndexer的库必须通过包装管理器从源或预先构建的软件包安装。
然后获取模块:
go get -a github.com/restream/reindexer/v3如果您需要修改的Reindexer的来源,则可以使用replace 。
# Clone reindexer via git. It's also possible to use 'go get -a github.com/restream/reindexer/v3', but it's behavior may vary depending on Go's version
git clone https://github.com/restream/reindexer.git $GOPATH /src/reindexer
bash $GOPATH /src/reindexer/dependencies.sh
# Generate builtin binding
cd $GOPATH /src/reindexer
go generate ./bindings/builtin
# Optional (build builtin server binding)
go generate ./bindings/builtinserver # Go to your app's directory
cd /your/app/path
go get -a github.com/restream/reindexer/v3
go mod edit -replace github.com/restream/reindexer/v3= $GOPATH /src/reindexer在这种情况下,Go Binding将生成显式库和路径列表,并且不会使用PKG-Config。
如果您不使用go.mod,则可以通过这种方式从来源获取和构建ReIndexer:
export GO111MODULE=off # Disable go1.11 modules
# Go to your app's directory
cd /your/app/path
# Clone reindexer via git. It's also possible to use 'go get -a github.com/restream/reindexer', but it's behavior may vary depending on Go's version
git clone --branch master https://github.com/restream/reindexer.git vendor/github.com/restream/reindexer/v3
# Generate builtin binding
go generate -x ./vendor/github.com/restream/reindexer/v3/bindings/builtin
# Optional (build builtin server binding)
go generate -x ./vendor/github.com/restream/reindexer/v3/bindings/builtinserverGO不支持CGO代码(Golang/GO#26366)的适当供应商,但是,可以使用VEND将ReIndexer的源复制到供应商-Directory中。
使用vend您将可以致电go generate -mod=vendor builtin和builtinserver的供应商,并放置在供应商目录中。
也可以使用git clone将Reindexer的来源复制到青年项目中。
在这些情况下,所有依赖性的依赖项必须用适当的版本手动安装mod。
在内部,结构分为两个部分:
reindex struct标签仅在索引字段上进行查询,并标有reindex标签。 reindex标签包含索引名称,类型和其他选项:
reindex:"<name>[[,<type>],<opts>]"
name - 索引名称。type - 索引类型:hash - 快速选择EQ和SET匹配。默认情况下使用。允许按字段进行缓慢效率低下的排序。tree - 快速按范围,GT和LT匹配。 EQ和设定匹配hash索引要慢一些。允许按字段进行快速分类结果。text - 全文搜索索引。全文搜索的用法详细信息在这里描述- - 列索引。无法执行快速选择,因为它是通过全扫描技术实现的。头顶上有最小的内存。ttl -TTL索引仅适用于INT64字段。这些索引非常方便地表示日期字段(存储为UNIX时间戳),该索引在指定的秒数后到期。rtree仅可用Dwithin匹配。仅适用于[2]float64 (或reindexer.Point )字段类型。有关详细信息,请参见几何小节。opts - 其他索引选项:pk - 字段是主要键的一部分。 struct必须至少有1个字段用pk标记composite - 创建复合索引。字段类型必须是一个空结构: struct{} 。joined - 字段是加入的接收者。字段类型必须为[]*SubitemType 。dense - 降低索引尺寸。对于hash和tree它将每个唯一的钥匙值节省8个字节。对于-每个元素将节省4-8个字节。对于具有较高选择性的索引很有用,但是对于具有低选择性的tree和hash索引可以严重降低更新性能。同样,由于缺乏CPU缓存优化,密集dense -会减慢宽度的全面查询。sparse - 行(文档)仅在故意设置的情况下包含稀疏索引的值 - 该行中没有此类索引的空(或默认)记录(文档)。它允许节省RAM,但它会使您的性能付出代价 - 它的工作原理比常规索引慢一点。collate_numeric创建字符串索引,以数字序列提供值顺序。字段类型必须是字符串。collate_ascii创建对案例不敏感的字符串索引与ASCII一起使用。字段类型必须是字符串。collate_utf8创建对案例不敏感的字符串索引与UTF8一起使用。字段类型必须是字符串。collate_custom=<ORDER> - 创建自定义订单字符串索引。字段类型必须是字符串。 <ORDER>是字母的顺序,它定义了排序顺序。linear , quadratic , greene或rstar指定用于构建rtree索引的算法(默认情况下为rstar )。有关详细信息,请参见几何小节。uuid将此值存储为UUID。从RAM/网络完成UUID的角度来看,这比字符串更有效。仅支持hash和-索引类型。可以与任何UUID变体一起使用,除了变体0带有常规索引的字段不可谋杀。条件is NULL仅由sparse和array索引支持。
默认情况下,ReIndexer扫描所有嵌套结构并将其字段添加到名称空间(以及指定的索引)。在索引扫描私有(未伸出的字段)期间,用reindex:"-" ,将跳过json:"-" 。
type Actor struct {
Name string `reindex:"actor_name"`
Age int `reindex:"age"`
}
type BaseItem struct {
ID int64 `reindex:"id,hash,pk"`
UUIDValue string `reindex:"uuid_value,hash,uuid"`
}
type ComplexItem struct {
BaseItem // Index fields of BaseItem will be added to reindex
Actor [] Actor // Index fields ("name" and "age") of Actor will be added to reindex as array-indexes
Name string `reindex:"name"` // Hash-index for "name"
Year int `reindex:"year,tree"` // Tree-index for "year"
Value int `reindex:"value,-"` // Store(column)-index for "value"
Metainfo int `json:"-"` // Field "MetaInfo" will not be stored in reindexer
Parent * Item `reindex:"-"` // Index fields of "Parent" will NOT be added to reindex and all of the "Parent" exported content will be stored as non-indexed data
ParentHidden * Item `json:"-"` // Indexes and fields of "ParentHidden" will NOT be added to reindexer
privateParent * Item // Indexes and fields of "ParentHidden" will NOT be added to reindexer (same as with `json:"-"`)
AnotherActor Actor `reindex:"actor"` // Index fields of "AnotherActor" will be added to reindexer with prefix "actor." (in this example two indexes will be created: "actor.actor_name" and "actor.age")
}Reindexer可以按字段(包括连接名称空间的嵌套和字段)或按升序或降序顺序进行表达式对文档进行排序。
要按非指数字段进行排序,所有值必须相互转换,即具有相同类型,或者是数字类型之一( bool ,int, int , int64或float )。
排序表达式可以包含:
bool , int , int64 , float或string类型。所有值都必须转换为忽略领先空间和饰面空间的数字;rank() , abs()和ST_Distance() ;+ , - (一元和二进制), *和/ 。如果字段名称后面+必须通过空间分开以区分复合索引名称。必须像这样编写连接的名称空间的字段: joined_namespace.field 。
Abs()表示论点的绝对价值。
Rank()表示匹配的全文等级,并且仅适用于全文查询。
ST_Distance()表示几何点之间的距离(请参见几何小节)。这些点可以是当前或连接的命名空间中的列或格式的固定点ST_GeomFromText('point(1 -3)')
在SQL查询中,必须引用表达式。
type Person struct {
Name string `reindex:"name"`
Age int `reindex:"age"`
}
type City struct {
Id int `reindex:"id"`
NumberOfPopulation int `reindex:"population"`
Center reindexer. Point `reindex:"center,rtree,linear"`
}
type Actor struct {
ID int `reindex:"id"`
PersonData Person `reindex:"person"`
Price int `reindex:"price"`
Description string `reindex:"description,text"`
BirthPlace int `reindex:"birth_place_id"`
Location reindexer. Point `reindex:"location,rtree,greene"`
}
... .
query := db . Query ( "actors" ). Sort ( "id" , true ) // Sort by field
... .
query = db . Query ( "actors" ). Sort ( "person.age" , true ) // Sort by nested field
... .
// Sort by joined field
// Works for inner join only, when each item from left namespace has exactly one joined item from right namespace
query = db . Query ( "actors" ).
InnerJoin ( db . Query ( "cities" )). On ( "birth_place_id" , reindexer . EQ , "id" ).
Sort ( "cities.population" , true )
... .
// Sort by expression:
query = db . Query ( "actors" ). Sort ( "person.age / -10 + price / 1000 * (id - 5)" , true )
... .
query = db . Query ( "actors" ). Where ( "description" , reindexer . EQ , "ququ" ).
Sort ( "rank() + id / 100" , true ) // Sort with fulltext rank
... .
// Sort by geometry distance
query = db . Query ( "actors" ).
Join ( db . Query ( "cities" )). On ( "birth_place_id" , reindexer . EQ , "id" ).
SortStPointDistance ( cities . center , reindexer. Point { 1.0 , - 3.0 }, true ).
SortStFieldDistance ( "location" , "cities.center" , true )
... .
// In SQL query:
iterator := db . ExecSQL ( "SELECT * FROM actors ORDER BY person.name ASC" )
... .
iterator := db . ExecSQL ( "SELECT * FROM actors WHERE description = 'ququ' ORDER BY 'rank() + id / 100' DESC" )
... .
iterator := db . ExecSQL ( "SELECT * FROM actors ORDER BY 'ST_Distance(location, ST_GeomFromText( ' point(1 -3) ' ))' ASC" )
... .
iterator := db . ExecSQL ( "SELECT * FROM actors ORDER BY 'ST_Distance(location, cities.center)' ASC" )也可以设置这样的自定义排序订单
type SortModeCustomItem struct {
ID int `reindex:"id,,pk"`
InsItem string `reindex:"item_custom,hash,collate_custom=a-zA-Z0-9"`
}或这样
type SortModeCustomItem struct {
ID int `reindex:"id,,pk"`
InsItem string `reindex:"item_custom,hash,collate_custom=АаБбВвГгДдЕеЖжЗзИиКкЛлМмНнОоПпРрСсТтУуФфХхЦцЧчШшЩщЪъЫыЬьЭ-ЯAaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0-9ЁёЙйэ-я"`
}此列表中的第一个字符具有最高优先级,最后一个字符的优先级是最小的字符。这意味着排序算法将将以第一个字符开头的项目放在其他角色之前。如果某些字符被跳过,其优先级将具有通常的值(根据列表中的字符)。
在字符串字段中进行简单的搜索文本模式, LIKE可以使用。它寻找与图案相匹配的字符串。在模式中_表示任何字符, %表示任何字符序列。
去示例:
query := db . Query ( "items" ).
Where ( "field" , reindexer . LIKE , "pattern" )SQL示例:
SELECT * FROM items WHERE fields LIKE ' pattern ''me_t'对应于“遇见”,“肉”,“融化”等等,等等“%tion”对应于'tion','tion',``条件'',''''
注意:类似使用扫描方法的条件。它可用于调试目的,也可以在其他良好选择条件下查询。
通常,对于以合理的速度,我们建议使用FullText索引。
更新查询用于修改名称空间的现有项目。有几种更新查询:更新现有字段,添加新字段并删除现有的非索引字段。
更新SQL-Syntax
UPDATE nsName
SET field1 = value1, field2 = value2, ..
WHERE condition;也可以使用 +, - , /, *和括号使用算术表达式
UPDATE NS SET field1 = field2 + field3 - (field4 + 5 ) / 2包括诸如now() , sec()和serial()函数。要使用Golang Code SetExpression()方法中的表达式,需要调用而不是Set() 。
使阵列场空
UPDATE NS SET arrayfield = [] WHERE id = 100并将其设置为空
UPDATE NS SET field = NULL WHERE id > 100在非索引字段的情况下,将其值设置为不同类型的值将完全替换它;如果是索引字段,则只能将其从相邻类型(积分类型和bool),数字字符串(例如“ 123456”)转换为积分类型和背部。将索引字段设置为null将其重置为默认值。
可以在现有项目中添加新字段
UPDATE NS SET newField = ' Brand new! ' WHERE id > 100甚至通过这样的复杂嵌套路径添加一个新字段
UPDATE NS SET nested . nested2 . nested3 . nested4 .newField = ' new nested field! ' WHERE id > 100将创建以下嵌套对象:嵌套,Nested2,Nested3,Nested4和Newfield作为对象Nested4的成员。
在Golang代码中使用更新查询的示例:
db . Query ( "items" ). Where ( "id" , reindexer . EQ , 40 ). Set ( "field1" , values ). Update ()ReIndexer可以更新和添加对象字段。可以通过struct,ap或字节数组(即对象表示的JSON版本)设置对象。
type ClientData struct {
Name string `reindex:"name" json:"name"`
Age int `reindex:"age" json:"age"`
Address int `reindex:"year" json:"year"`
Occupation string `reindex:"occupation" json:"occupation"`
TaxYear int `reindex:tax_year json:"tax_year"`
TaxConsultant string `reindex:tax_consultant json:"tax_consultant"`
}
type Client struct {
ID int `reindex:"id" json:"id"`
Data ClientData `reindex:"client_data" json:"client_data"`
...
}
clientData := updateClientData ( clientId )
db . Query ( "clients" ). Where ( "id" , reindexer . EQ , 100 ). SetObject ( "client_data" , clientData ). Update ()在这种情况下,Golang中的Map只能用字符串作为键使用。 map[string]interface{}是一个完美的选择。
通过SQL语句更新对象字段:
UPDATE clients SET client_data = { " Name " : " John Doe " , " Age " : 40 , " Address " : " Fifth Avenue, Manhattan " , " Occupation " : " Bank Manager " , " TaxYear " : 1999 , " TaxConsultant " : " Jane Smith " } WHERE id = 100 ;更新删除现有非索引字段的查询的SQL-Syntax:
UPDATE nsName
DROP field1, field2, ..
WHERE condition; db . Query ( "items" ). Where ( "id" , reindexer . EQ , 40 ). Drop ( "field1" ). Update ()ReIndexer Update机制可以修改数组字段:修改现有数组的某些项目,甚至更换整个字段。
更新项目订阅操作员语法:
UPDATE NS SET array[ * ].prices[ 0 ] = 9999 WHERE id = 5在哪里*表示所有项目。
要更新整个数组,请使用以下内容:
UPDATE NS SET prices = [ 999 , 1999 , 2999 ] WHERE id = 9使用此语法可以轻松地将任何非索引字段转换为数组。
Reindexer还允许更新对象数组的项目:
UPDATE NS SET extra . objects [ 0 ] = { " Id " : 0 , " Description " : " Updated! " } WHERE id = 9也这样
db . Query ( "clients" ). Where ( "id" , reindexer . EQ , 100 ). SetObject ( "extra.objects[0]" , updatedValue ). Update ()Reindexer支持异质数组:
UPDATE NS SET info = [ " hi " , " bro " , 111 , 2 . 71 ] WHERE id = 9 q := DB . Query ( ns ). Where ( "id" , reindexer . EQ , 1 ). Set ( "array" , [] interface {}{ "whatsup" , 777 , "bro" })
res , err := q . Update (). FetchAll ()索引阵列范围支持值只能转换为索引类型。保存后,此类值可能会因转换而改变精度。
UPDATE NS SET prices_idx = [ 11 , ' 2 ' , 3 ]要通过索引删除项目,您应该执行以下操作:
UPDATE NS DROP array[ 5 ]要将项目添加到现有数组中,支持以下语法:
UPDATE NS SET integer_array = integer_array || [ 5 , 6 , 7 , 8 ]和
UPDATE NS SET integer_array = [ 1 , 2 , 3 , 4 , 5 ] || integer_array第一个将元素添加到integer_array的末尾,第二个元素在其前面添加了5个项目。在Golang SetExpression()中使用此代码工作,而不是Set() 。
要通过值将项目删除到现有数组中,支持以下语法:
UPDATE NS SET integer_array = array_remove(integer_array, [ 5 , 6 , 7 , 8 ])和
UPDATE NS SET integer_array = array_remove_once(integer_array, [ 5 , 6 , 7 , 8 , 6 ])第一个删除了integer_array中列出的值的所有出现,第二个仅删除了第一次出现。在Golang SetExpression()中使用此代码工作,而不是Set() 。如果需要删除一个值,则可以使用方括号[5]或简单值5 。
UPDATE NS SET integer_array = array_remove(integer_array, [ 5 ]) update ns set integer_array = array_remove(integer_array, 5 )删除命令可以与数组串联结合在一起:
UPDATE NS SET integer_array = array_remove_once(integer_array, [ 5 , 6 , 7 , 8 ]) || [ 1 , 2 , 3 ]也这样
db . Query ( "main_ns" ). SetExpression ( "integer_array" , "array_remove(integer_array, [5,6,7,8]) || [1,2,3]" ). Update ()可以从第一个字段的值中删除第二个字段的值。还要添加新值等。注意:命令中的第一个参数预计是数组/field-array,第二个参数可以是数组/标量/field-array/field-scalar。对于需要兼容性/可兑换性
UPDATE NS SET integer_array = [ 3 ] || array_remove(integer_array, integer_array2) || integer_array3 || array_remove_once(integer_array, [ 8 , 1 ]) || [ 2 , 4 ] UPDATE NS SET integer_array = array_remove(integer_array, integer_array2) || array_remove(integer_array, integer_array3) || array_remove_once(integer_array, [ 33 , 777 ]) db . Query ( "main_ns" ). SetExpression ( "integer_array" , "[3] || array_remove(integer_array, integer_array2) || integer_array3 || array_remove(integer_array, [8,1]) || [2,4]" ). Update ()Reindexer支持交易。交易是执行原子名称空间更新。有同步和异步交易可用。启动事务方法db.BeginTx()使用。此方法创建事务对象,该对象为应用程序提供了常规更新/UPSERT/INSERT/DELETE接口。对于RPC客户端,有交易计数限制 - 每个连接不能同时拥有超过1024个开放交易。
// Create new transaction object
tx , err := db . BeginTx ( "items" );
if err != nil {
panic ( err )
}
// Fill transaction object
tx . Upsert ( & Item { ID : 100 })
tx . Upsert ( & Item { ID : 101 })
tx . Query (). WhereInt ( "id" , reindexer . EQ , 102 ). Set ( "Name" , "Petya" ). Update ()
// Apply transaction
if err := tx . Commit (); err != nil {
panic ( err )
}为了加快批量记录的插入,可以使用async模式。
// Create new transaction object
tx , err := db . BeginTx ( "items" );
if err != nil {
panic ( err )
}
// Prepare transaction object async.
tx . UpsertAsync ( & Item { ID : 100 }, func ( err error ) {})
tx . UpsertAsync ( & Item { ID : 100 }, func ( err error ) {})
// Wait for async operations done, and apply transaction.
if err := tx . Commit (); err != nil {
panic ( err )
} UpsertAsync的第二个参数是完成函数,在接收服务器响应后将调用。另外,如果在准备过程中发生任何错误,则tx.Commit应返回错误。因此,要检查tx.Commit返回的错误是足够的 - 确保所有数据均已成功合作。
根据交易的变化数量,有2种可能的提交策略:
可以在名称空间配置中选择选择提交策略的数据量。在struct DBNamespacesConfig (describer.go)中,检查字段StartCopyPolicyTxSize , CopyPolicyMultiplier和TxSizeToAlwaysCopy
如果交易大小小于TxSizeToAlwaysCopy ,则ReIndexer使用额外的启发式词,并试图避免命名空间复制,如果没有为此命名空间看到的选择查询。在某些情况下,这种启发式可能会增加选择延迟,因此可以通过将REINDEXER_NOTXHEURISTIC ENV变量设置为任何非空值来禁用它。
tx.Query("ns").Exec() ... ;Reindexer可以将文档从多个名称空间加入单个结果:
type Actor struct {
ID int `reindex:"id"`
Name string `reindex:"name"`
IsVisible bool `reindex:"is_visible"`
}
// Fields, marked with 'joined' must also be exported - otherwise reindexer's binding will not be able to put data in those fields
type ItemWithJoin struct {
ID int `reindex:"id"`
Name string `reindex:"name"`
ActorsIDs [] int `reindex:"actors_ids"`
ActorsNames [] int `reindex:"actors_names"`
Actors [] * Actor `reindex:"actors,,joined"`
}
... .
query := db . Query ( "items_with_join" ). Join (
db . Query ( "actors" ).
WhereBool ( "is_visible" , reindexer . EQ , true ),
"actors"
). On ( "actors_ids" , reindexer . SET , "id" )
it := query . Exec ()在此示例中,ReIndexer使用引擎盖下的反射来创建Actor Slice和Copy Actor Struct。
加入查询可能在与之相关的条件And (默认情况下) Or Not操作员的条件On从一到几个:
query := db . Query ( "items_with_join" ).
Join (
db . Query ( "actors" ).
WhereBool ( "is_visible" , reindexer . EQ , true ),
"actors" ).
On ( "actors_ids" , reindexer . SET , "id" ).
Or ().
On ( "actors_names" , reindexer . SET , "name" ) InnerJoin结合了两个名称空间的数据,在两个名称空间中的联接字段上都有匹配。 LeftJoin从LeftJoin关键字左侧的名称空间,右侧的表值返回所有有效的项目,或者如果不存在匹配项,则什么都没有。 Join是LeftJoin的别名。
InnerJoins可以用作Where的条件:
query1 := db . Query ( "items_with_join" ).
WhereInt ( "id" , reindexer . RANGE , [] int { 0 , 100 }).
Or ().
InnerJoin ( db . Query ( "actors" ). WhereString ( "name" , reindexer . EQ , "ActorName" ), "actors" ).
On ( "actors_ids" , reindexer . SET , "id" ).
Or ().
InnerJoin ( db . Query ( "actors" ). WhereInt ( "id" , reindexer . RANGE , [] int { 100 , 200 }), "actors" ).
On ( "actors_ids" , reindexer . SET , "id" )
query2 := db . Query ( "items_with_join" ).
WhereInt ( "id" , reindexer . RANGE , [] int { 0 , 100 }).
Or ().
OpenBracket ().
InnerJoin ( db . Query ( "actors" ). WhereString ( "name" , reindexer . EQ , "ActorName" ), "actors" ).
On ( "actors_ids" , reindexer . SET , "id" ).
InnerJoin ( db . Query ( "actors" ). WhereInt ( "id" , reindexer . RANGE , [] int { 100 , 200 }), "actors" ).
On ( "actors_ids" , reindexer . SET , "id" ).
CloseBracket ()
query3 := db . Query ( "items_with_join" ).
WhereInt ( "id" , reindexer . RANGE , [] int { 0 , 100 }).
Or ().
InnerJoin ( db . Query ( "actors" ). WhereInt ( "id" , reindexer . RANGE , [] int { 100 , 200 }), "actors" ).
On ( "actors_ids" , reindexer . SET , "id" ).
Limit ( 0 )请注意,通常Or操作员在Where条件下实现短路:如果上一个条件为True,则未评估下一个条件。但是在InnerJoin的情况下,它的工作方式有所不同:在query1中(从上面的示例)中,尽管有WhereInt的结果,但都评估了两种InnerJoin条件。 Limit(0)作为InnerJoin的一部分(从上面的示例中query3 )不连接任何数据 - 它像过滤器一样工作以验证条件。
Reindexer不支持ANTI JOIN SQL构建,但是,它支持与Joins的逻辑操作。实际上, NOT (INNER JOIN ...)完全等同于ANTI JOIN :
query := db . Query ( "items_with_join" ).
Not ().
OpenBracket (). // Brackets are essential here for NOT to work
InnerJoin (
db . Query ( "actors" ).
WhereBool ( "is_visible" , reindexer . EQ , true ),
"actors" ).
On ( "id" , reindexer . EQ , "id" )
CloseBracket () SELECT * FROM items_with_join
WHERE
NOT (
INNER JOIN (
SELECT * FROM actors WHERE is_visible = true
) ON items_with_join . id = actors . id
)为避免使用反射, Item可以实现Joinable接口。如果实现了,则ReIndexer使用它而不是基于缓慢的反射实现。这将整体绩效提高了10-20%,并减少了分配量。
// Joinable interface implementation.
// Join adds items from the joined namespace to the `ItemWithJoin` object.
// When calling Joinable interface, additional context variable can be passed to implement extra logic in Join.
func ( item * ItemWithJoin ) Join ( field string , subitems [] interface {}, context interface {}) {
switch field {
case "actors" :
for _ , joinItem := range subitems {
item . Actors = append ( item . Actors , joinItem .( * Actor ))
}
}
}当前查询中包含的另一个查询(子查询)的结果可以应用于条件。该条件可以在子查询的结果行上:
query := db . Query ( "main_ns" ).
WhereQuery ( db . Query ( "second_ns" ). Select ( "id" ). Where ( "age" , reindexer . GE , 18 ), reindexer . GE , 100 )或在主要查询名称空间的字段和子查询结果之间:
query := db . Query ( "main_ns" ).
Where ( "id" , reindexer . EQ , db . Query ( "second_ns" ). Select ( "id" ). Where ( "age" , reindexer . GE , 18 ))子查询的结果可能是通过Select方法指向的某个字段(在这种情况下,必须设置单个字段过滤器):
query1 := db . Query ( "main_ns" ).
WhereQuery ( db . Query ( "second_ns" ). Select ( "id" ). Where ( "age" , reindexer . GE , 18 ), reindexer . GE , 100 )
query2 := db . Query ( "main_ns" ).
Where ( "id" , reindexer . EQ , db . Query ( "second_ns" ). Select ( "id" ). Where ( "age" , reindexer . GE , 18 ))或ReqTotal或CachedTotal方法所需的子查询的项目计数:
query1 := db . Query ( "main_ns" ).
WhereQuery ( db . Query ( "second_ns" ). Where ( "age" , reindexer . GE , 18 ). ReqTotal (), reindexer . GE , 100 )
query2 := db . Query ( "main_ns" ).
Where ( "id" , reindexer . EQ , db . Query ( "second_ns" ). Where ( "age" , reindexer . GE , 18 ). CachedTotal ())或聚合:
query1 := db . Query ( "main_ns" ).
WhereQuery ( db . Query ( "second_ns" ). Where ( "age" , reindexer . GE , 18 ). AggregateMax ( "age" ), reindexer . GE , 33 )
query2 := db . Query ( "main_ns" ).
Where ( "age" , reindexer . GE , db . Query ( "second_ns" ). Where ( "age" , reindexer . GE , 18 ). AggregateAvg ( "age" )) Min , Max , Avg , Sum , Count和CountCached聚合。子查询不能同时包含多个聚合。
子查询可以应用于同一名称空间或另一个名称空间。
子查询不能包含另一个子查询,加入或合并。
如果要检查至少一个项目是否满足子征服,则可以使用ANY或EMPTY条件:
query1 := db . Query ( "main_ns" ).
WhereQuery ( db . Query ( "second_ns" ). Where ( "age" , reindexer . GE , 18 ), reindexer . ANY , nil )
query2 := db . Query ( "main_ns" ).
WhereQuery ( db . Query ( "second_ns" ). Where ( "age" , reindexer . LE , 18 ), reindexer . EMPTY , nil )文档可以将多个字段作为主要键。要启用此功能,将复合索引添加到struct。复合索引是涉及多个字段的索引,可以使用它代替几个单独的索引。
type Item struct {
ID int64 `reindex:"id"` // 'id' is a part of a primary key
SubID int `reindex:"sub_id"` // 'sub_id' is a part of a primary key
// Fields
// ....
// Composite index
_ struct {} `reindex:"id+sub_id,,composite,pk"`
}或者
type Item struct {
ID int64 `reindex:"id,-"` // 'id' is a part of primary key, WITHOUT personal searchable index
SubID int `reindex:"sub_id,-"` // 'sub_id' is a part of a primary key, WITHOUT a personal searchable index
SubSubID int `reindex:"sub_sub_id,-"` // 'sub_sub_id' is a part of a primary key WITHOUT a personal searchable index
// Fields
// ....
// Composite index
_ struct {} `reindex:"id+sub_id+sub_sub_id,,composite,pk"`
}同样,复合索引对于通过多个字段对结果进行分类很有用:
type Item struct {
ID int64 `reindex:"id,,pk"`
Rating int `reindex:"rating"`
Year int `reindex:"year"`
// Composite index
_ struct {} `reindex:"rating+year,tree,composite"`
}
...
// Sort query results by rating first, then by year
query := db . Query ( "items" ). Sort ( "rating+year" , true )
// Sort query results by rating first, then by year, and put item where rating == 5 and year == 2010 first
query := db . Query ( "items" ). Sort ( "rating+year" , true ,[] interface {}{ 5 , 2010 })为了对复合索引进行查询,请将[]接口{}传递到查询构建器的.WhereComposite函数:
// Get results where rating == 5 and year == 2010
query := db . Query ( "items" ). WhereComposite ( "rating+year" , reindexer . EQ ,[] interface {}{ 5 , 2010 })必须索引常规(非填充)复合指数中的所有字段。即要创建综合索引rating+year ,有必要首先为raiting和year创建某种索引:
type Item struct {
ID int64 `reindex:"id,,pk"`
Rating int `reindex:"rating,-"` // this field must be indexed (using index type '-' in this example)
Year int `reindex:"year"` // this field must be indexed (using index type 'hash' in this example)
_ struct {} `reindex:"rating+year,tree,composite"`
}Reindexer允许检索汇总结果。目前支持,计数,平均,总和,最小,最大,方面和不同的聚合。
Count - 获取符合查询条件的文档总数CountCached - 获取符合查询条件的文档总数。结果值将被缓存,并且可以通过Countcachectregation的其他查询重复使用AggregateMax获取最大字段值AggregateMin - 获取最小现场值AggregateSum - 获取总和字段值AggregateAvg - 获取平均现场值AggregateFacet - 获取字段facet值Distinct - 获取该字段唯一值的列表为了支持聚合, Query具有AggregateAvg方法, AggregateSum , AggregateMin , AggregateMax , AggregateFacet和Distinct这些方法应在Query执行之前调用:这将要求ReIndexer计算数据聚合。聚合方面适用于多个数据列,其结果可以通过任何数据列或“计数”来对其进行排序,并通过偏移和限制切断。为了支持此功能方法, AggregateFacet返回具有方法Sort , Limit和Offset AggregationFacetRequest 。
与合并合并的查询将对所有合并的子查询应用于总查询的汇总。亚Queries无法拥有自己的聚合。合并征服的可用聚合是:计数,计数,总和,最小和最大。
为了获得汇总结果, Iterator具有方法AggResults :可在查询执行后可用并返回结果。
按price和name按汇总items的示例代码
query := db . Query ( "items" )
query . AggregateMax ( "price" )
query . AggregateFacet ( "name" , "price" ). Sort ( "name" , true ). Sort ( "count" , false ). Offset ( 10 ). Limit ( 100 )
iterator := query . Exec ()
// Check the error
if err := iterator . Error (); err != nil {
panic ( err )
}
defer iterator . Close ()
aggMaxRes := iterator . AggResults ()[ 0 ]
if aggMaxRes . Value != nil {
fmt . Printf ( "max price = %d n " , * aggMaxRes . Value )
} else {
fmt . Println ( "no data to aggregate" )
}
aggFacetRes := iterator . AggResults ()[ 1 ]
fmt . Printf ( "'name' 'price' -> count" )
for _ , facet := range aggFacetRes . Facets {
fmt . Printf ( "'%s' '%s' -> %d" , facet . Values [ 0 ], facet . Values [ 1 ], facet . Count )
} query := db . Query ( "items" )
query . Distinct ( "name" ). Distinct ( "price" )
iterator := query . Exec ()
// Check the error
if err := iterator . Error (); err != nil {
panic ( err )
}
defer iterator . Close ()
aggResults := iterator . AggResults ()
distNames := aggResults [ 0 ]
fmt . Println ( "names:" )
for _ , name := range distNames . Distincts {
fmt . Println ( name )
}
distPrices := aggResults [ 1 ]
fmt . Println ( "prices:" )
for _ , price := range distPrices . Distincts {
fmt . Println ( price )
}通过汇总FACET的字段进行排序具有独特的SQL版本语法:
SELECT FACET(name, price ORDER BY " name " ASC , " count " DESC ) FROM items当匹配值具有相同的索引位置时,ReIndexer允许在数组字段中搜索数据。例如,我们有一系列结构:
type Elem struct {
F1 int `reindex:"f1"`
F2 int `reindex:"f2"`
}
type A struct {
Elems [] Elem
}在此数组中搜索值的常见尝试
db . Query ( "Namespace" ). Where ( "f1" , EQ , 1 ). Where ( "f2" , EQ , 2 )找到所有阵列Elem[]的项目,其中f1等于1和f2等于2。
EqualPosition函数允许在具有相等索引的数组字段中搜索。这样的查询:
db . Query ( "Namespace" ). Where ( "f1" , reindexer . GE , 5 ). Where ( "f2" , reindexer . EQ , 100 ). EqualPosition ( "f1" , "f2" )或者
SELECT * FROM Namespace WHERE f1 >= 5 AND f2 = 100 EQUAL_POSITION(f1,f2);将找到具有equal数组索引的数组Elem[]的所有项目,其中f1大或等于5和f2等于100(例如,查询返回的5个项目,其中两个数组的第3个元素具有适当的值)。
具有复杂表达式(带有括号的表达式)等于_position()可以在括号内:
SELECT * FROM Namespace WHERE (f1 >= 5 AND f2 = 100 EQUAL_POSITION(f1,f2)) OR (f3 = 3 AND f4 < 4 AND f5 = 7 EQUAL_POSITION(f3,f4,f5));
SELECT * FROM Namespace WHERE (f1 >= 5 AND f2 = 100 AND f3 = 3 AND f4 < 4 EQUAL_POSITION(f1,f3) EQUAL_POSITION(f2,f4)) OR (f5 = 3 AND f6 < 4 AND f7 = 7 EQUAL_POSITION(f5,f7));
SELECT * FROM Namespace WHERE f1 >= 5 AND (f2 = 100 AND f3 = 3 AND f4 < 4 EQUAL_POSITION(f2,f3)) AND f5 = 3 AND f6 < 4 EQUAL_POSITION(f1,f5,f6);等于equal_position与以下条件不起作用:null,是空的,并且在(带有空参数列表)中。
有原子函数在命名空间锁定下执行,因此保证数据一致性:
这些功能可以传递给3-RD和下一个参数中的UPSERT/INSERT/UPDATE。
如果提供了这些功能,则通过参考项将更改为更新值
// set ID field from serial generator
db . Insert ( "items" , & item , "id=serial()" )
// set current timestamp in nanoseconds to updated_at field
db . Update ( "items" , & item , "updated_at=now(NSEC)" )
// set current timestamp and ID
db . Upsert ( "items" , & item , "updated_at=now(NSEC)" , "id=serial()" )数据到期对于某些类别的信息很有用,包括机器生成的事件数据,日志和会话信息,这些信息只需要在有限的时间内持续存在。
ReIndexer使得可以为命名空间项目设置TTL(Live)。在指定的秒数之后,将TTLINDEX添加到名称空间会自动删除项目。
TTL索引仅适用于INT64字段,并存储UNIX TIMESTAMP数据。 expire_after秒钟后Expire__Expire__Expire_Exex的项目。 Golang宣布ttlindex的示例:
type NamespaceExample struct {
ID int `reindex:"id,,pk" json:"id"`
Date int64 `reindex:"date,ttl,,expire_after=3600" json:"date"`
}
...
ns . Date = time . Now (). Unix ()在这种情况下,命名空间命名空间exexample的项目在namespaceexample之后的3600秒内到期。
TTL索引以非TTL索引的方式支持查询。
如果以JSON格式获得源数据,则可以通过将JSON直接传递给ReIndexer来提高UPSERT/DELETE操作的性能。 JSON避难所将通过C ++代码完成,而GO代码中没有额外的分配/避难所化。
UPSERT或DELETE函数只需通过JSON传递[]字节来处理JSON
json := [] byte ( `{"id":1,"name":"test"}` )
db . Upsert ( "items" , json )它只是更快的等同:
item := & Item {}
json . Unmarshal ([] byte ( `{"id":1,"name":"test"}` ), item )
db . Upsert ( "items" , item )如果需要以JSON格式序列化查询结果,则可以通过直接从Reindexer中获得JSON格式的结果来提高性能。 JSON序列化将通过C ++代码完成,而无需在GO代码中进行额外的分配/序列化。
...
iterator := db . Query ( "items" ).
Select ( "id" , "name" ). // Filter output JSON: Select only "id" and "name" fields of items, another fields will be omitted. This fields should be specified in the same case as the jsonpaths corresponding to them.
Limit ( 1 ).
ExecToJson ( "root_object" ) // Name of root object of output JSON
json , err := iterator . FetchAll ()
// Check the error
if err != nil {
panic ( err )
}
fmt . Printf ( "%s n " , string ( json ))
...此代码将打印类似的内容:
{ "root_object" : [{ "id" : 1 , "name" : " test " }] }为了避免种族条件,默认情况下关闭对象缓存,并根据每个查询分配并从Reindexer内部格式(称为CJSON )分配并进行每个对象。避难所是使用反射,因此其速度不是最佳的(实际上CJSON挑战速度比JSON快〜3-10倍,而GOB的速度比GOB快〜1.2倍),但性能仍然受到反射开销的严重限制。
有两种启用对象缓存的方法:
如果对象是实现DeepCopy接口,则ReIndexer将打开对象缓存,并使用DeepCopy接口将对象从缓存复制到查询结果。 DeepCopy接口负责使源对象的深层复制。
这是DeepCopy接口实现的示例
func ( item * Item ) DeepCopy () interface {} {
copyItem := & Item {
ID : item . ID ,
Name : item . Name ,
Articles : make ([] int , cap ( item . Articles ), len ( item . Articles )),
Year : item . Year ,
}
copy ( copyItem . Articles , item . Articles )
return copyItem
}为了加快查询并不分配每个查询的新对象,可以直接从对象缓存中询问查询返回对象。为了启用此行为,请在Iterator上调用AllowUnsafe(true) 。
警告:使用AllowUnsafe(true)查询时,查询将共享指针返回到对象缓存中的结构。因此,应用程序不得修改返回的对象。
res , err := db . Query ( "items" ). WhereInt ( "id" , reindexer . EQ , 1 ). Exec (). AllowUnsafe ( true ). FetchAll ()
if err != nil {
panic ( err )
}
if len ( res ) > 1 {
// item is SHARED pointer to struct in object cache
item = res [ 0 ].( * Item )
// It's OK - fmt.Printf will not modify item
fmt . Printf ( "%v" , item )
// It's WRONG - can race, and will corrupt data in object cache
item . Name = "new name"
}默认情况下,每个名称空间的对象缓存的最大大小为256000个项目。要更改最大尺寸,请使用传递给OpenNamespace的NamespaceOptions ObjCacheSize方法的方法。例如
// Set object cache limit to 4096 items
db . OpenNamespace ( "items_with_huge_cache" , reindexer . DefaultNamespaceOptions (). ObjCacheSize ( 4096 ), Item {})!此缓存不应用于从其他节点复制的命名空间:对于这些副本的名称空间可能不一致。
唯一支持的几何数据类型是2D点,在Golang中实现为[2]float64 ( reindexer.Point )。
在SQL中,可以将一个点创建为ST_GeomFromText('point(1 -3)') 。
几何场唯一支持的请求是在距点一定距离内找到所有点。 DWithin(field_name, point, distance)如下示例。
相应的SQL函数是ST_DWithin(field_name, point, distance) 。
可以为积分创建RTREE索引。为此,应声明rtree和linear , quadratic , greene或rstar标签。 linear , quadratic , greene或rstar手段将使用RTREE构建算法。这里列出了算法,以从优化插入到优化搜索的顺序。但这取决于数据。测试更适合您。默认算法是rstar 。
type Item struct {
id int `reindex:"id,,pk"`
pointIndexed reindexer. Point `reindex:"point_indexed,rtree,linear"`
pointNonIndexed reindexer. Point `json:"point_non_indexed"`
}
query1 := db . Query ( "items" ). DWithin ( "point_indexed" , reindexer. Point { - 1.0 , 1.0 }, 4.0 ) SELECT * FROM items WHERE ST_DWithin(point_non_indexed, ST_GeomFromText( ' point(1 -3.5) ' ), 5 . 0 );像在此代码段中一样,可以通过db.SetLogger()方法打开Reindexer Logger:
type Logger struct {
}
func ( Logger ) Printf ( level int , format string , msg ... interface {}) {
log . Printf ( format , msg ... )
}
...
db . SetLogger ( Logger {})Reindexer支持记录缓慢的动作。它可以通过profiling.long_queries_logging #config系统名称空间进行配置。可以配置下一个操作的记录:
选择查询:
threshold_us (integer) :用于执行选择查询的阈值值(以微秒为单位)。如果超过超过,则将进行核心gog条目,如果禁用了threshold_us为-1记录。normalized (boolean) :以归一化形式输出查询。更新和删除查询:
threshold_us (integer) :执行更新或删除查询的阈值值(以微秒为单位)。如果超过超过,则将进行核心gog条目,如果禁用了threshold_us为-1记录。normalized (boolean) :以归一化形式输出查询。交易:
threshold_us (integer) :阈值值(以微秒为单位),如果threshold_us为-1通过总交所提交时间来记录,则禁用了threshold_us。avg_step_threshold_us (integer) :阈值值(以微秒为单位)在交易中的平均步长期间。如果avg_step_threshold_us是-1按平均交易的步长持续时间的记录。另一个有用的功能是处理已加工查询的打印。要调试打印查询详细信息,有两种方法:
db.SetDefaultQueryDebug(namespace string,level int) - 它在全球范围内启用所有查询的详细信息,按名称空间
query.Debug(level int) - 查询执行level的打印详细信息是冗长的级别:
reindexer.INFO将仅打印查询条件
reindexer.TRACE将使用定时打印查询条件和执行详细信息
query.Explain () - 计算和存储查询执行详细信息。
iterator.GetExplainResults () - 返回查询执行详细信息
ReIndexer支持TCMalloc(也是Gperftools的一部分)和Jemalloc分配器(在cmakelists.txt中检查ENABLE_TCMALLOC和ENABLE_JEMALLOC )。
If you have built standalone server from sources available allocators will be detected and used automatically.
In go:generate builds and prebuilt packages reindexer has TCMalloc support, however none of TCMalloc libraries will be linked automatically. To force allocator's libraries linkage LD_PRELOAD with required library has to be used:
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libtcmalloc_and_profiler.so ./my_executable
Custom allocator may be handy to track memory consummation, profile heap/CPU or to improve general performance.
Because reindexer core is written in C++ all calls to reindexer and their memory consumption are not visible for go profiler. To profile reindexer core there are cgo profiler available. cgo profiler now is part of reindexer, but it can be used with any another cgo code.
Usage of cgo profiler is very similar with usage of go profiler.
import _ "github.com/restream/reindexer/v3/pprof" go func () {
log . Println ( http . ListenAndServe ( "localhost:6060" , nil ))
}()HEAPPROFILE=/tmp/pprofpprof -symbolize remote http://localhost:6060/debug/cgo/pprof/heapInternal Reindexer's profiler is based on gperf_tools library and unable to get CPU profile via Go runtime. However, go profiler may be used with symbolizer to retrieve C++ CPU usage.
import _ "net/http/pprof" go func () {
log . Println ( http . ListenAndServe ( "localhost:6060" , nil ))
}()REINDEXER_CGOBACKTRACE=1pprof -symbolize remote http://localhost:6060/debug/pprof/profile ? seconds=10Due to internal Golang's specific it's not recommended to try to get CPU and heap profiles simultaneously, because it may cause deadlock inside the profiler.
Go binding for Reindexer comes with optional support for OpenTelemetry integration.
To enable generation of OpenTelemetry tracing spans for all exported client side calls ( OpenNamespace , Upsert , etc), pass reindexer.WithOpenTelemetry() option when creating a Reindexer DB instance:
db := reindexer . NewReindex ( "cproto://user:[email protected]:6534/testdb" , reindexer . WithOpenTelemetry ()) All client side calls on the db instance will generate OpenTelemetry spans with the name of the performed operation and information about Reindexer DSN, namespace name (if applicable), etc.
For example, a call like this on the db instance above:
db . OpenNamespace ( "items" , reindexer . DefaultNamespaceOptions (), Item {}) will generate an OpenTelemetry span with the span name of Reindexer.OpenNamespace and with span attributes like this:
rx.dsn : cproto://user:[email protected]:6534/testdbrx.ns : items Use opentelemetry-go in your client go application to export the information externally. For example, as a minimum, you will need to configure OpenTelemetry SDK exporter to expose the generated spans externally (see the Getting Started guide for more information).
A list of connectors for work with Reindexer via other program languages (TBC later):
Pyreindexer is official connector, and maintained by Reindexer's team. It supports both builtin and standalone modes. Before installation reindexer-dev (version >= 2.10) should be installed. See installation instructions for details.
For install run:
pip3 install pyreindexerURLs:
Python version >=3.6 is required.
Reindexer for java is official connector, and maintained by Reindexer's team. It supports both builtin and standalone modes. For enable builtin mode support reindexer-dev (version >= 3.1.0) should be installed. See installation instructions for details.
For install reindexer to Java or Kotlin project add the following lines to maven project file
<dependency>
<groupId>com.github.restream</groupId>
<artifactId>rx-connector</artifactId>
<version>[LATEST_VERSION]</version>
</dependency>
URL: https://github.com/Restream/reindexer-java
Note: Java version >= 1.8 is required.
Spring wrapper for Java-connector: https://github.com/evgeniycheban/spring-data-reindexer
URL: https://github.com/Smolevich/reindexer-client
URL: https://github.com/coinrust/reindexer-rs
URL: https://github.com/oruchreis/ReindexerNet
Currently, Reindexer is stable and production ready, but it is still a work in progress, so there are some limitations and issues:
You can get help in several ways:
Landing: https://reindexer.io/
Packages repo: https://repo.reindexer.io/
More documentation (RU): https://reindexer.io/reindexer-docs/