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

Distributed Transactions Mit 6.824

Topic1:distributed transactions = concurrency control + atomic commit

传统计划:事务

程序员标记代码序列的开始/结束作为事务。

事务示例
  • xy 是银行余额——数据库表中的记录。
  • xy 位于不同的服务器上(可能在不同的银行)。
  • xy 开始时都是 $10。
  • T1T2 是事务。
    • T1: 从 xy 转账 $1。
    • T2: 审计,检查没有钱丢失。
事务操作
T1:             T2:
begin_xaction   begin_xactionadd(x, 1)       tmp1 = get(x)add(y, -1)      tmp2 = get(y)
end_xaction       print tmp1, tmp2end_xaction
事务的正确行为是什么?

通常被称为“ACID”:

  • 原子性(Atomic) - 尽管有失败,但要么全部写入成功,要么一个也不写。
  • 一致性(Consistent) - 遵守应用程序特定的不变量。
  • 隔离性(Isolated) - 事务之间没有干扰——可串行化。
  • 持久性(Durable) - 提交的写操作是永久的。
什么是可串行化?

当您执行一些并发事务并产生结果时,“结果”既包括输出也包括数据库中的变化。如果满足以下条件,则这些结果是可串行化的:

  • 存在一种串行执行事务的顺序,
  • 这种顺序产生的结果与实际执行的结果相同。

(串行意味着一次一个——没有并行执行。)

(这个定义应该让你想起线性一致性。)

您可以通过寻找产生相同结果的顺序来测试执行结果是否是可串行化的。对于我们的例子,可能的串行顺序是:

  • T1; T2
  • T2; T1

因此,正确的(可串行化的)结果是:

  • T1; T2:x=11 y=9 “11,9”
  • T2; T1:x=11 y=9 “10,10”

两者的结果不同;任何一种都可以接受。没有其他结果是可以接受的。实现可能已经并行执行了T1和T2,但它必须仍然产生如同按照串行顺序执行一样的结果。

为什么可串行化很受欢迎
  • 对程序员来说是一个简单的模型
    • 程序员可以编写复杂的事务,同时忽略并发性。
  • 它允许对不同记录的事务进行并行执行。
事务可以在出现问题时“中止”
  • 中止会撤销任何记录修改。
  • 事务可能会自愿中止,
    • 例如,如果账户不存在,或者y的余额小于等于0。
  • 系统可能会强制中止,例如,为了打破锁定死锁。
  • 一些服务器故障会导致中止。
  • 应用程序可能会(也可能不会)再次尝试事务
分布式事务包含两个主要组成部分:
  • 并发控制(提供隔离性/可串行化)
  • 原子提交(尽管有失败,也能提供原子性)

首先,让我们谈谈并发控制:

  • 正确执行并发事务

事务的并发控制分为两类:

  • 悲观并发控制
    • 在使用记录前对其加锁
    • 冲突会导致延迟(等待锁)
  • 乐观并发控制
    • 不加锁地使用记录
    • 提交时检查读/写操作是否可串行化
    • 冲突会导致中止+重试
    • 称为乐观并发控制(OCC)
  • 如果冲突频繁,悲观并发控制更快
  • 如果冲突罕见,乐观并发控制更快

今天的话题是悲观并发控制,下周将讨论乐观并发控制(FaRM)

“两阶段锁定”是实现可串行化的一种方式:

  • 两阶段锁(2PL)定义
    • 事务在使用记录前必须获取记录的锁
    • 事务必须持有其锁,直到提交或中止之后
我们例子中的两阶段锁定

假设T1和T2同时开始:

  • 事务系统会根据需要自动获取锁
  • 因此,T1/T2中首先使用x的将获取锁
  • 另一个事务将等待,直到第一个事务完全结束
  • 这阻止了非可串行化的交错执行
详细信息:
  • 每个数据库记录都有一个锁
  • 如果是分布式的,锁通常存储在记录的服务器上
    • [图表:客户端、服务器、记录、锁]
    • (但两阶段锁定不太受分布式影响)
  • 执行事务需要时获取锁,首次使用时
    • add()get()隐式获取记录的锁
    • end_xaction()释放所有锁
  • 所有锁都是排他性的(在此讨论中,没有读/写锁)
  • 全名是“强严格两阶段锁定”
  • 与线程锁定相关(例如,Go的Mutex),但更简单:
    • 显式的begin/end_xaction
    • 数据库在每条记录首次使用时自动加锁
    • 数据库在事务结束时自动解锁
    • 数据库可能会自动中止以解决死锁

保持锁直到提交(commit)或中止(abort)之后的原因是为了确保事务的整体原子性和一致性。如果在完成记录使用后立即释放锁,可能会导致一些问题,例如:

  • 如果T2在get(x)后立即释放x的锁,T1就可以在T2的两次get()操作之间执行。结果,T2可能会打印10,9,这并不是一个可串行化的执行:既不是T1;T2也不是T2;T1的顺序。
  • 如果T1写入x然后立即释放x的锁,T2可以读取x并打印结果。但如果T1之后中止,那么T2使用了一个实际上从未存在过的值。理论上,我们应该中止T2,这将是一个“级联中止”;这样做会非常尴尬。

两阶段锁定(2PL)可能会导致死锁,例如:

T1      T2
get(x)  get(y)
get(y)  get(x)

系统必须检测(循环?锁超时?)并中止一个事务。

两阶段锁定(2PL)是否会禁止一个正确的(可串行化的)执行?

是的,例如:

T1        T2
get(x)  get(x)put(x,2)
put(x,1)

锁定会禁止这种交错执行,但结果(x=1)是可串行化的(与T2;T1相同)。

问题:描述一个情况,其中两阶段锁定比简单锁定产生更高的性能。

简单锁定:在任何使用之前锁定每个记录;在中止/提交后释放。

两阶段锁定(2PL)比简单锁定(Simple Locking)表现出更高性能的一个情况是:

  • 当事务访问的数据集合较小并且与其他事务的重叠较少时。在这种情况下,2PL通过只在需要时锁定记录,允许更多的并行执行。事务可以更快地完成其操作并释放资源,减少了等待和阻塞的时间,从而提高了整体系统性能。
  • 相反,简单锁定策略要求事务在开始执行任何操作之前锁定其将访问的所有记录,这会导致不必要的等待,特别是在事务只需要读取一小部分其锁定记录的情况下。这种策略在高并发环境下性能较差,因为它限制了并行操作的可能性,增加了事务完成时间。

Next topic: distributed transactions versus failures

我们将开发一个称为“两阶段提交”的协议,它被分布式数据库用于多服务器事务

我们想要的是“原子提交”:

  • 一群计算机在某项任务上合作
  • 每台计算机扮演不同的角色
  • 想要确保原子性:全部执行,或者全部不执行
  • 挑战:故障,性能
设置
  • 数据在多个服务器之间分片
  • 事务在“事务协调器”(TC)上运行
  • 对于每个读/写,TC向相关的分片服务器发送RPC
    • 每个都是一个“参与者”
    • 每个参与者管理其数据分片的锁
  • 可能有许多并发事务,许多TC
    • TC为每个事务分配唯一的事务ID(TID)
    • 每条消息,每个表条目都用TID标记
    • 为了避免混淆
无故障时的两阶段提交:
  • TC向A,B发送put(),get()等RPC
    • 修改是暂定的,只有在提交时才会安装。
  • TC进行到事务的末尾。
  • TC向A和B发送准备(PREPARE)消息。
  • 如果A愿意提交,
    • A回应YES。
    • 然后A处于“准备好的”状态。
  • 否则,A回应NO。
  • B也是同样。
  • 如果A和B都说YES,TC向A和B发送提交(COMMIT)消息。
  • 如果A或B任何一个说NO,TC发送中止(ABORT)消息。
  • 如果A/B收到TC的提交消息,他们将提交。
    • 即,他们将暂定记录写入真实数据库。
    • 并释放其记录上的事务锁。
  • A/B确认提交消息。

这个协议试图确保即使在分布式系统中发生故障时,事务也能保持原子性,确保所有参与的服务器要么都提交事务,要么都不提交。

在这里插入图片描述

这为什么到目前为止是正确的?

  • 除非A和B都同意,否则它们都不能提交。

如果B崩溃并重启会怎样?

  • 如果B在崩溃前发送了YES,B必须记住(尽管发生了崩溃)!
  • 因为A可能已经收到了COMMIT消息并提交了。
  • 所以B必须能够在重启后提交(或不提交)。

因此,参与者必须写入持久的(磁盘上的)状态:

  • B在说YES前必须在磁盘上记住,包括修改过的数据。
  • 如果B重启,磁盘显示YES但没有COMMIT,
    • B必须询问TC,或等待TC重新发送。
  • 与此同时,B必须继续持有事务的锁。
  • 如果TC说COMMIT,B将修改的数据复制到真实数据。

如果TC崩溃并重启会怎样?

  • 如果TC可能在崩溃前发送了COMMIT,TC必须记住!
    • 因为一个工作者可能已经提交了。
  • 因此,TC必须在发送COMMIT消息前将COMMIT写入磁盘。
  • 并且如果它崩溃并重启后重复COMMIT,
    • 或者如果参与者询问(即,如果A/B没有收到COMMIT消息)。
  • 参与者必须过滤掉重复的COMMIT(使用TID)。

如果TC从B那里永远得不到YES/NO怎么办?

  • 也许B崩溃了并没有恢复;也许网络出现了故障。
  • TC可以超时,并中止(因为没有发送任何COMMIT消息)。
  • 好处是:允许服务器释放锁。

如果B在等待TC的PREPARE时超时或崩溃怎么办?

  • B还没有对PREPARE做出回应,所以TC不能决定提交
  • 所以B可以单方面中止,并释放锁
  • 对未来的PREPARE响应NO

如果B回答了PREPARE的YES,但没有收到COMMIT或ABORT怎么办?

  • B可以单方面决定中止吗?
    • 不可以!TC可能已经从两者那里得到了YES,
    • 并且向A发送了COMMIT,但在发送给B之前崩溃了。
    • 那么A将提交而B将中止:这是错误的。
  • B也不能单方面提交:
    • A可能已经投了NO。

所以:如果B投了YES,它必须“阻塞”:等待TC的决定。

注意:

  • 提交/中止决定由单一实体——TC做出。
  • 这使得两阶段提交相对直接。
  • 惩罚是A/B在投了YES之后必须等待TC。

TC何时可以完全忘记一个已提交的事务?

  • 如果它从每个参与者那里收到了对COMMIT的确认。
  • 然后没有参与者会再次需要询问。

参与者何时可以完全忘记一个已提交的事务?

  • 在它确认了TC的COMMIT消息之后。
  • 如果它再次收到COMMIT,并且没有该事务的记录,
    • 它必须已经提交并忘记了,并且可以再次确认。

两阶段提交的视角

  • 在事务使用多个分片上的数据时,在分片数据库中使用
  • 但它有不好的声誉:
    • 慢:多轮消息传递
    • 慢:磁盘写入
    • 在准备/提交交换期间持有锁;阻塞其他事务
    • TC崩溃可能导致无限期阻塞,同时持有锁
  • 因此通常只在单一小领域中使用
    • 例如,不在银行之间,不在航空公司之间,不在广域网上
Raft和两阶段提交解决的是不同的问题!
  • 使用Raft通过复制来获得高可用性
    • 即,当一些服务器崩溃时仍能操作
    • 服务器都做相同的事情
  • 使用2PC是当每个参与者做不同的事情时
    • 并且所有参与者都必须做他们的部分
  • 2PC不提高可用性
    • 因为所有服务器必须运行才能完成任何事情
  • Raft不确保所有服务器都做某事
    • 因为只需要大多数活着就可以了

如果你想要高可用性原子提交怎么办?

  • 这里有一个计划。
    • TC和服务器应该每个都用Raft复制
    • 在复制的服务之间运行两阶段提交
    • 然后你可以在容忍故障的同时仍然取得进展
    • 你将在实验4中构建类似的东西来转移分片
    • 下次会议的Spanner使用了这种安排

FAT

  • 为什么事务的原子性如此重要?

    所谓“事务”,意味着事务内的步骤相对于故障和其他事务而言是原子性发生的。这里的原子性意味着“全部或无”。事务是某些存储系统提供的一项功能,旨在简化编程工作。事务有用的一个例子是银行转账。如果银行想要从爱丽丝的账户转移100美元到鲍勃的账户,如果在此过程中途发生崩溃,导致爱丽丝的账户被扣除了100美元但鲍勃的账户增加100美元,这将非常尴尬。因此,如果您的存储系统支持事务,程序员可以编写如下内容:

    BEGIN TRANSACTIONdecrease Alice's balance by 100;increase Bob's balance by 100;
    END TRANSACTION
    

    事务系统将确保事务是原子的;即使在某处发生故障,也要么两者都发生,要么都不发生。

  • 两阶段锁定是否会产生死锁?

    是的。如果两个事务都使用记录R1和R2,但以相反的顺序,它们将各自获得其中一个锁,然后在尝试获取另一个锁时发生死锁。数据库能够检测到这些死锁并解决它们。数据库可以通过锁获取的超时,或者在事务之间的等待图中寻找循环来检测死锁。可以通过中止参与的其中一个事务来打破死锁。

  • 可以同时有多个活跃的事务吗?参与者如何知道一条消息是指哪个事务?

    可以有许多并发事务,由许多事务协调器(TC)管理。TC为每个事务分配一个唯一的事务ID(TID)。每条消息都包括相关事务的TID。TC和参与者用TID标记他们表中的条目,这样(例如)当一个COMMIT消息到达一个参与者时,它知道哪些暂定记录要变成永久的,以及要释放哪些锁。

  • 如果必须中止一个事务,两阶段提交系统如何撤销修改?

    每个参与者对记录的临时副本进行修改。如果参与者对TC的准备消息回答“是”,参与者必须首先将临时记录值保存到其磁盘上的日志中,这样如果它崩溃并重启,它可以找到它们。如果TC决定提交,参与者必须将临时值复制到真实数据库记录中;如果TC决定中止,参与者必须丢弃临时记录。

  • 可串行化与线性一致性有什么关系?

    它们是来自不同社区的类似概念。两者都要求最终结果与某些串行执行相同。可串行化通常指的是涉及多个操作的整个事务。线性一致性通常指的是简单的读和写操作。还有一点是,线性一致性要求等效的串行执行与实际执行的实时顺序相匹配,而可串行化通常不要求这一点。

  • 为什么在我们查看的设计中经常出现日志?

    • 一个原因是日志捕获了系统为事务选择的串行顺序,这样例如所有副本以相同的顺序执行事务,或者服务器在崩溃+重启后按照与崩溃前相同的顺序考虑事务。
    • 另一个原因是日志是一种高效的方式将数据写入硬盘或SSD,因为这两种媒介在顺序写入(即追加到日志)时要比随机写入快得多。、第三个原因是日志是一种方便的方式,让崩溃恢复软件看到系统在崩溃前进行到哪一步,以及最后的事务是否在日志中有完整记录,因此可以安全地重放。也就是说,日志是实现可崩溃恢复的原子事务的一种方便方式,通过预写日志(write-ahead logging)。

相关文章:

Distributed Transactions Mit 6.824

Topic1:distributed transactions concurrency control atomic commit 传统计划:事务 程序员标记代码序列的开始/结束作为事务。 事务示例 x 和 y 是银行余额——数据库表中的记录。x 和 y 位于不同的服务器上(可能在不同的银行&#x…...

Redis可视化工具:Another Redis Desktop Manager下载安装使用

1.Github下载 github下载地址: Releases qishibo/AnotherRedisDesktopManager GitHub 2. 安装 直接双击exe文件进行安装 3. 连接Redis服务 先启动Redis服务,具体启动过程可参考: Windows安装并启动Redis服务端(zip包&#xff09…...

Parquet文件格式详解(含行、列式存储区别)

Parquet文件格式详解 Parquet 是一种列式存储格式,旨在高效地存储和处理大规模数据集。它被设计用于在大数据生态系统中进行数据存储和分析,如 Apache Hadoop 和 Apache Spark。 行式存储 vs 列式存储 在了解 Parquet 文件格式之前,先来对…...

一文了解https为什么是安全的

目录 前言一、https和http二、http为什么不安全?2.1 http的工作原理2.2 http的明文传输 三、https3.1 加密3.2 身份验证 四、总结 前言 目前绝大多数网站都已经切换到了https,切换的原因很简单,因为它更安全,https未来会完全取代…...

[‘column‘]和[:,‘column‘]的区别

之前,关于numpy和pandas的操作一直不熟悉,对于获取数据中的行,列一直混淆。 df[column] df[column]是 Pandas DataFrame 切片的常用语法,用于选择名为 column 的单个列。它返回一个 Pandas Series 对象。 df.loc[:,column] df[:,…...

icloud如何高效利用

iCloud是Apple提供的一项云存储和云计算服务,能够帮助用户在不同的Apple设备之间同步和共享数据。要高效利用iCloud,可以参考以下几个方面: 自动备份:确保所有重要的Apple设备都开启了iCloud备份功能,这样可以自动将设…...

k8s二进制安装与部署

目录 一、实验目的 二、实验环境 三、实验步骤 3.1 操作系统初始化配置 3.2 部署 docker引擎 3.3 部署 etcd 集群 3.3.1 在 master01 节点上操作 ​3.3.2 在 node01 节点上操作 3.3.3 在 node02 节点上操作 3.4 部署 Master 组件 3.4.1 在 mast…...

驱动编译报error: negative width in bit-field ‘<anonymous>’错误

错误如下图所示: 代码如下: 问题点:module_param的其他用户的权限参数上。 在Linux中,文件权限由读(r)、写(w)、执行(x)权限组成,分别对应数值4、2、1。 第一位0是占位符,在这里没有意义,因为…...

Go语言的命名规范是怎样的?

文章目录 Go语言的命名规范详解一、标识符命名规范示例代码 二、包名命名规范示例代码 三、变量命名规范示例代码 四、常量命名规范示例代码 五、函数命名规范示例代码 总结 Go语言的命名规范详解 在Go语言中,代码的命名规范对于项目的可读性、可维护性和可扩展性至…...

Vue3骨架屏(Skeleton)

效果如下图:在线预览 APIs 参数说明类型默认值必传animated是否展示动画效果booleantruefalsebutton是否使用按钮占位图boolean | SkeletonButtonPropsfalsefalseavatar是否显示头像占位图boolean | SkeletonAvatarPropsfalsefalseinput是否使用输入框占位图boolea…...

【文末附gpt升级方案】亚马逊与Hugging Face合作:定制芯片低成本运行AI模型的创新探索

亚马逊与Hugging Face合作:定制芯片低成本运行AI模型的创新探索 摘要 本文探讨了亚马逊云部门与人工智能初创公司Hugging Face的合作,旨在通过定制计算芯片Inferentia2在亚马逊网络服务(AWS)上更低成本地运行数千个AI模型。文章首…...

二叉树的链式实现

目录 一、二叉树的基础操作 二、二叉树代码图解 2.1 遍历 2.2 求大小 2.3 创建与销毁 2.4 与队列结合解决问题 三、二叉树C语言源码汇总 二叉树的代码实现运用了函数递归的思想,了解函数递归的知识请见博主的另一篇博客: http://t.csdnimg.cn/Po…...

STM32中断编程入门

文章目录 一、 理论部分1.中断系统2.中断执行流程3.NVIC的基本结构4.EXTI介绍5.AFIO复用IO口 二、实验目的:学习stm32中断原理和开发编程方法。使用标准完成以下任务:(一)实验一 开关控制LED的亮灭1.代码部分2.运行结果 &#xff…...

《我的阿勒泰》读后感

暂没时间写,记录在此,防止忘记,后面补上!!! 【经典语录】 01、如果天气好的话,阳光广阔地照耀着世界,暖洋洋又懒洋洋。这样的阳光下,似乎脚下的每一株草都和我一样,也把身子完全舒展开了。 02、…...

Android.mk简单介绍、规则与基本格式

文章目录 Android.mk与makefile区别Android.mk规则Android.mk基本格式 Android.mk与makefile区别 Android.mk 和 Makefile 都是用于构建代码项目的构建脚本文件,但是它们在特定上下文中有一些区别: Android.mk: Android.mk 是用于构建 Android 应用或库…...

【MySQL精通之路】InnoDB(3)-MVCC多版本管理

InnoDB是一个多版本(MVCC)的存储引擎。 它保留有关更改行的旧版本的信息,以支持事务性功能,如并发和回滚。 这些信息存储在称为回滚段的数据结构中的Undo表空间中。 参见“Undo表空间”。 InnoDB使用回滚段(rollback…...

uniapp 对接 微信App/支付宝App 支付

相关文档:uni.requestPayment(OBJECT) | uni-app官网 示例代码: import qs from qsasync aliPay(){const { provider } await uni.getProvider({ service:payment })if(provider.includes(alipay)){uni.request({url:后端接口地址,data:{ //传参 },suc…...

cmake配置opencv与boost库

Cmake配置外部依赖库(以Opencv和Boost为例) Cmake对于外部依赖库,需要知道外部库的头文件路径,库文件路径以及库的名字。比如,对于要使用的Boost库,需要知道头文件的位置,库目录的位置以及库依…...

【Kotlin 一】Kotlin入门知识简介、变量声明、数字类型

1. Kotlin简介 Kotlin旨在解决 Java语言在编码效率和代码质量上存在的问题,并且与Java语言完全兼容。Kotlin通过简化语法、提供更强大的函数以及减少样本代码的编写,使开发者能够更高效地编写代码。Kotlin适用于Android、Web后端开发等多种场景 2.Kotl…...

Java 微信小程序登录(openId方式)

1 需求 在开发微信小程序项目时,登录采用的是openId方式,这是一种用户无感的登录方式,用户点开微信小程序时,去调用后端的登录接口。 核心代码 Slf4j Component public class WeChatUtil {private static final String …...

conda相比python好处

Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理&#xff1a…...

XCTF-web-easyupload

试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM&#xff09…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...