
关于用Golang编写的称为Coffer
的嵌入式键值数据库的简短说明。 如果是非常简短的话:当数据库处于停止状态时,数据在磁盘上,当数据库启动时,数据将被复制到内存中。 读取来自内存。 在记录期间,内存数据被更改,并且更改被写入磁盘日志。 存储数据的最大大小受RAM大小限制。 该API允许您为数据库记录创建标头,并在事务中使用它们,同时保持数据的一致性。
但首先,进行一些抒情介绍。 从前,当草丛变绿时,我花了很多钱为Go应用程序嵌入了一个键值数据库。 环顾四周并碰到不同的程序包,我不知何故(主观地)找不到我想要的东西,而是将解决方案与外部关系数据库一起应用了。 很好的解决方案。 但正如他们所说,发现了一个勺子,但沉积物仍然存在。 首先,我想要的是完全本机的,写在Go数据库上的本机。 有这样的东西,看起来很棒。 但是,其中没有一百万。 当您认为程序员在世界上很少见,而他一生中没有编写数据库,框架或休闲游戏时,这甚至令人惊讶。
好吧,您可以尝试将二十一点和其他物品与自行车堆在一起。 同时,每个人都知道,或者至少是猜测,即使乍看之下,即使编写一个简单的键值数据库也似乎很简单。 但是实际上,一切都变得更加有趣(而且确实发生了)。 我对ACID感到好奇,并对交易感到担忧。 从财务角度讲,真正的交易更有可能,因为 那时我忙于金融科技。
资料安全性
请考虑以下情况:在具有活动记录的应用程序运行期间,计算机中的电源被铜制水槽覆盖,并且磁盘没有损坏。 如果此时应用程序从数据库中接收到ok
,那么来自此操作的数据将不会丢失。 如果应用程序收到否定答案,则该操作当然不会完成。 好了,在应用程序发送了一个请求但没有收到响应的情况下:该操作很可能没有完成,但是该操作落入日志的可能性很小,但是恰好在发送响应时,电源关闭了。
在最后一种情况下如何找出上次操作发生了什么? 这是一个有趣的问题。 间接地,您可以通过从数据库启动新应用程序后查看关注记录的值来猜测(得出结论)。 但是,如果操作足够频繁,恐怕将无济于事。 您可以看到最后一个日志的文件(它将具有最大的编号),但是手动操作很不方便。 我认为,将来您可以在API中添加查看日志的功能(自然,在这种情况下,不应删除日志)。
坦白说,我本人并未将电源线从插座中拔出,因为 我不想为了检查数据库而冒险。 在测试中,我只是破坏了正常的日志文件,在这种情况下,一切都会按预期进行。 但是,没有实际使用数据库的经验,它在产品上没有作用,并且存在风险。 但是,对于宠物项目,我认为数据库可以无所畏惧地使用。 一般而言,通常的免责声明,没有任何保证。
目前,该数据库并未受到保护,无法在配置为使用同一目录的两个不同应用程序中使用(或相同,在这里无关紧要)。 请考虑这一刻! 但是,由于数据库是内置的,因此在参数中传递了某种引用类型,因此绝对不值得在并行goroutine中对其进行更改。
构型
数据库中有很多参数可以配置,但是几乎所有参数都具有默认值,因此所有内容都可以放入一个短行cof, err, wrn := Db(dirPath).Create()
返回错误(如果发生错误,请进一步使用数据库禁止)和警告,您可以了解,但这不会干扰数据库的运行。
我不会用繁琐的描述弄乱文本,如有必要,请在存储库的自述文件中观看它们-github.com/claygod/coffer/blob/master/README_RU.md#config注意,连接事务处理程序的Handler方法,我将写几行内容下面,我只列出它们:
- Db(目录路径)
- BatchSize(batchSize)
- LimitRecordsPerLogfile(limitRecordsPerLogfile)
- 暂停(100 *时间。第二)
- LogsByCheckpoint(1000)
- AllowStartupErrLoadLogs(true)
- MaxKeyLength(maxKeyLength)
- MaxValueLength(maxValueLength)
- MaxRecsPerOperation(1,000,000)
- RemoveUnlessLogs(true)
- 内存限制(100 * 1,000,000)
- 限制磁盘(1000 * 1,000,000)
- 处理程序(“ handler1”和&handler1)
- 处理程序(“ handler2”,&handler2)
- 处理程序(映射[string] *处理程序)
- 建立()
API
我尽可能简化了API,对于键值基础,不要太聪明:
- 开始-启动数据库
- 停止-停止数据库
- StopHard-停止,无论现在正在执行什么操作(也许我将其删除)
- 保存-保存数据库当前状态的快照
- 写入-向数据库添加一条记录
- WriteList-将多个记录添加到数据库(严格和可选模式)
- WriteListUnsafe-将多个记录添加到数据库中而不考虑数据安全性
- 读取-通过按键获得一条记录
- ReadList-获取记录列表
- ReadListUnsafe-获取记录列表,而不考虑数据安全性
- 删除-删除一条记录
- DeleteList-以严格/可选模式删除多个记录
- 交易-执行交易
- 计数-数据库中有多少条记录
- CountUnsafe-数据库中有多少条记录(速度稍快,但不安全)
- RecordsList-所有数据库键的列表
- RecordsListUnsafe-所有数据库密钥的列表(速度稍快,但不安全)
- RecordsListWithPrefix-具有指定前缀的键列表
- RecordsListWithSuffix-具有指定结尾的键的列表
API的简短说明:
- 严格模式-全部执行或不执行任何操作。
- 可选模式-尽一切可能。
- StopHard-可能应从API中删除此方法,直到确定为止。
- 所有RecordsList方法都不快,因为 目前,商店中没有索引,但这是一次全扫描。
- 所有不安全的方法都更快,但使用它们时并不意味着一致性。 在停止的数据库上使用它们来快速填充或以其他方式使用它们是合乎逻辑的。
- 跟随者监视数据库快照的定期更新,因此,在您确实要创建新快照的某些特殊情况下,Save方法最有可能(直到我想到这种情况,但也许是这样)。
一个简单的用例:
package main import ( "fmt" "github.com/claygod/coffer" ) const curDir = "./" func main() {
交易次数
如上所述,我对事务的定义可能与数据库构建中普遍接受的定义不一致,也许它们仅由一个概念结合在一起。 在一个特定的实现中,事务是在数据库配置阶段( Handler
方法)指定的特定标头。 当我们使用此标头调用事务时,数据库将阻止该标头将使用的记录,并将其当前值传输到该标头。 标头根据需要处理此数据,并返回数据库的新值,并将其保存为一百。 之后,记录将被解锁并可供其他操作使用。
回购中有一些示例很好地揭示了使用事务的本质。 出于好奇,我举了一个小小的财务示例,其中涉及借方和贷方操作,转移,购买和出售。 编写此示例非常容易,与此同时,这种超高的实现也非常一致,适合用于各种金融解决方案或物流中。
重要的一点:处理程序代码未存储在数据库中。 我本来打算将其存储在日志中,但是在我看来,这太浪费了,因此没有使它复杂化,因此,负责使用不同数据库的处理程序之间的一致性的责任始于开发该数据库的代码。 如果应用程序和数据库停止崩溃,则绝对不能更改处理程序。 在这种情况下,您必须首先启动数据库,然后定期停止它-将创建一个新的数据快照。 为了不引起混淆,我建议您在处理程序的名称中使用版本号。
接收和处理回复
数据库返回报告,这些报告指示响应的状态以及数据。 由于代码很多,并且编写一个处理每个代码的开关很麻烦,因此您可能需要检查大约。 不应这样做。 事实是,代码的状态可能为“正常”,“错误”,“紧急”。 好的,一切都清楚了,但是其他两个呢? 如果状态为“错误”,则说明特定操作已完成或未完成。 必须在应用程序中正确处理此错误。 但是,可以(并且有必要)对数据库进行进一步的工作。 另一件事恐慌-应该停止使用数据库。
检查IsCodeError
可简化所有错误的工作,因此,如果您对这些细节不感兴趣,请继续工作。
IsCodePanic
检查涵盖了必须停止使用数据库的所有情况。
在简单的情况下,三重开关足以处理响应:
IsCodeOk
继续IsCodeOk
工作IsCodeError
记录报告中的错误并进一步处理IsCodePanic
记录报告中的错误并停止使用数据库
台面
对于名称,选择了将单词
转换为英语的选项之一,我当然更喜欢box
,但是这个单词太流行了,我希望coffer
会这样做。
在我看来,关于ACID的话题似乎比较全面,所以我想说,科夫致力于这一点,但不是事实,我也不声称他成功了。
性能表现
我立即编写了一个数据库,其中考虑了并发性和竞争性。 正是在这种模式下,它显示了其有效性(尽管可能说得太大了)。 在下面的结果中,基准测试显示了200k rps的带宽。 当然,这是一个人造工作台,实际情况将完全不同,因为 在很大程度上取决于所记录数据的大小,已记录数据的数量,铁的性能和月相。 但是这种趋势至少是可以理解的。 如果数据库在单线程模式下使用,则每个请求仅在收到上一个请求的答案后才执行,速度会很慢,我建议您查看其他数据库,而不要查看Coffer。
- BenchmarkCofferTransactionSequence-4 2000 227928 ns / op
- BenchmarkCofferTransactionPar32HalfConcurent-4 100000 4199 ns / op
顺便说一句,如果有人花时间并且倾向于自己在Coffer中的存储库,如果可能的话,运行躺在其中的工作台。 我对性能将显示数据库的机器非常感兴趣。 首先,当然,这完全取决于磁盘。 在我最近购买了新的三星EVO之后,这一点对我而言尤其明显。 但是不用担心,这不能代替死磁盘。 旧的东芝继续正常运行,现在存储了我的视频档案。
内置的内存手表仍然是一个简单的地图,甚至没有分成多个部分。 当然,改进它可能很棒,例如,使它可以快速通过前缀和后缀选择键。 虽然我没有这样做,tk。 我在事务中看到的主要功能(如我所说的DB芯片)以及事务性能的瓶颈将与磁盘一起使用,然后才与内存一起使用。
执照
现在,许可证允许您在数据库中存储多达一千万条记录,在我看来,这已经足够了。 正在开发数据库的进一步计划。
总的来说,对于我来说,将数据库作为一个包使用,并主要关注其API很有意思。
结论
最近,我经常遇到编写具有高可用性特征的服务的任务。 不幸的是,由于事实上这几乎总是意味着存在多个实例,因此在这种情况下使用内置数据库是不值得的。 在一个实例中仍然存在常规应用程序或服务的选项。 在我看来,这是一种较为罕见的情况,但无论如何,在这种情况下,最好有一个数据库尝试尽可能地保存存储在其中的数据。 我创建的保险箱正在尝试解决此类问题。 让我们看看他是如何做到的。
致谢
- 致读完本文的所有人
- 希望分享意见的评论员
- 发送有关错别字和文本错误的个人信息
- 邻居在晚上打开音乐
参考文献
数据库存储库
俄语描述