简而言之的新功能
|
如上图所示,左侧为1号事务,在不同时间点对id=1的记录分别查询了三次。右侧为2号事务,对id=1的记录进行了更新。更新前该记录只有一个版本,更新好变成了两个版本。 1号事务在RC隔离级别下每次执行select请求都会生成一个最新的read_view,前两次查询生成的全局事务活跃列表中包含trx2,因此根据MVCC规定查到的记录为老版本;最后一次查询的时间点位于2号事务提交之后,因此生成的全局活跃事务列表中不包含trx2,此时在根据MVCC规定查到的记录就是最新版本记录。 Repeatable Read(技术解读:写写并发使用X锁,读写并发使用MVCC避免不可重复读;当前读使用Gap锁避免幻读)
和RC模式不同,RR模式下事务不会再每次执行select的时候生成最新的read_view,而是在事务第一次select时就生成read_view,后续不会再变更,直至当前事务结束。这样可以有效避免不可重复读,使得当前事务在整个事务过程中读到的数据都保持一致。示意图如下所示: read_view是实现MVCC的一个关键点,它用来判断记录的哪个版本对当前事务可见。如果当前事务要读取某行记录,该行记录的版本号(事务ID)为trxid,那么: 1. 如果trxid < up_trx_id,说明该行记录所在的事务已经在当前事务创建之前就提交了,所以该行记录对当前事务可见。 2. 如果trxid > low_trx_id,说明该行事务所在的事务是在当前事务创建之后才开启,所以该行记录对当前事务不可见。 3. 如果up_trx_id < trxid < low_trx_id, 那么表明该行记录所在事务在本次新事务创建的时候处于活动状态。从up_trx_id到low_trx_id进行遍历,如果trxid等于他们之中的某个事务id的话,那么不可见,否则可见。 以下面行记录为例,该行记录存在多个版本(trx2、trx5、trx7以及trx12),其中trx12是最新版本。看看该行记录中哪个版本对当前事务可见。 1. 该行记录的最新版本为trx12,与当前事务read_view进行对比发现,trx12大于当前活跃事务列表中的最大事务trx10,表示trx12是在当前事务创建之后才开启的,因此不可见。 2. 再查看该行记录的第二个最新版本为trx7,与当前事务read_view对比发现,trx7介于当前活跃事务列表最小事务ID和最大事务ID之间,表明该行记录所在事务在当前事务创建的时候处于活动状态,在活跃列表中遍历发现trx7确实存在,说明该事务还没有提交,所以对当前事务不可见。
3. 继续查看该记录的第三个最新版本trx5,也介于当前活跃事务列表最小事务ID和最大事务ID之间,表明该行记录所在事务在当前事务创建的时候处于活动状态,但遍历发现该版本并不在活跃事务列表中,说明trx5对应事务已经提交(注:事务提交时间与事务编号没有任何关联,有可能事务编号大的事务先提交,事务编号小的事务后提交),因此trx5版本行记录对当前事务可见,直接返回。 可能的解决方案 由于不能能使用上面的kninesis方案,因此需要新的解决方案来解决问题。可选的方案有以下几种。 Elixir 如前的架构介绍,系统使用Elixir服务检查客户访问权限。该服务是私有的,只能从虚拟私有云(VPC)中访问。由于从未遇到过该服务的任何可扩展性问题,并且大多数逻辑已经存在。所以可选择简单地从该服务中将日志发送到Kinesis,而跳过Node.js服务层。这是一个值得尝试的方案。 做了一番改进后,系统进行了测试。效果会好一点,但仍然不是很佳。系统的基准测试表明,GC垃圾收集的水平仍然很高,并且在使用日志时仍会有5xx的日志返回给用户。 Golang 系统也考虑到Golang。这是一个很好的选择方案,但是,毕竟Golang也是一种垃圾收集语言。虽然可能可以实现比上述更高效,但随着规模的扩展,很可能还会遇到类似的问题。考虑到这些限制,系统需要一个更好的选择。 以Rust为核心进行重新架构 在系统最初的实现和备份中,核心问题都是相同的:垃圾回收。解决方案是使用一种具有内存管理更好的并且没有垃圾回收的语言。那么可选择的语言就到了Rust。 Rust Rust不是垃圾收集的语言。Rust依赖于称为变量生命周期和所有权的概念。所有权是Rust的最独特功能,它使Rust无需垃圾收集器即可保证内存安全。 所有权是一个经常使Rust难以学习和编写的概念,但又使它非常适合像这个项目遇到的情况。Rust中的每个值都有一个所有者变量,因此在内存中有一个分配点。一旦该变量超出范围,内存将会立即释放。 由于提取日志所需的代码很小,应该非常值得尝试。为了对此进行测试,通过问题的瓶颈:向Kinesis发送大量数据。第一个基准测试非常成功。 所以Rust最终成了救世主,最后决定将原型充实并在生产系统的部署。 在这些实验过程中,并没有直接使用Rust直接替换原始的Node.js服务,而是重构了日志提取的大部分架构。新服务的核心是通过Envoy代理,在其中Rust应用程序作为辅助工具。
新架构流程 (编辑:柳州站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
