热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

关于golang:rosedb-事务实践

事务是传统关系型数据库中必不可少的性能,例如Mysql、Oracle、PostgreSql都反对事务,然而在NoSQL数据库中,事务的概念比拟弱化,在实现上也没有关系型数据库那么简单。

一、前言

事务是传统关系型数据库中必不可少的性能,例如 Mysql、Oracle、PostgreSql 都反对事务,然而在 NoSQL 数据库中,事务的概念比拟弱化,在实现上也没有关系型数据库那么简单。

然而为了数据的残缺一致性,大多数 k-v 都会实现事务的根本个性,例如 k-v 数据库的两大鼻祖 LevelDB 和 RocksDB,一些 Go 语言实现的开源 k-v 也都反对事务,例如 Bolt,Badger 等。

rosedb 的事务目前刚实现了一个高级的版本,代码还比较简单,只不过在我的预期构思内,后续可能会缓缓演变得更加简单。

须要阐明的是,在实现 rosedb 的事务之前,我对事务的了解也仅限于 ACID 这些根底概念,所以这次实现齐全是摸着石头过河,可能存在一些槽点,大家有什么疑难能够指出来,我前面也会持续学习并欠缺。

二、基本概念

说到事务,就很容易想到事务的 ACID 个性,带大家回顾一下:

  • 原子性(Atomicity):一个事务中的所有操作,要么全副实现,要么全副失败,不会在中间环节完结。如果事务执行过程中产生谬误,可能被回滚至事务开始之前的状态。
  • 一致性(Consistency):在事务开始前和完结后,数据库的完整性没有被毁坏,这意味着数据状态始终合乎预期。
  • 隔离性(Isolation):隔离性形容的是多个执行中的事务相互影响的水平,有常见的四种隔离级别,示意事务之间不同的影响水平:

    • 读未提交(read uncommitted):一个事务还未提交,另一个事务就能看到它所做的批改(存在脏读)
    • 读提交(read committed):一个事务对数据的批改,只能等到它提交之后,其余事务能力看到(没有脏读,然而不可反复读)
    • 可反复读(repeatable read):一个事务在执行过程中获取到的数据,和事务开始时的数据统一(没有脏读,能够反复读,然而有幻读)
    • 串行化(serializable):读写互斥,防止事务并发,一个事务必须等到前一个事务提交后能力执行(无脏读,可反复读,无幻读)
  • 持久性(Durability):一个事务提交之后,它所做的批改是永恒的,即便数据库解体之后也可能保障平安。

ACID 的概念看起来挺多,但并不难理解,要实现事务,其实就是保障在数据读写时,满足事务的这几个基本概念,其中 AID 是必须保障的。

而 Consistency 即一致性,能够简略了解为它就是事务的最终目标,数据库通过 AID 来保障一致性,而咱们在利用层面也要保障一致性,如果咱们写入的数据自身逻辑上就是谬误的,那么即便数据库事务再欠缺,也无奈保障一致性。

三、具体实现

在解说事务实现之前,先来看看 rosedb 当中事务的根本用法:

// 关上数据库实例
db, err := rosedb.Open(rosedb.DefaultConfig())
if err != nil {
   panic(err)
}

// 在事务中操作数据
err = db.Txn(func(tx *Txn) (err error) {
   err = tx.Set([]byte("k1"), []byte("val-1"))
   if err != nil {
      return
   }
   err = tx.LPush([]byte("my_list"), []byte("val-1"), []byte("val-2"))
   if err != nil {
      return
   }
   return
})

if err != nil {
   panic(fmt.Sprintf("commit tx err: %+v", err))
}

首先还是会关上一个数据库实例,而后调用 Txn 办法,这个办法的入参是一个函数,事务的操作都在这个函数中实现,在提交的时候一次性执行。

像这样应用的话,事务会主动提交,当然也能够手动开启事务并提交,并且在有谬误产生时手动回滚,如下:

// 关上数据库实例
db, err := rosedb.Open(rosedb.DefaultConfig())
if err != nil {
   panic(err)
}

// 开启事务
tx := db.NewTransaction()
err = tx.Set([]byte("k1"), []byte("val-1"))
if err != nil {
   // 有谬误产生时回滚
   tx.Rollback()
   return
}

// 提交事务
if err = tx.Commit(); err != nil {
   panic(fmt.Sprintf("commit tx err: %+v", err))
}

当然还是举荐第一种用法,省去了手动提交事务和回滚。

Txn 办法示意的是读写事务,此外还有一个 TxnView 办法,示意的是只读事务,应用形式完全一致,只不过在 TxnView 办法内的写入命令都会被疏忽。

db.TxnView(func(tx *Txn) error {
   val, err := tx.Get([]byte("k1"))
   if err != nil {
      return err
   }
   // 解决 val

   hVal := tx.HGet([]byte("k1"), []byte("f1"))
   // 解决 hVal
  
   return nil
})

理解了事务的 ACID 基本概念和 rosedb 事务根本用法之后,再来看看在 rosedb 当中,事务到底是怎么实现的,也能够认为是如何来保障 AID 个性的。

3.1 原子性

后面曾经说到,原子性指的是的事务执行的完整性,要么全副胜利,要么全副失败,不能停留在中间状态。

要实现原子性其实不难,能够借助 rosedb 的写入个性来解决。先来回顾一下 rosedb 数据写入的根本流程,两个步骤:首先数据会先落磁盘,保障可靠性,而后更新内存中的索引信息。

对于一个事务操作,要保障原子性,能够先将须要写入的数据在内存中暂存,而后在提交事务的时候,一次性写入到磁盘文件当中。

这样存在一个问题,那就是在批量写入磁盘的时候出错,或者零碎解体了怎么办?也就是说可能有一些数据曾经写入胜利,有一些写入失败了。依照原子性的定义,这一次事务没有提交实现,是有效的,那么应该怎么晓得曾经写入的数据是有效的呢?

目前 rosedb 采纳了一种最容易了解,也是比较简单的一种方法来解决这个问题。

具体做法是这样的:每一次事务开始时,都会调配一个全局惟一的事务 id,须要写入的数据都会带上这个事务 id 并写入到文件。当所有的数据写入磁盘实现之后,将这个事务 id 独自存起来(也是写入到一个文件当中)。在数据库启动的时候,会先加载这个文件中的所有事务 id,保护到一个汇合当中,称之为已提交的事务 id。

这样的话,就算数据在批量写入时出错,因为没有寄存对应的事务 id,所以在数据库启动并取出数据构建索引的时候(回顾一下 rosedb 的启动流程),可能查看到数据对应的事务 id 没有在已提交事务 id 汇合当中,所以会认为这些数据有效。

大多数 LSM 流派的 k-v 都是利用相似的思路来保障事务的原子性,例如 rocksdb 是将事务中所有的写入都寄存到了一个 WriteBatch 中,在事务提交的时候一次性写入。

3.2 隔离性

目前 rosedb 反对两种事务类型:读写事务和只读事务。只能同时开启一个读写事务,只读事务则能够同时开启多个。

在这种模式下,读会加读锁,写会加写锁,也就是说,读写会互斥,不能同时进行。能够了解为这是四种隔离级别中的串行化,它的长处是简略易实现,毛病是并发能力差。

须要阐明的是,目前的这种实现在前面大概率会进行调整,我的构想是能够应用快照隔离的形式来反对读提交或者可反复读,这样数据读取可能读到历史版本,不会造成写操作的阻塞,只不过在实现上要简单得多了。

3.3 持久性

持久性须要保证数据曾经写到了非易失性存储介质当中,比方最常见的有磁盘或者 SSD,这样即便产生零碎异样,也可能保障数据安全。

在 rosedb 当中,写入数据时,如果走默认的刷盘策略,是将数据写到了操作系统页缓存当中,实际上并没有落磁盘。如果操作系统还没来来得及将页缓存的数据刷到磁盘,那么会造成数据失落。这样虽不能齐全保障持久性,但性能是绝对更好的,因为 Sync 刷磁盘是一次极其慢速的操作。

如果在启动 rosedb 的时候指定了配置项 Sync 为 true,那么每次写入都会强行 Sync,可能保证数据不丢,然而写性能会降落。

理论应该怎么抉择,能够依据本人的应用场景来,如果零碎稳固,对性能的要求较高,并且可能容忍失落大量数据,那么能够采纳默认策略,即 Sync 为 false,否则能够强制刷盘。

四、缺点

通过下面的简略剖析,能够看到 rosedb 曾经根本实现了事务的 AID 个性,整体来说还是挺简略的,易于学习和应用,并且可能很好了解便于进一步的扩大。当然,目前也存在一些缺点亟待解决。

第一个便是下面提到的隔离级别的问题,目前这种形式太过简略,应用一把全局大锁搞成了串行化,后续能够思考只锁定须要操作的某个 key,减小锁的粒度。

还有一个问题便是,因为 rosedb 反对了多种数据结构,然而像 List、ZSet 这种构造,在事务中反对全副命令的难度较大,因而目前 List 只反对了 LPush 和 RPush,ZSet 只反对了ZAdd、ZScore、ZRem 命令。

次要的起因是如果在事务中对曾经存在的 key 进行读写,那么去反对像范畴查找这种类型的命令就会很艰难,目前我还没有想到比拟好的解决方案。

最初,附上我的项目地址:https://github.com/roseduan/rosedb,欢送各位前来围观吐槽。

Ps:rosedb 也欢送对存储、k-v 感兴趣的敌人退出,也可加我微信进行深入探讨交换。


推荐阅读
  • 从0到1搭建大数据平台
    从0到1搭建大数据平台 ... [详细]
  • 解决Bootstrap DataTable Ajax请求重复问题
    在最近的一个项目中,我们使用了JQuery DataTable进行数据展示,虽然使用起来非常方便,但在测试过程中发现了一个问题:当查询条件改变时,有时查询结果的数据不正确。通过FireBug调试发现,点击搜索按钮时,会发送两次Ajax请求,一次是原条件的请求,一次是新条件的请求。 ... [详细]
  • php更新数据库字段的函数是,php更新数据库字段的函数是 ... [详细]
  • 本文详细介绍了 InfluxDB、collectd 和 Grafana 的安装与配置流程。首先,按照启动顺序依次安装并配置 InfluxDB、collectd 和 Grafana。InfluxDB 作为时序数据库,用于存储时间序列数据;collectd 负责数据的采集与传输;Grafana 则用于数据的可视化展示。文中提供了 collectd 的官方文档链接,便于用户参考和进一步了解其配置选项。通过本指南,读者可以轻松搭建一个高效的数据监控系统。 ... [详细]
  • MySQL Decimal 类型的最大值解析及其在数据处理中的应用艺术
    在关系型数据库中,表的设计与SQL语句的编写对性能的影响至关重要,甚至可占到90%以上。本文将重点探讨MySQL中Decimal类型的最大值及其在数据处理中的应用技巧,通过实例分析和优化建议,帮助读者深入理解并掌握这一重要知识点。 ... [详细]
  • 在CentOS 7环境中安装配置Redis及使用Redis Desktop Manager连接时的注意事项与技巧
    在 CentOS 7 环境中安装和配置 Redis 时,需要注意一些关键步骤和最佳实践。本文详细介绍了从安装 Redis 到配置其基本参数的全过程,并提供了使用 Redis Desktop Manager 连接 Redis 服务器的技巧和注意事项。此外,还探讨了如何优化性能和确保数据安全,帮助用户在生产环境中高效地管理和使用 Redis。 ... [详细]
  • Oracle字符集详解:图表解析与中文乱码解决方案
    本文详细解析了 Oracle 数据库中的字符集机制,通过图表展示了不同字符集之间的转换过程,并针对中文乱码问题提供了有效的解决方案。文章深入探讨了字符集配置、数据迁移和兼容性问题,为数据库管理员和开发人员提供了实用的参考和指导。 ... [详细]
  • 在CentOS上部署和配置FreeSWITCH
    在CentOS系统上部署和配置FreeSWITCH的过程涉及多个步骤。本文详细介绍了从源代码安装FreeSWITCH的方法,包括必要的依赖项安装、编译和配置过程。此外,还提供了常见的配置选项和故障排除技巧,帮助用户顺利完成部署并确保系统的稳定运行。 ... [详细]
  • 本文详细介绍了MySQL数据库的基础语法与核心操作,涵盖从基础概念到具体应用的多个方面。首先,文章从基础知识入手,逐步深入到创建和修改数据表的操作。接着,详细讲解了如何进行数据的插入、更新与删除。在查询部分,不仅介绍了DISTINCT和LIMIT的使用方法,还探讨了排序、过滤和通配符的应用。此外,文章还涵盖了计算字段以及多种函数的使用,包括文本处理、日期和时间处理及数值处理等。通过这些内容,读者可以全面掌握MySQL数据库的核心操作技巧。 ... [详细]
  • 在PHP中如何正确调用JavaScript变量及定义PHP变量的方法详解 ... [详细]
  • C++ 异步编程中获取线程执行结果的方法与技巧及其在前端开发中的应用探讨
    本文探讨了C++异步编程中获取线程执行结果的方法与技巧,并深入分析了这些技术在前端开发中的应用。通过对比不同的异步编程模型,本文详细介绍了如何高效地处理多线程任务,确保程序的稳定性和性能。同时,文章还结合实际案例,展示了这些方法在前端异步编程中的具体实现和优化策略。 ... [详细]
  • 本文深入探讨了NoSQL数据库的四大主要类型:键值对存储、文档存储、列式存储和图数据库。NoSQL(Not Only SQL)是指一系列非关系型数据库系统,它们不依赖于固定模式的数据存储方式,能够灵活处理大规模、高并发的数据需求。键值对存储适用于简单的数据结构;文档存储支持复杂的数据对象;列式存储优化了大数据量的读写性能;而图数据库则擅长处理复杂的关系网络。每种类型的NoSQL数据库都有其独特的优势和应用场景,本文将详细分析它们的特点及应用实例。 ... [详细]
  • 在 Axublog 1.1.0 版本的 `c_login.php` 文件中发现了一个严重的 SQL 注入漏洞。该漏洞允许攻击者通过操纵登录请求中的参数,注入恶意 SQL 代码,从而可能获取敏感信息或对数据库进行未授权操作。建议用户尽快更新到最新版本并采取相应的安全措施以防止潜在的风险。 ... [详细]
  • 本文详细介绍了使用 Python 进行 MySQL 和 Redis 数据库操作的实战技巧。首先,针对 MySQL 数据库,通过 `pymysql` 模块展示了如何连接和操作数据库,包括建立连接、执行查询和更新等常见操作。接着,文章深入探讨了 Redis 的基本命令和高级功能,如键值存储、列表操作和事务处理。此外,还提供了多个实际案例,帮助读者更好地理解和应用这些技术。 ... [详细]
  • 开发心得:利用 Redis 构建分布式系统的轻量级协调机制
    开发心得:利用 Redis 构建分布式系统的轻量级协调机制 ... [详细]
author-avatar
手机用户2502930741
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有