深入OceanBase内部机制:系统架构与组件精讲

码到三十五 : 个人主页

心中有诗画,指尖舞代码,目光览世界,步履越千山,人间尽值得 !


目录

    • 1️⃣OceanBase 整体架构
      • 1.1 分区
      • 1.2 分片
      • 1.3 日志流
      • 1.4 对等节点
      • 1.5 多租户
    • 2️⃣OceanBase 架构与组件详解
      • 2.1 存储层
      • 2.2 复制层
      • 2.3 均衡层
      • 2.4 事务层
        • 原子性
        • 隔离性
      • 2.5 SQL 层
      • 2.5 多种计划
      • 2.6 接入层
    • 3️⃣ 核心组件与功能概述
      • 基础架特性
      • 核心组件与功能
      • 数据库实例内部组件
    • 结语

1️⃣OceanBase 整体架构

OceanBase 数据库采用 Shared-Nothing 架构,各个节点之间完全对等,每个节点都有自己的 SQL 引擎、存储引擎、事务引擎,运行在普通 PC 服务器组成的集群之上,具备高可扩展性、高可用性、高性能、低成本、与主流数据库高兼容等核心特性。

在这里插入图片描述

1.1 分区

OceanBase 数据库的一个集群由若干个节点组成。这些节点分属于若干个可用区(Zone),每个节点属于一个可用区。可用区是一个逻辑概念,表示集群内具有相似硬件可用性的一组节点,它在不同的部署模式下代表不同的含义。例如,当整个集群部署在同一个数据中心(IDC)内的时候,一个可用区的节点可以属于同一个机架,同一个交换机等。当集群分布在多个数据中心的时候,每个可用区可以对应于一个数据中心。每个可用区具有 IDC 和地域(Region)两个属性,描述该可用区所在的 IDC 及 IDC 所属的地域。一般地,地域指 IDC 所在的城市。可用区的 IDC 和 Region 属性需要反映部署时候的实际情况,以便集群内的自动容灾处理和优化策略能更好地工作。根据业务对数据库系统不同的高可用性需求,OceanBase 集群提供了多种部署模式。

1.2 分片

在 OceanBase 数据库中,一个表的数据可以按照某种划分规则水平拆分为多个分片,每个分片叫做一个表分区,简称分区(Partition)。某行数据属于且只属于一个分区。分区的规则由用户在建表的时候指定,包括hash、range、list等类型的分区,还支持二级分区。例如,交易库中的订单表,可以先按照用户 ID 划分为若干一级分区,再按照月份把每个一级分区划分为若干二级分区。对于二级分区表,第二级的每个子分区是一个物理分区,而第一级分区只是逻辑概念。一个表的若干个分区可以分布在一个可用区内的多个节点上。每个物理分区有一个用于存储数据的存储层对象,叫做 Tablet,用于存储有序的数据记录。

1.3 日志流

当用户对 Tablet 中记录进行修改的时候,为了保证数据持久化,需要记录重做日志(REDO)到 Tablet 对应的日志流(Log Stream)里。每个日志流服务了其所在节点上的多个 Tablet。为了能够保护数据,并在节点发生故障的时候不中断服务,每个日志流及其所属的 Tablet 有多个副本。一般来说,多个副本分散在多个不同的可用区里。多个副本中有且只有一个副本接受修改操作,叫做主副本(Leader),其他副本叫做从副本(Follower)。主从副本之间通过基于 Multi-Paxos 的分布式共识协议实现了副本之间数据的一致性。当主副本所在节点发生故障的时候,一个从副本会被选举为新的主副本并继续提供服务。

1.4 对等节点

在集群的每个节点上会运行一个叫做 observer 的服务进程,它内部包含多个操作系统线程。节点的功能都是对等的。每个服务负责自己所在节点上分区数据的存取,也负责路由到本机的 SQL 语句的解析和执行。这些服务进程之间通过 TCP/IP 协议进行通信。同时,每个服务会监听来自外部应用的连接请求,建立连接和数据库会话,并提供数据库服务。

1.5 多租户

为了简化大规模部署多个业务数据库的管理并降低资源成本,OceanBase 数据库提供了独特的多租户特性。在一个 OceanBase 集群内,可以创建很多个互相之间隔离的数据库"实例",叫做一个租户。从应用程序的视角来看,每个租户等同于一个独立的数据库实例。不仅如此,每个租户可以选择 MySQL 或 Oracle 兼容模式。应用连接到 MySQL 租户后,可以在租户下创建用户、database,与一个独立的 MySQL 库的使用体验是一样的。同样的,应用连接到 Oracle 租户后,可以在租户下创建 schema、管理角色等,与一个独立的 Oracle 库的使用体验是一样的。一个新的集群初始化之后,就会存在一个特殊的名为 sys 的租户,叫做系统租户。系统租户中保存了集群的元数据,是一个 MySQL 兼容模式的租户。

2️⃣OceanBase 架构与组件详解

OceanBase 使用通用服务器硬件,依赖本地存储,分布式部署使用的多个服务器也是对等的,没有特殊的硬件要求。OceanBase 的分布式数据库处理采用 Shared Nothing 架构,数据库内的 SQL 执行引擎具有分布式执行能力。

OceanBase 在服务器上会运行叫做 observer 的单进程程序作为数据库的运行实例,使用本地的文件存储数据和事务 Redo 日志。

OceanBase 集群部署需要配置可用区(Zone),由若干个服务器组成。可用区是一个逻辑概念,表示集群内具有相似硬件可用性的一组节点,它在不同的部署模式下代表不同的含义。例如,当整个集群部署在同一个数据中心(IDC)内的时候,一个可用区的节点可以属于同一个机架,同一个交换机等。当集群分布在多个数据中心的时候,每个可用区可以对应于一个数据中心。

在这里插入图片描述

用户存储的数据在分布式集群内部可以存储多个副本,用于故障容灾,也可以用于分散读取压力。同一个租户在一个可用区内的数据只有一个副本,不同的可用区可以存储同一个数据的多个副本,副本之间由共识协议保证数据的一致性。

OceanBase 内置多租户特性,每个租户对于使用者是一个独立的数据库,一个租户能够在租户级别设置租户的分布式部署方式。租户之间 CPU、内存和 IO 都是隔离的。

OceanBase 的数据库实例内部由不同的组件相互协作,这些组件从底层向上由存储层、复制层、均衡层、事务层、SQL 层、接入层组成。

2.1 存储层

存储层以一张表或者一个分区为粒度提供数据存储与访问,每个分区对应一个用于存储数据的Tablet(分片),用户定义的非分区表也会对应一个 Tablet。

Tablet 的内部是分层存储的结构,总共有 4 层。DML 操作插入、更新、删除等首先写入 MemTable,等到 MemTable 达到一定大小时转储到磁盘成为 L0 SSTable。L0 SSTable 个数达到阈值后会将多个 L0 SSTable 合并成一个 L1 SSTable。在每天配置的业务低峰期,系统会将所有的 MemTable、L0 SSTable 和 L1 SSTable 合并成一个 Major SSTable。

每个 SSTable 内部是以 2MB 定长宏块为基本单位,每个宏块内部由多个不定长微块组成。

Major SSTable 的微块会在合并过程中用编码方式进行格式转换,微块内的数据会按照列维度分别进行列内的编码,编码规则包括字典/游程/常量/差值等,每一列压缩结束后,还会进一步对多列进行列间等值/子串等规则编码。编码能对数据大幅压缩,同时提炼的列内特征信息还能进一步加速后续的查询速度。

在编码压缩之后,还可以根据用户指定的通用压缩算法进行无损压缩,进一步提升数据压缩率。

2.2 复制层

复制层使用日志流(LS、Log Stream)在多副本之间同步状态。每个 Tablet 都会对应一个确定的日志流,每个日志流对应多个 Tablet,DML 操作写入 Tablet 的数据所产生的 Redo 日志会持久化在日志流中。日志流的多个副本会分布在不同的可用区中,多个副本之间维持了共识算法,选择其中一个副本作为主副本,其他的副本皆为从副本。Tablet 的 DML 和强一致性查询只在其对应的日志流的主副本上进行。

通常情况下,每个租户在每台机器上只会有一个日志流的主副本,可能存在多个其他日志流的从副本。租户的总日志流个数取决于 Primary Zone 和 Locality 的配置。

日志流使用自研的 Paxos 协议实现了将 Redo 日志在本服务器持久化,同时通过网络发送给日志流的从副本,从副本在完成各自持久化后应答主副本,主副本在确认有多数派副本都持久化成功后确认对应的 Redo 日志持久化成功。从副本利用 Redo 日志的内容实时回放,保证自己的状态与主副本一致。

日志流的主副本在被选举成为主后会获得租约(Lease),正常工作的主副本在租约有效期内会不停的通过选举协议延长租约期。主副本只会在租约有效时执行主的工作,租约机制保证了数据库异常处理的能力。

复制层能够自动应对服务器故障,保障数据库服务的持续可用。如果出现少于半数的从副本所在服务器故障,因为还有多于半数的副本正常工作,数据库的服务不受影响。如果主副本所在服务器出现问题,其租约会得不到延续,待其租约失效后,其他从副本会通过选举协议选举出新的主副本并授予新的租约,之后即可恢复数据库的服务。

2.3 均衡层

新建表和新增分区时,系统会按照均衡原则选择合适的日志流创建 Tablet。当租户的属性发生变更,新增了机器资源,或者经过长时间使用后,Tablet 在各台机器上不再均衡时,均衡层通过日志流的分裂和合并操作,并在这个过程中配合日志流副本的移动,让数据和服务在多个服务器之间再次均衡。

当租户有扩容操作,获得更多服务器资源时,均衡层会将租户内已有的日志流进行分裂,并选择合适数量的 Tablet 一同分裂到新的日志流中,再将新日志流迁移到新增的服务器上,以充分利用扩容后的资源。当租户有缩容操作时,均衡层会把需要缩减的服务器上的日志流迁移到其他服务器上,并和其他服务器上已有的日志流进行合并,以缩减机器的资源占用。

当数据库长期使用后,随着持续创建删除表,并且写入更多的数据,即使没有服务器资源数量变化,原本均衡的情况可能被破坏。最常见的情况是,当用户删除了一批表后,删除的表可能原本聚集在某一些机器上,删除后这些机器上的 Tablet 数量就变少了,应该把其他机器的 Tablet 均衡一些到这些少的机器上。均衡层会定期生成均衡计划,将 Tablet 多的服务器上日志流分裂出临时日志流并携带需要移动的 Tablet,临时日志流迁移到目的服务器后再和目的服务器上的日志流进行合并,以达成均衡的效果。

2.4 事务层

事务层保证了单个日志流和多个日志流DML操作提交的原子性,也保证了并发事务之间的多版本隔离能力。

原子性

一个日志流上事务的修改,即使涉及多个 Tablet,通过日志流的 write-ahead log 可以保证事务提交的原子性。事务的修改涉及多个日志流时,每个日志流会产生并持久化各自的write-ahead log,事务层通过优化的两阶段提交协议来保证事务提交的原子性。

当涉及到多个日志流的事务发起提交时,事务会选择其中一个日志流作为两阶段提交的协调者,协调者会与事务修改的所有日志流通信,判断 write-ahead log 是否持久化,当所有日志流都完成持久化后,事务进入提交状态,协调者会再驱动所有日志流写下这个事务的 Commit 日志,表示事务最终的提交状态。当从副本回放或者数据库重启时,已经完成提交的事务都会通过 Commit 日志确定各自日志流事务的状态。

宕机重启场景下,宕机前还未完成的事务,会出现写完 write-ahead log 但是还没有Commit 日志的情况,每个日志流的 write-ahead log 都会包含事务的所有日志流列表,通过此信息可以重新确定哪个日志流是协调者并恢复协调者的状态,再次推进两阶段提交协议,直到事务最终的 Commit 或 Abort 状态。

隔离性

GTS 服务是一个租户内产生连续增长的时间戳的服务,其通过多副本保证可用性,底层机制与上面复制层所描述的日志流副本同步机制是一样的。

每个事务在提交时会从 GTS 获取一个时间戳作为事务的提交版本号并持久化在日志流的write-ahead log 中,事务内所有修改的数据都以此提交版本号标记。

每个语句开始时(对于 Read Committed 隔离级别)或者每个事务开始时(对于Repeatable Read 和 Serializable 隔离级别)会从 GTS 获取一个时间戳作为语句或事务的读取版本号。在读取数据时,会跳过事务版本号比读取版本号大的数据,通过这种方式为读取操作提供了统一的全局数据快照。

2.5 SQL 层

SQL 层将用户的 SQL 请求转化成对一个或多个 Tablet 的数据访问。

SQL 层组件
SQL 层处理一个请求的执行流程是:Parser、Resolver、Transformer、Optimizer、Code Generator、Executor。

Parser 负责词法/语法解析,Parser 会将用户的 SQL 分成一个个的 “Token”,并根据预先设定好的语法规则解析整个请求,转换成语法树(Syntax Tree)。

Resolver 负责语义解析,将根据数据库元信息将 SQL 请求中的 Token 翻译成对应的对象(例如库、表、列、索引等),生成的数据结构叫做 Statement Tree。

Transformer 负责逻辑改写,根据内部的规则或代价模型,将 SQL 改写为与之等价的其他形式,并将其提供给后续的优化器做进一步的优化。Transformer 的工作方式是在原Statement Tree 上做等价变换,变换的结果仍然是一棵 Statement Tree。

Optimizer(优化器)为 SQL 请求生成最佳的执行计划,需要综合考虑 SQL 请求的语义、对象数据特征、对象物理分布等多方面因素,解决访问路径选择、联接顺序选择、联接算法选择、分布式计划生成等问题,最终生成执行计划。

Code Generator(代码生成器)将执行计划转换为可执行的代码,但是不做任何优化选择。

Executor(执行器)启动 SQL 的执行过程。

在标准的 SQL 流程之外,SQL 层还有 Plan Cache 能力,将历史的执行计划缓存在内存中,后续的执行可以反复执行这个计划,避免了重复查询优化的过程。配合 Fast-parser 模块,仅使用词法分析对文本串直接参数化,获取参数化后的文本及常量参数,让 SQL 直接命中 Plan Cache,加速频繁执行的 SQL。

2.5 多种计划

SQL 层的执行计划分为本地、远程和分布式三种。本地执行计划只访问本服务器的数据。远程执行计划只访问非本地的一台服务器的数据。分布式计划会访问超过一台服务器的数据,执行计划会分成多个子计划在多个服务器上执行。

SQL 层并行化执行能力可以将执行计划分解成多个部分,由多个执行线程执行,通过一定的调度的方式,实现执行计划的并行处理。并行化执行可以充分发挥服务器 CPU 和 IO 处理能力,缩短单个查询的响应时间。并行查询技术可以用于分布式执行计划,也可以用于本地执行计划。

2.6 接入层

OceanBase 数据库代理(OceanBase Database Proxy,ODP)是 OceanBase 数据库的接入层,负责将用户的请求转发到合适的 OceanBase 实例上进行处理。

ODP 是独立的进程实例,独立于 OceanBase 的数据库实例部署。ODP 监听网络端口,兼容 MySQL 网络协议,支持使用 MySQL 驱动的应用直接连接 OceanBase。

ODP 能够自动发现 OceanBase 集群的数据分布信息,对于代理的每一条 SQL 语句,会尽可能识别出语句将访问的数据,并将语句直接转发到数据所在服务器的 OceanBase 实例。

ODP 有两种部署方式,一种是部署在每一个需要访问数据库的应用服务器上,另一种是部署在与 OceanBase 相同的机器上。第一种部署方式下,应用程序直接连接部署在同一台服务器上的 obproxy,所有的请求会由 ODP 发送到合适的 OceanBase 服务器。第二种部署方式下,需要使用网络负载均衡服务将多个 ODP 聚合成同一个对应用提供服务的入口地址。

3️⃣ 核心组件与功能概述

基础架特性

OceanBase 是一个分布式数据库系统,它采用通用服务器硬件,不依赖特定的高性能硬件。它在本地存储数据,并通过分布式部署在多个对等的服务器上实现高可用性和可扩展性。OceanBase 遵循 Shared Nothing 架构,其 SQL 执行引擎具备分布式执行能力。

核心组件与功能

  1. Observer

    • OceanBase 在每个服务器上运行一个单进程程序,称为 observer,作为数据库的运行实例。
    • 使用本地文件存储数据和事务 Redo 日志。
  2. 集群与可用区(Zone)

    • OceanBase 集群由多个服务器组成,配置为不同的可用区。
    • 可用区是逻辑概念,代表集群内硬件可用性相似的一组节点。
  3. 数据副本与一致性

    • 用户数据在集群内部存储多个副本,用于故障恢复和读取扩展。
    • 副本之间通过共识协议保持数据一致性。
  4. 多租户特性

    • OceanBase 内置多租户支持,每个租户表现为一个独立的数据库。
    • 租户之间在 CPU、内存和 IO 方面实现隔离。

数据库实例内部组件

  • 存储层:以表或分区为单位提供数据存储与访问,采用分层存储结构,包括 MemTable、L0/L1 SSTable 和 Major SSTable,支持数据压缩和编码。

  • 复制层:使用日志流(LS)在多个副本之间同步状态,通过 Paxos 协议保证数据一致性,能自动处理服务器故障。

  • 均衡层:负责数据的均衡分布,通过日志流的分裂、合并和迁移操作,确保数据和服务在多个服务器之间保持均衡。

  • 事务层:保证 DML 操作的原子性和事务之间的多版本隔离能力,通过优化的两阶段提交协议和 GTS 服务实现。

  • SQL 层:将 SQL 请求转化为对 Tablet 的数据访问,包括解析、语义解析、逻辑改写、优化、代码生成和执行等步骤。

  • 接入层:通过 OceanBase 数据库代理(ODP)实现用户请求的接入和转发,支持 MySQL 网络协议,能够自动发现数据分布信息。

结语

OceanBase 通过其分布式架构和核心组件的协作,提供了高可用、可扩展的数据库服务,适用于需要处理大量数据和复杂事务的应用场景。其多租户特性和灵活的部署选项进一步增强了其作为企业级数据库解决方案的吸引力。



听说...关注下面公众号的人都变牛了,纯技术,纯干货 !

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

错误日志:解决在VScode中调试C++代码断点无效、断点错位的问题

问题可能原因有: 调试时断点无效,大概率是 CMakeLists.txt 设置成了 Release 模式;如果在 CMakeLists.txt 在设置成 Debug 以后,调试时能够停下来,但没在断点处停下,而是停在了别的地方,这就是…

解决solidworks electrical无法连接数据库

很多人在第一次安装电气软件的过程中,会遇到这样的一个问题:“无法连接至数据库,请检查连接参数”,相信很多人看到就感到非常的头疼。的确,对于我们专业人士来说,也是非常的难受,那怎么办呢&…

计算机毕业设计vue+PHP校园二手书交易系统_ij5dr

开发语言:php 后端框架:Thinkphp/Laravel 前端框架:vue.js 服务器:apache 数据库:mysql 运行环境:phpstudy/wamp/xammp等 基于vue框架的二手图书交易系统为当前传统管理模式提供了一个高效、便捷、信息化的解决方案&a…

【热门话题】OneFlow深度学习框架介绍

🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 ​💫个人格言: "如无必要,勿增实体" 文章目录 OneFlow深度学习框架介绍引言一、OneFlow概述1.1 定位与起源1.2 核心特性数据流…

TQZC706开发板教程:在ZC706上运行AD9361

首先需要在github上下载两个文件,本例程用到的文件以及最终文件,我都会放在网盘里面,地址在本文的末尾,需要自行提取 在github上搜索hdl选择第一个-->选择版本-->我所使用的vivado是2018.3版本,所以这里我下载的…

JAVA面试八股文之数据库

MySQL面试题 MySQL 存储引擎架构了解吗?CHAR 和 VARCHAR 的区别是什么?索引是越多越好嘛?MySQL数据库中空值(null)和空字符串()的区别?SQL 中 on 条件与 where 条件的区别&#xff1…

【Linux系列】如何确定当前运行的是 RHEL 9 还是 RHEL 8?

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

mysql8主从配置报错Authentication plugin ‘caching_sha2_password‘ reported error

错误信息: Error connecting to source slave192.168.2.177:3306. This was attempt 2/86400, with a delay of 60 seconds between attempts. Message: Authentication plugin caching_sha2_password reported error: Authentication requires secure connection.…

C/C++的内存管理

栈帧最主要的作用就是存储局部数据 C语言中动态内存管理方式 C语言动态内存管理 该篇详细的讲述了C语言动态内存管理的使用,不太懂的小伙伴可以去了解一下 C中动态内存管理方式 首先,C语言内存管理的方式在C中可以继续使用。但有些地方就无能为力而且使用…

谷歌浏览器网页自动刷新插件

谷歌浏览器网页自动刷新插件下载:https://www.123pan.com/s/f43eVv-CO7Kd.html 效果图(win和mac系统同样操作) 1.打开谷歌浏览器,点击头像旁边的三点,点击扩展程序,点击管理扩展程序。 2.打开开发者模式&a…

【SpringBoot】配置swagger

pom.xml 安装插件 <dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version></dependency>创建swagger配置类 import org.springframework.context.annotation.…

韩顺平Java | C24 MySQL数据库(下)

※多表查询 笛卡尔集&#xff1a;查询两个表&#xff0c;默认无条件情况下&#xff0c;取出第一张表中的每一条记录和第二张表的每一条记录进行组合&#xff0c;返回row1*row2条记录数&#xff0c;包含两张表的所有列 内连接 # 写出正确的过滤条件&#xff1a;多表查询条件不…

Jackson配置处理LocalDateTime、LocalDate等java8时间类型失效的问题解决

目录 前言 一、问题排查过程 1.1 SpringMvc是如何处理请求报文和响应报文 1.2 JacksonConfig配置排查 二、导致Jackson配置失效的原因 2.1 没有addSerializer 2.2 添加了EnableMvc注解 2.3 另外有地方配置了Jacksonhttpconver覆盖了配置 总结 前言 上一篇文章《使用Ja…

SpringBoot 整合RocketMQ

目录 一、引入依赖 二、配置文件 三、生产者 四、消费者 五、结果 一、引入依赖 <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.2.0</version> </d…

Flutter - flutter_gen 资源管理

引言&#xff1a; 在开发 Flutter 应用时&#xff0c;我们经常需要使用各种静态资源&#xff0c;如图片、字体和音频等。如何有效地管理和加载这些资源呢&#xff1f;本篇博客将以图片为例带你解密 Flutter 项目中是如何管理资源地。 assets 加载资源 具体文件名引入 在工程…

【1500字干货】6大实用技巧,轻松在1688找到优质货源厂家!

1688平台提供了海量的选品机会&#xff0c;并且汇聚了大量的优质源头厂家。对于想要寻找高质量、有竞争力的产品的商家来说&#xff0c;1688平台是一个非常好的选择。然而&#xff0c;在选择源头厂家时&#xff0c;商家也需要注意一些问题。好的货源很重要&#xff0c;可是如何…

Linux/Lame

Lame 今天随便乱逛发现这台机器貌似是 HackTheBox 平台的第一台机器&#xff0c;而且我还没做过&#xff0c;从简介上来看的话是一台很简单的机器&#xff0c;快快的玩一下 Enumeration nmap 首先用 nmap 扫描一下常见的端口&#xff0c;发现系统对外开放了 21,22,139,445 端…

《剑指 Offer》专项突破版 - 面试题 105 和 106 : 最大的岛屿和二分图(C++ 实现)

目录 面试题 105 : 最大的岛屿 面试题 106 : 二分图 面试题 105 : 最大的岛屿 题目&#xff1a; 海洋岛屿地图可以用由 0、1 组成的二维数组表示&#xff0c;水平或竖直方向相连的一组 1 表示一个岛屿&#xff0c;请计算最大的岛屿的面积&#xff08;即岛屿中 1 的数目&…

如何使用Jellyfin+cpolar低成本部署私人影音平台并实现无公网IP远程访问

文章目录 1. 前言2. Jellyfin服务网站搭建2.1. Jellyfin下载和安装2.2. Jellyfin网页测试 3.本地网页发布3.1 cpolar的安装和注册3.2 Cpolar云端设置3.3 Cpolar本地设置 4.公网访问测试5. 结语 1. 前言 随着移动智能设备的普及&#xff0c;各种各样的使用需求也被开发出来&…

事务的隔离性

参考: 小林coding MySQL服务器同时处理多个事务时&#xff0c;会出现脏读&#xff0c;不可重复读&#xff0c;幻读问题。 脏读 一个事务读到另一个未提交事务修改过的数据。 举例&#xff1a;事务A先读取数据&#xff0c;并对其进行修改&#xff0c;此时事务B进行读取获取到…