当前位置: 首页 > news >正文

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

使用时间戳的并发控制

专栏内容

  • 手写数据库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
如有错误或者疏漏欢迎指出,互相学习。

相关文章:

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

使用时间戳的并发控制 ​专栏内容: 手写数据库toadb 本专栏主要介绍如何从零开发,开发的步骤,以及开发过程中的涉及的原理,遇到的问题等,让大家能跟上并且可以一起开发,让每个需要的人成为参与者。 本专栏会…...

Python 日志(略讲)

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

C++ 指针进阶

目录 一、字符指针 二、指针数组 三、数组指针 数组指针的定义 &数组名 与 数组名 数组指针的使用 四、数组参数 一维数组传参 二维数组传参 五、指针参数 一级指针传参 二级指针传参 六、函数指针 七、函数指针数组 八、指向函数指针数组的指针 九、回调函…...

stm32中滴答定时器与普通定时器的区别

1、两者在单片机中的位置不一样 滴答定时器在内核上,普通定时器在外设上。 由于位置不同,滴答定时器的程序可以移植到所有相同内核的芯片上,但普通定时器的程序却不可以。 2、两者的中断优先级不一样 滴答定时器优先级高,普通定…...

某60区块链安全之薅羊毛攻击实战一学习记录

区块链安全 文章目录 区块链安全薅羊毛攻击实战一实验目的实验环境实验工具实验原理实验内容薅羊毛攻击实战一 实验步骤EXP利用 薅羊毛攻击实战一 实验目的 学会使用python3的web3模块 学会分析以太坊智能合约薅羊毛攻击漏洞 找到合约漏洞进行分析并形成利用 实验环境 Ubun…...

Java程序员,你掌握了多线程吗?(文末送书)

目录 01、多线程对于Java的意义02、为什么Java工程师必须掌握多线程03、Java多线程使用方式04、如何学好Java多线程送书规则 摘要:互联网的每一个角落,无论是大型电商平台的秒杀活动,社交平台的实时消息推送,还是在线视频平台的流…...

排序算法——桶排序/基数排序/计数排序

桶排序 是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理: 假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使…...

FFmpeg之将视频转为16:9(横屏)或9:16(竖屏)(三十六)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…...

git学习笔记02(小滴课堂)

window 安装教程:https://www.yuque.com/u1106272/cai80g/skawco 查看: 创建文件夹: 我们把这个文件夹当作我们的暂存区。 这样就进入了工作区。 初始化: 可以看到.git文件夹。 查看本地仓库状态: 我们进入这个ide…...

2022 RedisDays 内容揭秘

上个月,Redis举办了3场线上会议,分别介绍了即将正式发布的Redis 7中包括的重要更新的内容,还有Redis完全重写的RedisJSON 2.0模块,和新发布的Redis Stack模块。除此之外,在此次线上会议中还介绍了现代化的软件架构与Re…...

论文阅读——Img2LLM(cvpr2023)

arxiv:[2212.10846] From Images to Textual Prompts: Zero-shot VQA with Frozen Large Language Models (arxiv.org) 一、介绍 使用大语言模解决VQA任务的方法大概两种:multi-modal pretraining and language-mediated VQA,即多模态预训练…...

南京大学考研机试题DP

3. dp 求子序列的个数 https://www.acwing.com/problem/content/description/3716/ #include <iostream> #include <cstring> #include <algorithm> #include <unordered_set> #include <vector> using namespace std; const int N 1e4 10…...

如何进行多ip服务器租用?

如何进行多ip服务器租用&#xff1f; 对于网络时代来说&#xff0c;是需要很多设备才能维持的&#xff0c;比如说多ip服务器就是互联网时代常见的设备&#xff0c;所以我们需要对多ip服务器有足够的了解&#xff0c;这样才能更好的获取互联网上的信息&#xff0c;满足我们工作…...

(动手学习深度学习)第13章 实战kaggle竞赛:树叶分类

文章目录 实战kaggle比赛&#xff1a;树叶分类1. 导入相关库2. 查看数据格式3. 制作数据集4. 数据可视化5. 定义网络模型6. 定义超参数7. 训练模型8. 测试并提交文件 竞赛技术总结1. 技术分析2. 数据方面模型方面3. AutoGluon4. 总结 实战kaggle比赛&#xff1a;树叶分类 kagg…...

vue中shift+alt+f格式化防止格式掉其它内容

好处就是使得提交记录干净&#xff0c;否则修改一两行代码&#xff0c;习惯性按了一下格式化快捷键&#xff0c;遍地飘红&#xff0c;下次找修改就费时间 1.点击设置图标-设置 2.点击这个转成配置文件 {"extensions.ignoreRecommendations": true,"[vue]":…...

WPS导出的PDF比较糊,和原始的不太一样,将带有SVG的文档输出为PDF

一、在WPS的PPT中 你直接输出PDF可能会导致一些问题&#xff08;比如照片比原来糊&#xff09;/ 或者你复制PPT中的图片到AI中类似的操作&#xff0c;得到的照片比原来糊&#xff0c;所以应该选择打印-->高级打印 然后再另存为PDF 最后再使用AI打开PDF文件再复制到你想用…...

Linux /etc/hosts文件

Linux的 /etc/hosts 文件用于静态地映射主机名到 IP 地址。 通常用于本地网络中的名称解析&#xff0c;它可以覆盖 DNS 的设置。当你访问一个域名时&#xff0c;系统会首先检查 /etc/hosts 文件&#xff0c;如果找到了匹配项&#xff0c;就会使用该 IP 地址&#xff0c;否则会…...

webpack学习-3.管理输出

webpack学习-3.管理输出 1.简单练手2.设置 HtmlWebpackPlugin3.清理 /dist 文件夹4.manifest5.总结 1.简单练手 官网的第一个预先准备&#xff0c;是多入口的。 const path require(path);module.exports {entry: {index: ./src/index.js,print: ./src/print.js,},output: …...

【Go语言反射reflect】

Go语言反射reflect 一、引入 先看官方Doc中Rob Pike给出的关于反射的定义&#xff1a; Reflection in computing is the ability of a program to examine its own structure, particularly through types; it’s a form of metaprogramming. It’s also a great source of …...

LC-1466. 重新规划路线(DFS、BFS)

1466. 重新规划路线 中等 n 座城市&#xff0c;从 0 到 n-1 编号&#xff0c;其间共有 n-1 条路线。因此&#xff0c;要想在两座不同城市之间旅行只有唯一一条路线可供选择&#xff08;路线网形成一颗树&#xff09;。去年&#xff0c;交通运输部决定重新规划路线&#xff0c…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

JS红宝书笔记 - 3.3 变量

要定义变量&#xff0c;可以使用var操作符&#xff0c;后跟变量名 ES实现变量初始化&#xff0c;因此可以同时定义变量并设置它的值 使用var操作符定义的变量会成为包含它的函数的局部变量。 在函数内定义变量时省略var操作符&#xff0c;可以创建一个全局变量 如果需要定义…...

PydanticAI快速入门示例

参考链接&#xff1a;https://ai.pydantic.dev/#why-use-pydanticai 示例代码 from pydantic_ai import Agent from pydantic_ai.models.openai import OpenAIModel from pydantic_ai.providers.openai import OpenAIProvider# 配置使用阿里云通义千问模型 model OpenAIMode…...

【版本控制】GitHub Desktop 入门教程与开源协作全流程解析

目录 0 引言1 GitHub Desktop 入门教程1.1 安装与基础配置1.2 核心功能使用指南仓库管理日常开发流程分支管理 2 GitHub 开源协作流程详解2.1 Fork & Pull Request 模型2.2 完整协作流程步骤步骤 1: Fork&#xff08;创建个人副本&#xff09;步骤 2: Clone&#xff08;克隆…...