【数据库】基于时间戳的并发访问控制,乐观模式,时间戳替代形式及存在的问题,与封锁模式的对比

使用时间戳的并发控制

专栏内容

  • 手写数据库toadb
    本专栏主要介绍如何从零开发,开发的步骤,以及开发过程中的涉及的原理,遇到的问题等,让大家能跟上并且可以一起开发,让每个需要的人成为参与者。
    本专栏会定期更新,对应的代码也会定期更新,每个阶段的代码会打上tag,方便阶段学习。

开源贡献

  • toadb开源库

个人主页:我的主页
管理社区:开源数据库
座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.

文章目录

  • 使用时间戳的并发控制
  • 前言
  • 概述
  • 时间戳介绍
    • 记录时间戳的方法
    • 事务提交的记录
  • 可以解决的问题
    • 过晚的读
    • 过晚的写
    • 脏数据的问题
    • mysql中的表现
  • 基于时间戳调度的规则
    • 调度器选择
    • 读写请求的处理
  • 多版本时间戳
  • 时间戳与封锁
  • 总结
  • 结尾

前言

随着信息技术的飞速发展,数据已经渗透到各个领域,成为现代社会最重要的资产之一。在这个大数据时代,数据库理论在数据管理、存储和处理中发挥着至关重要的作用。然而,很多读者可能对数据库理论感到困惑,不知道如何选择合适的数据库,如何设计有效的数据库结构,以及如何处理和管理大量的数据。因此,本专栏旨在为读者提供一套全面、深入的数据库理论指南,帮助他们更好地理解和应用数据库技术。

数据库理论是研究如何有效地管理、存储和检索数据的学科。在现代信息化社会中,数据量呈指数级增长,如何高效地处理和管理这些数据成为一个重要的问题。同时,随着云计算、物联网、大数据等新兴技术的不断发展,数据库理论的重要性日益凸显。

因此,本专栏的分享希望可以提高大家对数据库理论的认识和理解,对于感兴趣的朋友带来帮助。

概述

在数据库中如何保证并发事务时,数据的一致性,也就是可串行化,会有采用调度器来进行协调各事务中动作的顺序,以衣是否可以执行等。调度器采用的模型主要有几种:

  • 基于封锁的调度模型
  • 基于时间戳的调度模型
  • 基于有效性确认的调度模型

前几篇博文中分享了基于封锁的调度模型,本文主要介绍基于时间戳的调度模型,主要从时间戳的概念,可以保证的行为和存在的问题,调度规则,以及多版本的优化,与封锁模型的联合使用等方面进行介绍。

时间戳介绍

也就是记录上次读和写每个数据库元素的事务时间点,同时每个事务也有一个时间戳,记录它的开始时间点。

当有事务要请求该数据库元素时,比较这两个时间,根据事务的时间戳来调度,来确保串行调度。

记录时间戳的方法

  • 理论上当多个事务开始的时间间隔大于时间最小计数时,使用时间来记录是可以达到目标的,但是往往时间的精度不足以记录多个同时开始的事务。

  • 调度器维护一个计时器。每当一个事务开始时,计数器就加1,而新值成为该事务的时间戳。这种方法与时间无关,但是它们具有时间的特性,单调递增,不会重复,总是保证晚的事务比开始早的事务具有更高的时间戳;

事务提交的记录

当一个事务T读到另一事务U所写的数据,这一行为也是符合串行化规则,但是事务U最后中止了,并没有提交,这样事务T读到的是脏数据,这一问题肯定会导致数据库状态变得不一致,这是任何调度器都要防止的脏读。

除了两个事务和数据库元素上的时间戳外,还需要记录一个事务的提交状态位,当事务没有提交时,调度器也需要阻止其它事务的访问请求。

可以解决的问题

假如事务在开始的那一时刻就立即执行结束,那也就不会发生非可串行化的问题。往往事务中的各个动作都会持续一段时间,这就会过晚读和过晚写的问题发生,而当事务中止时,读取的此事务写的数据,就会发生脏读的情况。

过晚的读

  • 问题描述
    事务执行的时间轴是这样的

在这里插入图片描述

如图所示,事务T的读在事务U的写之后,而事务U的开始时间晚于事务T,这就导致事务T读到的数据不一致。

  • 解决方法
    当事务T的进行读请求时,发现当前数据元素上的时间戳晚于自己的事务开始时间戳时,事务T应该是需要中止,它什么都不能做了。

过晚的写

  • 问题描述
    事务执行的时间轴是这样的

在这里插入图片描述

如图所示,事务U开始时间晚于事务T,而事务U的读操作早于事务T,本应该事务U可以读到T写入的值,但是T的写入更晚。

  • 解决方法
    事务T因为时间戳晚于数据元素上的时间戳,也就是事务U访问的时间戳,应该中止事务T,让事务U可以读取正确的数据。

脏数据的问题

事务提交标志的设置,就是用来解决这个问题的,先来看两个问题。

  • 问题一
    |事务U | 事务T|
    |:–|:–|
    |begin; ||
    |write(X) | |
    || begin;|
    ||read(X)|
    |abort||
    ||commit;|

  • 问题二
    |事务U | 事务T|
    |:–|:–|
    |begin; ||
    |write(X) | |
    || begin;|
    ||write(X)|
    ||commit;|
    |abort||

对于问题一,因为事务U在事务T之前启动,并写入X,所有事务T读取X是符合上面时间戳的规则,但是当事务U最终中止时,事务T读取的X就是脏数据,是数据库中本不存在的数据;

对于问题二,有趣的事情来了,此时事务T提交后,其实它是基于事务U的,比如X=1,事务U写入后X=2, 事务T写入后X=3,那么提交成功后X=3;而事务U回滚后,好像什么都不需要做,还是事务U回滚为X=1,事务T重新再做一遍呢?

  • 解决方法
    对于问题一的此类问题,请求读操作时,需要看当前数据元素是否已经提交,如果没有提交,需要中止当前请求,或推迟到该数据库元素提交之后再处理。

而对于问题二的此类问题,写操作请求时,也同样需要判断当前数据元素是否已经提交,如果没有提交,需要中止当前请求,或推迟到该数据库元素提交之后再处理。 当然,更晚的写也可以什么都不做,这被称为Thomas写法则,最后事务U中止后,它要回退它的写入和数据库元素上的时间戳,但是事务T的写入被跳过了,同时也提交完成了,此时想恢复事务T的操作已经不可能了。

mysql中的表现

mysql> show variables like 'transaction%';
+----------------------------------+-----------------+
| Variable_name                    | Value           |
+----------------------------------+-----------------+
| transaction_alloc_block_size     | 8192            |
| transaction_allow_batching       | OFF             |
| transaction_isolation            | REPEATABLE-READ |
| transaction_prealloc_size        | 4096            |
| transaction_read_only            | OFF             |
| transaction_write_set_extraction | XXHASH64        |
+----------------------------------+-----------------+
6 rows in set (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)mysql> select * from test_concurrent;
+------+
| i    |
+------+
|    5 |
+------+
1 row in set (0.00 sec)
-- 这此时另外启动一个事务,将i修改为6,并提交事务
mysql> select * from test_concurrent;
+------+
| i    |
+------+
|    5 |
+------+
1 row in set (0.00 sec)mysql> update test_concurrent set i = 3 where i = 5;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 0  Changed: 0  Warnings: 0mysql> commit;
Query OK, 0 rows affected (0.00 sec)mysql> select * from test_concurrent;
+------+
| i    |
+------+
|    6 |
+------+
1 row in set (0.00 sec)

可以看到mysql中,当前事务可以看到i=5,但确修改不成功,返回0 rows被updated,这就是一个很迷惑的现象。

基于时间戳调度的规则

经过上面问题的分析,现在我们概括基于时间戳调度的规则。

调度器选择

对于来自事务的读写操作请求,调度器有几种选择:

  • 同意该请求
  • 推迟请求
  • 中止请求事务

读写请求的处理

调度器收到读写操作请求,

  1. 收到读操作请求时,检查当前数据库元素上次操作事务的提交状态,
  • 如果已经提交,则再检查时间戳的先后顺序,如果请求事务的时间戳大于当前数据元素的时间戳,则可以同意请求,并将时间戳更新为当前事务;如果事务时间戳小于当前数据元素的时间戳,则需要中止;
  • 如果尚未提交,则请求事务需要推迟;
  1. 当收到写操作请求时,先检查当前事务与数据库元素上的时间戳,
  • 如果请求事务的时间戳大于当前数据元素的时间戳,再检查数据元素上次操作的事务是否提交,如果已经提交,则同意本次写请求;如果未提交,则需要推迟本次请求;
  • 如果事务时间戳小于当前数据元素的时间戳,本次请求事务需要中止;
  1. 当收到事务提交请求时,更新数据元素上的提交状态;同时唤醒等待的事务请求;

  2. 当收到事务T中止请求时,那么回退事务T对应的所有操作数据;等待的事务需要重新发起读或写请求,因为需要检查事务T的写被中止后是否合法。

多版本时间戳

基于时间戳的并发控制调度器,如上面介绍的,会存在读写之间冲突,所以在这个基础上进行了一个重要的演进,就是同时保留数据库元素的多个带不同时间戳的版本,使得读写可以同时进行。

多版本时间戳的流程与上面流程类似:

  1. 当收到写操作请求时WT(X),如果请求被同意,那么X的一个新版本Xi被创建,它的时间戳为Ti(X);
  2. 此时收到一个读操作请求时RU(X)时,最新版本检查不通过时,查找时间戳小于事务U的版本X;也是就WT(X)执行前的版本,就是当前可读的版本,同意RU(X)在版本X上的读请求;
  3. 数据元素的时间戳与对应的版本有关;
  4. 当然再有事务的写请求来时,还是需要在最后的版本Xi上处理;
  5. 旧版本的清理,当X的某个版本上的时间戳小于任何当前活跃事务的时间戳时,就可以清理掉它了。

多版本时间戳的方式,解决了读写并发时的性能问题。

时间戳与封锁

在大多数只读事务或者并发读写同一元素的情况不频繁时,基于时间戳的调度比较有优势;

而当读写并发比较高,而且对同一数据库元素竞争较大时,封锁调度反而比较优,因为此种情况下基于时间戳的调度,需要进行频繁的回退操作。

在现代商用数据库中,会将事务分为只读事务和读写事务,在只读事务时,只使用时间戳的方式,而只读事务时采用两阶段锁的方式。

总结

基于时间戳的调度模型可以说是一种乐观的模型,它假设没有非可串行化行为发生,并且只有在违例发生时才会进行修正或者中止。与此相反,封锁的调度模型是假设非可串行化行为一定会发生,那么提前进行预防,并且推迟可能发生的事务,但不中止它们,它是一种悲观模型。

这两种模型,如果对于大量只读操作时,乐观型好于悲观型调度器。

结尾

非常感谢大家的支持,在浏览的同时别忘了留下您宝贵的评论,如果觉得值得鼓励,请点赞,收藏,我会更加努力!

作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/204953.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

聊一聊Java中的枚举和泛型(两种强大的编程特性)

聊一聊Java中的枚举和泛型(两种强大的编程特性) 保持热爱,奔赴山海。。。。。。 Java中的枚举 在Java中,枚举(Enum)是一种特殊的数据类型,用于定义包含固定常量集合的数据类型。枚举类型在Jav…

Win10 安装.NET Framework 3.5 报错0x80240438

环境: Win10专业版 NET Framework 3.5 问题描述: Win10 安装.NET Framework 3.5 报错0x80240438 解决方案: 1.检查自动更新服务是否未开启,开启自动更新失败,用工具开启自动更新,重启电脑(未解决&am…

如何使用phpStudy本地快速搭建网站并内网穿透远程访问

文章目录 使用工具1. 本地搭建web网站1.1 下载phpstudy后解压并安装1.2 打开默认站点,测试1.3 下载静态演示站点1.4 打开站点根目录1.5 复制演示站点到站网根目录1.6 在浏览器中,查看演示效果。 2. 将本地web网站发布到公网2.1 安装cpolar内网穿透2.2 映…

C++11——包装器

该篇为lambda表达式的延申,请在熟知lambda表达式的基础上阅读该文章 一文详解C11lambda表达式https://blog.csdn.net/qq_74260823/article/details/134839319?spm1001.2014.3001.5501 包装器的由来 这同样是一个不属于C原始风味的语法 我们在lambda表达式中讲到过…

时间序列预测实战(二十五)PyTorch实现Seq2Seq进行多元和单元预测(附代码+数据集+完整解析)

一、本文介绍 本文给大家带来的时间序列模型是Seq2Seq,这个概念相信大家都不陌生了,网上的讲解已经满天飞了,但是本文给大家带来的是我在Seq2Seq思想上开发的一个模型和新的架构,架构前面的文章已经说过很多次了,其是…

MySQL数据库,函数与分组

单行函数: 操作数据对象 接受参数返回一个结果 只对一行进行变换 每行返回一个结果 可以嵌套 参数也可以是一列或一个值 数值函数 基本函数: 注:ROUND(x,y)函数的y是负数时,即往高位进行四舍五入,如-3就是按百位…

出生率持续下降,而低代码,成了!

低代码这个概念在IT界应该是火了很久,在十年前就有低代码的概念。 在最初的时候,我们都是用高级语言或者脚本来开发页面或者应用,比如Java、C,前端会使用Vue、React等等。但是我们发现经常写的功能或者页面都是重复的,…

Python实现内网穿透和端口转发代理

对于大型的爬虫项目,肯定需要有良好的反爬机制,还应该配合代理iP使用,只有这两种结合才能让你的爬虫更高效准确。今天我就借用我一点空闲时间,来谈一谈有关python爬虫网络方面的解决方案,这里提供内网穿透和端口转发两…

印刷企业实施数字工厂管理系统有几个步骤

随着科技的飞速发展,许多传统行业正面临着前所未有的挑战。印刷行业也不例外,尤其在面对生产效率低下,资源浪费严重等问题时,数字工厂管理系统的实施成为了必然选择。那么,印刷企业实施数字工厂管理系统具体有几个步骤…

echarts 柱状图 定时自动轮播(非提示框轮播)

看了很多文档都是实现提示框轮播的,而我要实现的功能是:柱状图有多条数据时,轮播展示其中几条,比如我有100条数据,不能全部展示,设置轮播5条或者10条,依次显示数据,并形成闭环。 &a…

zabbix配置snmp trap--使用snmptrapd和Bash接收器--图文教程

1.前言 我的zabbix的版本是5.0版本,5.0的官方文档没有使用bash接收器的示例,6.0的官方文档有使用bash接收器的示例,但是,下载文件的链接失效?! 这里讲解zabbix-server端配置和zabbix web端配置 2.zabbix-…

11、pytest断言预期异常

官方用例 # content of test_exception_zero.py import pytestdef test_zero_division():with pytest.raises(ZeroDivisionError):1/0# content of test_exception_runtimeerror.py import pytestdef test_recursion_depth():with pytest.raises(RuntimeError) as excinfo:def…

ncnn模型部署——使用VS2019把项目打包成DLL文件

一、项目打包成DLL文件 1.创建动态链接库DLL项目 创建完成,项目中包含源文件dllmain.cpp, pch.cpp,头文件framework.h, pch.h 2.编写和配置DLL项目 (1)配置pch.h文件,在头文件pch.h中定义宏,宏的作用的是…

【Unity动画】Unity 2D动画创建流程

本文以2D为案例,讲解Unity 播放动画的流程 准备和导入2D动画资源 外部导入序列帧生成的 Unity内部制作的 外部导入的3D动画 2.创建动画过程 打开时间轴Ctrl6 选中场景中的一个未来需要播放动画的物体 回到时间轴点击Create一个新动画片段 拖动2D动画资源放入…

什么是SPA(Single Page Application)?它的优点和缺点是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

IT外包对中小企业的独特优势

在竞争激烈的商业环境中,企业的发展稍有缓慢,就很有可能被竞争对手快速赶超、趁机抢占市场。一些中小企业为了更好地应对市场变化和提高自身竞争力,越来越多地转向了IT外包服务。相较于大型企业,中小企业在选择IT外包时能够获得一…

数据结构实验任务七:基于广度优先搜索的六度空间理论验证

问题描述 “六度空间”理论又称作“六度分隔(Six Degrees of Separation)”理论。这个理论 可以通俗地阐述为:“你和任何一个陌生人之间所间隔的人不会超过六个,也就是 说,最多通过五个人你就能够认识任何一个陌生人。”假如给你一个社交网络图&#xf…

Tap虚拟网卡

1 概述 Tap设备通常用于虚拟化场景下,其驱动代码位于drivers/net/tun.c,tap与tun复用大部分代码, 注:drivers/net/tap.c并不是tap设备的代码,而是macvtap和ipvtap; 下文中,我们统一称tap&#…

四、分代垃圾回收机制及垃圾回收算法

学习垃圾回收的意义 Java 与 C等语言最大的技术区别:自动化的垃圾回收机制(GC) 为什么要了解 GC 和内存分配策略 1、面试需要 2、GC 对应用的性能是有影响的; 3、写代码有好处 栈:栈中的生命周期是跟随线程&…

Python 日志(略讲)

日志操作 日志输出: # 输出日志信息 logging.debug("调试级别日志") logging.info("信息级别日志") logging.warning("警告级别日志") logging.error("错误级别日志") logging.critical("严重级别日志")级别设置…