Xedis 是一个内存kv数据库
你可曾觉得:
- redis的Keys命令性能太低?
- DEL命令不够好用、不支持pattern?
- 支持的数据结构太少,不能满足业务需求?
等等各种不尽如人意、不够契合业务场景的问题。是组合多种不同数据结构最后勉强满足业务需求,还是变更业务形态使之契合redis?这些问题在Xedis中都不再是问题。
* 核心功能
Xedis就是用来解决这种**不够契合**的问题的,Xedis在<数据结构(项目中称为Structures)>、<命令(Cmd)>、<Key的组织方式(项目中称为View)>三个维度上提供了非常简单的扩展方法,只要用户懂一点点golang的语法即可(从零学golang语法通常只需要花费1-3个小时的学习时间)。
* 默认兼容redis协议,便于使用。
在通信协议(默认使用redis协议)、通信方式(默认使用TCP)上都保留了扩展性。
目前,实现了两种通信协议:simple协议(一个简单的字符协议, 可以使用telnet交互)和redis协议(提供二进制读写功能)
## 获取Xedis包
go get git.oschina.net/yyzybb537/Xedis
## 编译&安装
cd $GOPATH/src/git.oschina.net/yyzybb537/Xedis
./build.sh
## 启动Xedis服务端
// 启动Xedis监听8030端口 $ ./Xedis $ ./Xedis -L ":8030" // 启动Xedis监听回环网卡的8030端口 $ ./Xedis -L "127.0.0.1:8030" // 更多启动参数请看help手册 $ ./Xedis -h // 命令帮助手册, 可以显示所有当前支持的命令、数据结构、协议、通信方式、View $ ./Xedis -D
## 使用Xedis客户端
Xedis有3种client:Xedis自带的golang客户端、 任一语言的redis客户端、 redis-cli/telnet命令行工具,后续还会提供xedis专属的命令行工具。
推荐使用Xedis自带的golang客户端,因为每一个自定义的命令在build之后都会生成相应格式的接口,并且可以使用代码补全工具来给出补全提示。
* Example1:单链接的使用方式
package main import ( xedis "git.oschina.net/yyzybb537/Xedis/client" "fmt" ) func main() { // 连接Xedis c, err := xedis.Dial("127.0.0.1:8030") if err != nil { fmt.Printf("Dial error:%s", err.Error()) return } defer c.Close() // 执行命令: SET A str err = c.Set("A", "str") if err != nil { fmt.Printf("Set error:%s", err.Error()) return } // 执行命令: GET A // * 将刚刚写入的内容读取出来 s, err := c.Get("A") if err != nil { fmt.Printf("Get error:%s", err.Error()) return } fmt.Printf("A: %s", s) }
* Example2: 使用连接池
package main import ( xedis "git.oschina.net/yyzybb537/Xedis/client" "fmt" ) func main() { // 使用DialPool接口创建连接池 // * 连接成功后, 连接池的使用方式和单链接的方式完全一样! nRepeated := 10 // 连接池中链接的数量 c, err := xedis.DialPool(nRepeated, "127.0.0.1:8030") if err != nil { fmt.Printf("Dial error:%s", err.Error()) return } defer c.Close() // 执行命令: SET A str err = c.Set("A", "str") if err != nil { fmt.Printf("Set error:%s", err.Error()) return } // 执行命令: GET A s, err := c.Get("A") if err != nil { fmt.Printf("Get error:%s", err.Error()) return } fmt.Printf("A: %s", s) }
## 定制Xedis
对xedis的扩展需要使用golang语言,数据结构的扩展代码置于extend/structures目录下,View的扩展代码置于extend/view目录下。
### 扩展一个数据结构和相应的Cmd
扩展新的数据结构需要自定义一个struct,并符合如下interface:
type Structures interface { Name() string }
这个interface定义在interfaces/structures.go文件中,Name接口返回小写的结构名。
下面的代码定制了一个String结构, 其中的13-15行代码是将String结构体的工厂函数注册到系统中, 注意在14行代码的地方完成相关的初始化代码。
这个String结构定义了两个命令:Get Set,命令名大小写不敏感,且命令不可重复定义。
定义命令的方式是给String结构提供一个接口,接口名以Cmd开头,紧接着的下一个字符(**Tag**)必须是C、W、R中的一个,然后下一个字符必须是一个下划线,最后是命令名。
**Tag**:
C表示这个命令可以触发创建一个新的String对象; W表示会改变String的内容; R表示对String是只读操作。
生成后的代码会给每个命令接口加上读写锁,CW的接口会加写锁,只读接口会加上读锁;**因此,命令接口之间不要互相调用,以免死锁。**
命令接口的输入参数只支持有限的内置类型,list如下:
bool
int
int8
int16
int32
int64
uint
uint8
uint16
uint32
uint64
string
以及这些类型的切片(Slice), 比如:[]string, []int等等.
在一个参数表中,切片类型只能有一个。
命令接口的输出参数除了上面的类型以外,还可以在末尾增加一个error类型的参数,用于输出命令执行过程中的错误信息。
接口的帮助信息要以注释的形式写在接口定义的前一行,只能以"//"开头,这部分信息可以在执行./Xedis -D时看到,也可以在客户端调用xedis.HelpInfo()接口来获取。
1 package structures 2 3 import ( 4 "fmt" 5 I "git.oschina.net/yyzybb537/Xedis/interfaces" 6 "strconv" 7 ) 8 9 type String struct { 10 Value string 11 } 12 13 var stringRegister interface{} = I.RegisterStructures("string", func() I.Structures { 14 return &String{} 15 }) 16 17 func (this *String) Name() string { 18 return "string" 19 } 20 21 // Set string value or create 22 func (this *String) CmdC_Set(v string) { 23 this.Value = v 24 } 25 26 // Get value of string 27 func (this *String) CmdR_Get() string { 28 return this.Value 29 }
### 让定制生效
将上文定制的代码保存到extend/structures/string.go文件中,重新执行./build.sh进行编译,即可完成定制。
注意保存的文件名需要与结构体同名,而且是全小写。
此时执行./Xedis -D可以看到增加了一个Structure和两个命令:
$ ./Xedis -D ----- Commands ----- [Structure] string: SET func(v string) () summary: Set string value or create GET func() (string) summary: Get value of string --------------------
### 扩展一个View
View是Xedis中所有Key-Value的组织结构,默认使用的是hash表。 当我们有高性能的前缀搜索、后缀搜索、前缀批量操作等需求时,hash表就不再适用,此时需要自定义全新的View来替换掉默认的View。
为了讲解简单,我们扩展一个用slice管理key-value的View。
View需要符合以下interface:
type View interface { Name() string GetKey(key string) Structures SetKey(key string, s Structures) DelKey(key string) bool }
这个interface定义在interfaces/view.go文件中,Name接口返回小写的view名。
View的扩展与structures的扩展基本相同,代码如下:
package view import ( I "git.oschina.net/yyzybb537/Xedis/interfaces" ) type structWrap struct { key string s I.Structures } type ListView struct { list []*structWrap } var listViewRegister interface{} = I.RegisterView("list", func() I.View { return &ListView{ list : make([]*structWrap, 0) } }) func (this *ListView) Name() string { return "list" } func (this *ListView) GetKey(key string) I.Structures { for _, s := range this.list { if s.key == key { return s.s } } return nil } func (this *ListView) SetKey(key string, s I.Structures) { for _, s := range this.list { if s.key == key { return } } this.list = append(this.list, &structWrap{ key : key, s : s, }) } func (this *ListView) DelKey(key string) bool { for i, s := range this.list { if s.key == key { this.list[i] = this.list[len(this.list) - 1] this.list = this.list[:len(this.list) - 1] return true } } return false } func patternMatch(s, pattern string) bool { return true } // Get some key name by pattern func (this *ListView) CmdR_Keys(pattern string) (keys []string) { keys = make([]string, 0) for _, s := range this.list { if patternMatch(s.key, pattern) { keys = append(keys, s.key) } } return }
可以看到,这个View定义了CmdW_Keys接口,与数据结构的扩展一样的是,这个接口会生成一个名为KEYS的命令。但是不同之处在于,这个KEYS命令是专属于list这个View的,也就是说Xedis选用list来启动时这个命令才会生效。
客户端的调用View专属命令时要用c.ViewList().Keys接口来调用。
不同View之间,View专属命令是可以重名的。
GetKey/SetKey/DelKey以及自定义的命令接口都会被加上读写锁,因此**不能相互调用,不然会死锁!**
### 扩展一个全局命令
有时候我们需要一些操作多个key或不操作任何key的命令,我们称之为全局命令。
redis中有个MGET命令,用于一次性获取多个String的值。
下面以此为例讲解一下全局命令的扩展。
全局命令的扩展和普通命令基本一样,唯一的差别在于全局命令定义的是一个全局的函数,不再是类成员函数。
// Multiply GET command func CmdR_MGet(keys []string) []string { r := make([]string, 0) for _, key := range keys { // 通过View找到key对应的数据结构 s := I.GetView().GetKey(key) if s == nil { r = append(r, "") continue } // 判断数据结构是否是string if s.Name() != "string" { r = append(r, "") continue } r = append(r, s.(*String).CmdR_Get()) } return r }
代码中的`I.GetView()`是View的全局访问点,在这里可以调用当前View的接口找到key对应的数据结构.
将这段代码追加到string.go的文件尾部,重新执行./build.sh,一个全局命令就创建好了。
$ ./Xedis -D ----- Commands ----- [Structure] string: SET func(v string) () summary: Set string value or create GET func() (string) summary: Get value of string -------------------- ----- Commands ----- [View] Global: MGET func(keys []string) ([]string) summary: Multiply GET command --------------------
一、内存数据库: 在SQLite中,数据库通常是存储在磁盘文件中的。然而在有些情况下,我们可以让数据库始终驻留在内存中。最常用的一种方式是在调用sqlite3_open()的时候,数据库文件名参数传递":memory:",如: rc = sqlite3_open(":memory:", &db); 在调用完以上函数后,不会有任何磁盘文件被生成,取而代之的是,一个新的数据库在纯内存中被成功创建了。
名称: txt_to_pika 位置: /pika-tools/txt_to_pika 目的: 将txt文本的kv数据写入pika 使用: Usage: ./txt_to_pika txt pika_ip pika_port -n [thread_num] -t [ttl] -p [password] example: ./txt_to_pika data.txt 127.0.0.1 9921 -
主要内容:程序员的幽默计算机要处理的信息是多种多样的,如数字、文字、符号、图形、音频、视频等,这些信息在人们的眼里是不同的。但对于计算机来说,它们在内存中都是一样的,都是以二进制的形式来表示。 要想学习编程,就必须了解二进制,它是计算机处理数据的基础。 内存条是一个非常精密的部件,包含了上亿个电子元器件,它们很小,达到了纳米级别。这些元器件,实际上就是电路;电路的电压会变化,要么是 0V,要么是 5V,只有这两种电压。
问题内容: 我想将稀疏矩阵(156060x11780)转换为数据帧,但出现内存错误,这是我的代码 我有一个问题 。我该如何解决? 问题答案: 尝试这个: 更新: 对于Pandas 0.20+,我们可以直接从稀疏数组构造:
我在研究内存数据库的概念。有关这方面的文章说, 内存数据库系统是一种将数据完全存储在主存中的数据库管理系统。 他们讨论了这个概念的优点和缺点。 我的问题是如果这些数据库管理系统将数据完全存储在主存储器中, 停电后所有数据都消失了吗??? 还是有办法保护数据???
名称: pika_to_txt 位置: /pika-tools/pika_to_txt 目的: 离线迁移pika的kv数据到txt文本 txt格式: [key_length][key][value_length][value] 注意: 长度使用uint32_t 使用: Usage: Pika_To_Txt reads kv data from Blackwidow DB and w