MySQL架构

Chinese, Simplified
SEO Title
mysql Architecture

【MySQL】MySQL高可用性框架解释 - 第三部分:失败场景

Chinese, Simplified

在这个由三部分组成的博客系列中,我们在第一部分中介绍了MySQL托管的高可用性(HA)框架,并在第二部分中讨论了MySQL半同步复制的细节。现在在第三部分中,我们将回顾框架如何处理一些重要的MySQL故障情况并进行恢复以确保高可用性。

MySQL失败场景


场景1  -  Master MySQL关闭

 

  1. Corosync和Pacemaker框架检测到主MySQL不再可用。如果可能的话,Pacemaker会降级主资源并尝试通过重新启动MySQL服务来恢复。
  2. 此时,由于复制的半同步性质,至少一个从设备已接收在主设备上提交的所有事务。
  3. 起搏器等待,直到所有收到的交易都应用于奴隶并让奴隶报告他们的促销分数。分数计算以如下方式进行:如果从属设备与主设备完全同步则得分为“0”,否则为负数。
  4. Pacemaker选择已报告0分的奴隶,并提升该奴隶,该奴隶现在担任允许写入的主MySQL角色。
  5. 从属升级后,资源代理会触发DNS重新路由模块。模块使用新主服务器的IP地址更新代理DNS条目,从而促进所有应用程序写入重定向到新主服务器。
  6. Pacemaker还设置可用的从站以开始从这个新主站复制。

因此,每当主MySQL出现故障时(无论是由于MySQL崩溃,操作系统崩溃,系统重启等),我们的HA框架都会检测到它并促使合适的从服务器接管主服务器的角色。这可确保系统继续可供应用程序使用。

 

场景2  -  Slave MySQL关闭

 

  1. Corosync和Pacemaker框架检测到从属MySQL不再可用。
  2. Pacemaker尝试通过尝试在节点上重新启动MySQL来恢复资源。如果它出现,它将作为从属添加回当前主服务器并继续复制。
  3. 如果恢复失败,Pacemaker会将资源报告为已关闭 - 基于可生成的警报或通知。如有必要,ScaleGrid支持团队将处理此节点的恢复。

在这种情况下,对MySQL服务的可用性没有影响。


场景3  - 网络分区 - 网络连接在主节点和从节点之间分解


这是任何分布式系统中的经典问题,其中每个节点认为其他节点已关闭,而实际上,仅断开节点之间的网络通信。这种情况通常被称为裂脑情景,如果处理不当,可能会导致多个节点声称自己是主MySQL,从而导致数据不一致和损坏。

让我们用一个例子来回顾一下我们的框架如何处理集群中的裂脑情景。我们假设由于网络问题,集群已分为两组 - 一组中的主机和另一组中的两个机箱,我们将其表示为[(M),(S1,S2)]。

  1. Corosync检测到主节点无法与从节点通信,并且从节点可以相互通信,但不能与主节点通信。
  2. 主节点将无法提交任何事务,因为半主机可以提交之前,半同步复制需要来自至少一个从服务器的确认。与此同时,由于缺乏基于Pacemaker设置'no-quorum-policy = stop'的法定人数,Pacemaker在主节点上关闭了MySQL。仲裁在这里意味着大多数节点,或3节点集群设置中的两个节点。由于在此群集的分区中只运行一个主节点,因此会触发no-quorum-policy设置,从而导致MySQL主服务器关闭。
  3. 现在,分区上的Pacemaker [(S1),(S2)]检测到群集中没有可用的主服务器并启动促销过程。假设S1与主服务器是最新的(由半同步复制保证),则它将被提升为新主服务器。
  4. 应用程序流量将重定向到此新的主MySQL节点,从属S2将开始从新主节点复制。

因此,我们看到MySQL HA框架有效地处理裂脑情况,确保在主节点和从节点之间网络连接中断时数据的一致性和可用性。

以上是关于使用半同步复制和 Corosync 加 Pacemaker堆栈的MySQL高可用性(HA)框架的3部分博客系列。在ScaleGrid,我们在AWS上为MySQL提供高度可用的托管,在Azure上提供MySQL,这是基于本博客系列中介绍的概念实现的。请访问ScaleGrid控制台,免费试用我们的解决方案。

原文:https://scalegrid.io/blog/mysql-high-availability-framework-explained-part-3/

本文:http://pub.intelligentx.net/mysql-high-availability-framework-explained-part-iii-failure-scenarios

讨论:请加入知识星球或者小红圈【首席架构师圈】

SEO Title
MySQL High Availability Framework Explained – Part III: Failure Scenarios

【MySQL】MySQL高可用性框架解释 - 第二部分:半同步复制

Chinese, Simplified

在第一部分中,我们介绍了MySQL托管的高可用性(HA)框架,并讨论了各种组件及其功能。现在在第二部分中,我们将讨论MySQL半同步复制的细节以及相关的配置设置,这些设置有助于我们确保HA设置中数据的冗余和一致性。请务必重新检入第III部分,我们将审查可能出现的各种故障情况以及框架响应和恢复这些条件的方式。

什么是MySQL半同步复制?


简单地说,在MySQL MySQL semisynchronous replication配置中,主设备仅在收到来自至少一个从设备的确认后才将事务提交到存储引擎。只有在收到事件并将其复制到中继日志并刷新到磁盘后,从站才会提供确认。这保证了对于提交并返回到客户端的所有事务,数据至少存在于2个节点上。半同步(复制)中的术语“半”是由于主设备在接收到事件并将其刷新到中继日志后提交事务,但不一定提交给从设备上的数据文件。这与完全同步复制形成对比,完全同步复制在会话返回客户端之前,事务将在从服务器和主服务器上提交。

半导体复制(在MySQL中本机可用)可帮助HA框架确保已提交事务的数据一致性和冗余。如果主服务器出现故障,主服务器上提交的所有事务都将被复制到至少一个从服务器(保存到中继日志)。因此,故障转移到该从站将是无损的,因为从站是最新的(在从站的中继日志完全耗尽之后)。

复制和半同步相关设置


让我们讨论一些关键的MySQL设置,这些设置用于确保我们框架中的高可用性和数据一致性的最佳行为。

管理Slave的执行速度


第一个考虑因素是处理半同步复制的“半”行为,它只保证数据已被接收器的I / O线程接收并刷新到中继日志,但不一定由SQL线程提交。默认情况下,MySQL从属服务器中的SQL线程是单线程的,无法与多线程的主服务器保持同步。这样做的明显影响是,在主服务器发生故障时,从服务器将不会是最新的,因为它的SQL线程仍在处理中继日志中的事件。这将延迟故障转移过程,因为我们的框架期望从服务器在可以升级之前完全是最新的。这对于保持数据一致性是必要的。为解决此问题,我们使用slave_parallel_workers选项启用多线程从站,以设置处理中继日志中事件的并行SQL线程数。

此外,我们配置以下设置,以确保从站不会进入主站不在的任何状态:


这为我们提供了更强的数据一致性。通过这些设置,我们将能够在从属设备上获得更好的并行化和速度,但是如果并行线程太多,则线程之间协调所涉及的开销也会增加,并且不幸地会抵消这些优势。

我们可以用来提高从站上并行执行效率的另一个配置是调整主站上的 binlog_group_commit_sync_delay 。通过在master上设置它,主服务器上的二进制日志条目以及从服务器上的中继日志条目将具有可由SQL线程并行处理的批量事务。这在 J-F Gagné’s blog 中有详细解释,他将这种行为称为“减慢主人加速奴隶的速度”。

如果您通过ScaleGrid控制台管理MySQL部署,则可以持续监视和接收有关从属服务器复制延迟的实时警报。它还允许您动态调整上述参数,以确保从站与主站一起工作,从而最大限度地减少故障转移过程中的时间。

重要的半同步复制选项


根据设计,MySQL半同步复制可以基于从确认超时设置或基于任何时间点可用的具有半同步能力的从设备的数量而回退到异步模式。根据定义,异步模式不能保证已提交的事务被复制到从属服务器,因此主服务器丢失会导致丢失尚未复制的数据。 ScaleGrid HA框架的默认设计是避免回退到异步模式。让我们来看看影响这种行为的配置。

rpl_semi_sync_master_wait_for_slave_count


此选项用于配置在半同步主服务器提交事务之前必须发送确认的从站数。在3节点主从配置中,我们将其设置为1,因此我们始终确保数据在至少一个从站中可用,同时避免在等待来自两个从站的确认时所涉及的任何性能影响。

rpl_semi_sync_master_timeout


此选项用于配置半同步主控器在切换回异步模式之前等待来自从器件的确认的时间。我们将其设置为相对较高的超时值,因此不会回退到异步模式。

由于我们使用2个从站运行并且rpl_semi_sync_master_wait_for_slave_count设置为1,因此我们注意到至少有一个从站确实在合理的时间内确认,并且主站在临时网络中断期间不会切换到异步模式。

rpl_semi_sync_master_wait_no_slave


这将控制主服务器是否等待rpl_semi_sync_master_timeout配置的超时时间到期,即使从服务器计数降至小于超时期间rpl_semi_sync_master_wait_for_slave_count配置的从服务器数。我们保留默认值ON,以便主服务器不会回退到异步复制。

失去所有半同步从属的影响


如上所述,如果所有从站都关闭或无法从主站访问,我们的框架会阻止主站切换到异步复制。这样做的直接影响是写入在主服务器上停滞不前影响服务的可用性。这基本上如CAP定理所述,关于任何分布式系统的局限性。该定理指出,在存在网络分区的情况下,我们必须选择可用性或一致性,但不能同时选择两者。在这种情况下,网络分区可以被视为与主服务器断开连接的MySQL从服务器,因为它们已关闭或无法访问。

我们的一致性目标是确保对于所有已提交的事务,数据至少在2个节点上可用。因此,在这种情况下,ScaleGrid HA框架有利于可用性的一致性。虽然MySQL主服务器仍将提供读取请求,但不会从客户端接受进一步的写入。这是我们作为默认行为的有意识的设计决策,当然,它可以根据应用程序要求进行配置。

请务必订阅ScaleGrid博客,这样您就不会错过第三部分,我们将讨论更多故障情景和MySQL HA框架的恢复能力。敬请关注!!

原文:https://scalegrid.io/blog/mysql-high-availability-framework-explained-part-2/

本文: http://pub.intelligentx.net/mysql-high-availability-framework-explained-part-ii-semisynchronous-replication

讨论:请加入知识星球或者小红圈【首席架构师圈】

SEO Title
MySQL High Availability Framework Explained – Part II: Semisynchronous Replication

【MySQL】为什么甲骨文继续诋毁自己的产品MySQL?

Chinese, Simplified

甲骨文首席技术官拉里·埃里森(Larry Ellison)无法在他的财报电话中充分利用MySQL。 有这种疯狂的方法吗?

oracle.jpg

从大多数人的角度来看,甲骨文一直是MySQL的一个相当好的管家,MySQL是几年前甲骨文收购Sun Microsystems时所采用的开源数据库。甲骨文为MySQL提供了重要的工程资源,以确保“在长期以来被认为无法解决的领域中提高性能”,正如之前的MySQL产品主管Zack Urlocker所说的那样。

最近,Oracle首席技术官兼董事长拉里·埃里森毫不掩饰他对MySQL的蔑视,告诉分析师“你必须愿意放弃大量的可靠性,大量的安全性,大量的性能[使用MySQL而不是甲骨文因为......我们拥有巨大的技术优势。“这对于竞争对手来说是公平的游戏,但这是Ellison谈论主要由Oracle开发的产品。

 

陷入交火


并非埃里森开始贬低该行业第二大流行数据库 - 他试图破坏对亚马逊网络服务的信心,亚马逊网络服务提供MySQL作为数据库服务,无论是作为RDS还是作为Aurora。埃里森因为伸展事实以适应他的竞争姿态而闻名,他相信“亚马逊Aurora只是MySQL的开源,亚马逊Redshift也只是一个借来的开源系统。”正如Gartner分析师Nick Heudecker指出的那样,这两个断言都是可疑的。

确实,AWS客户可以购买具有MySQL智能功能的Aurora服务,但除此之外还有更多内容。

 

MySQL还有比“非常古老的系统[]更多”。 (来自甲骨文的一个好奇的批评,甚至推翻了更老的数据库技术。)而且,“Oracle的自主数据库拥有甲骨文所拥有的最大技术领先优势”(在任何有意义的意义上)都是不对的。 (尽管AWS产生的IaaS收入比Oracle高出80倍,PaaS收入比Oracle高出10倍,但这个同样的传闻听起来像甲骨文的前笑语“AWS落后甲骨文20年”。)甲骨文新的许可收入一直在下降很多年了。相比之下,MySQL和它的亲吻表兄PostgreSQL仍然很受欢迎,无论是通过像AWS这样的云公司购买还是在本地运行。

甲骨文酒店加州


事实是,甲骨文让人难以置信地离开。如果没有[那么多]努力,如果亚马逊甚至无法到达亚马逊数据库,那么“没有人(但亚马逊)会如何通过强制游行进行宣传”,埃里森提出了迁移数据库的困难,特别是,Oracle的数据库,还有其他任何东西。 “但就技术而言,没有人可以移动 - 一个普通人会从Oracle数据库转移到亚马逊数据库。这只是非常昂贵和复杂。”

对于渴望资金的金融分析师来说,这种声音可能会很好,但如果你是开发人员或CIO阅读埃里森的评论,它会发出什么样的信号?

看看:甲骨文的埃里森:“正常”的人不会转向AWS(ZDNet)

它说,正如一位Twitter评论员所说的那样,“埃里森是正确的,离开甲骨文是昂贵和复杂的。这就是它的设计方式。这就像邀请臭虫进入你的房子。”甲骨文... ...臭虫确实。

这只是许多开发人员根本没有开始使用Oracle的原因之一。事实上,我猜想几乎没有开发人员选择Oracle。他们怎么样? Oracle技术不仅繁琐复杂,而且价格昂贵。开发人员可以以0.00美元的价格开始使用MySQL,或者使用在AWS上运行的MySQL(或RDS或Aurora),价格为0.01美元。纯粹从便利因素来看,Oracle每次都会失败。

甲骨文唯一要做的就是惯性。正如Gartner分析师Merv Adrian所说:“遗留DBMS中最大的力量就是惯性。”不是技术优势。没有成本优势。除习惯和钙化方案设计,物理数据放置等多年以外,其他任何事情都不是

但这仅仅是旧应用程序的护城河。随着公司构建未来,他们并没有在Oracle上构建它,尽管他们有时可能会在Oracle似乎鄙视的开源数据库上构建它,MySQL。鉴于埃里森对自己的产品感到厌恶,很难看出甲骨文长期以来一直是MySQL的管家。

 

原文:https://www.techrepublic.com/article/why-does-oracle-keep-trashing-mysql-its-own-product/

本文:

讨论:请加入知识星球或者小红圈【首席架构师圈】

SEO Title
Why does Oracle keep trashing MySQL, its own product?

【MySQL架构】MariaDB versus MySQL: Compatibility

Chinese, Simplified

完全替代MySQL

MariaDB版本的功能相当于一个“完全替代”的MySQL版本,但有一些限制。这意味着:

  • MariaDB的数据文件通常是二进制的,与MySQL版本的数据文件兼容。
    • 所有的文件名和路径通常是相同的。
    • 数据和表定义文件(.frm)文件是二进制兼容的。
    • 请参阅下面的注释,查看与视图的不兼容性!
  • MariaDB的客户端协议与MySQL的客户端协议是二进制兼容的。
    • 所有的客户端api和结构都是相同的。
    • 所有端口和套接字通常是相同的。
    • 所有的MySQL连接器(PHP、Perl、Python、Java、. net、MyODBC、Ruby、MySQL C连接器等)都可以不加修改地使用MariaDB。
    • 您应该注意PHP5的一些安装问题(旧的PHP5客户机如何检查库兼容性的一个bug)。

这意味着在很多情况下,你可以卸载MySQL并安装MariaDB,这样就可以了。

通常不需要转换任何数据文件。但是,您仍然必须运行mysql_upgrade来完成升级。

这是确保mysql特权和事件表使用MariaDB使用的新字段更新所必需的。我们每月都会合并MySQL代码库,以确保MariaDB有任何相关的bug修复添加到MySQL中。

也就是说,MariaDB有很多新的选项、扩展、存储引擎和修复MySQL中没有的bug。

你可以在不同的MariaDB发布页面上找到不同版本的特性集。

特定MariaDB版本的完全兼容性

就InnoDB而言,MariaDB 10.2、MariaDB 10.3和MariaDB 10.4是MySQL 5.7的有限替代。然而,在每一个新的MariaDB版本中,实现差异都在不断增加。

就InnoDB而言,MariaDB 10.0和MariaDB 10.1可以作为MySQL 5.6的有限替代。

但是,在某些特性中存在一些实现差异。MariaDB 5.5是MySQL 5.5的替代版本。

MariaDB 5.1、MariaDB 5.2和MariaDB 5.3可以作为MySQL 5.1的完全替代。

复制的兼容性

Master→ MariaDB-5.5 MariaDB-10.1 MariaDB-10.2 MariaDB-10.3 MariaDB-10.4 MySQL-5.6 MySQL-5.7 MySQL-8.0
Slave ↓                  
MariaDB-5.5  
MariaDB-10.1            
MariaDB-10.2        
MariaDB-10.3      
MariaDB-10.4    
MySQL-5.6            
MySQL-5.7            
MySQL-8.0            

 

  • 支持✅:这种组合。
  • 不支持⛔:这种组合。
  • ∗:Mariadb不能对mysql的组合进行任何断言。要确定受支持的组合,请参阅特定MySQL版本的文档。

注意:当以GTID模式从MySQL复制时,MariaDB将删除MySQL GTID事件,并将它们替换为MariaDB GTID事件。

原文:https://mariadb.com/kb/en/library/mariadb-vs-mysql-compatibility/

本文:https://pub.intelligentx.net/mariadb-versus-mysql-compatibility

讨论:请加入知识星球或者小红圈【首席架构师圈】

SEO Title
MariaDB versus MySQL: Compatibility

【PostgreSQL架构】PostgreSQL和堆元组更新-第1部分

Chinese, Simplified

这是一系列文章,重点介绍版本11中的新功能。

在此版本的开发过程中,一个功能引起了我的注意。可以在发行说明中找到:https://www.postgresql.org/docs/11/static/release-11.html

当表达式的值不变时,允许对表达式索引进行仅堆元组(HOT)更新(Konstantin Knizhnik)

我承认这不是很明确,并且此功能需要有关postgres如何工作的一些知识,我将尝试通过几篇文章进行解释:

  1. How MVCC works and heap-only-tuples updates
  2. When postgres do not use heap-only-tuple updates and introduction to the new feature in v11
  3. Impact on performances


在11.1中已禁用此功能,因为它可能导致实例崩溃1。我选择发布这些文章是因为它们有助于了解HOT更新的机制以及此功能可能带来的好处。

我感谢Guillaume Lelarge对本文的评论;)。

MVCC如何运作


由于MVCC,postgres不会直接更新行:它会复制行并提供可见性信息。

为什么会这样工作?

使用RDBMS时,需要考虑一个关键因素:并发性。

您正在编辑的行可能被先前的事务使用。正在进行的备份,例如:)

为此,RDBMS采用了不同的技术:

  1. 修改该行并将以前的版本存储在另一个位置。例如,这就是oracle对撤消日志所做的事情。
  2. 复制该行并存储可见性信息,以了解哪条交易可以看到哪条交易。这就需要一种清洁机制,以消除任何人都不可见的管线。这是Postgres中的实现,并且真空负责执行此清洁。

让我们看一个非常简单的表,并使用pageinspect扩展观看其内容的演变:

CREATE TABLE t2(c1 int);
INSERT INTO t2 VALUES (1);
SELECT lp,t_data FROM  heap_page_items(get_raw_page('t2',0));
 lp |   t_data   
----+------------
  1 | \x01000000
(1 row)

UPDATE t2 SET c1 = 2 WHERE c1 = 1;
SELECT lp,t_data FROM  heap_page_items(get_raw_page('t2',0));
 lp |   t_data   
----+------------
  1 | \x01000000
  2 | \x02000000
(2 rows)

VACUUM t2;
SELECT lp,t_data FROM  heap_page_items(get_raw_page('t2',0));
 lp |   t_data   
----+------------
  1 |
  2 | \x02000000
(2 rows)

 

我们可以看到引擎复制了管路和真空清洁的位置,以备将来使用。

heap-only-tuple 【HOT】机制


让我们来看另一种情况,稍微复杂一点,该表包含两列,并且在两列之一中有一个索引:

CREATE TABLE t3(c1 int,c2 int);
CREATE INDEX ON t3(c1);
INSERT INTO t3(c1,c2) VALUES (1,1);
INSERT INTO t3(c1,c2) VALUES (2,2);
SELECT ctid,* FROM t3;
 ctid  | c1 | c2
-------+----+----
 (0,1) |  1 |  1
 (0,2) |  2 |  2
(2 rows)

 

以相同的方式可以读取表块,也可以使用pageinspect读取块:

SELECT * FROM  bt_page_items(get_raw_page('t3_c1_idx',1));
 itemoffset | ctid  | itemlen | nulls | vars |          data           
------------+-------+---------+-------+------+-------------------------
          1 | (0,1) |      16 | f     | f    | 01 00 00 00 00 00 00 00
          2 | (0,2) |      16 | f     | f    | 02 00 00 00 00 00 00 00
(2 rows)

到目前为止,这非常简单,我的表包含两个记录,索引也包含两个记录,这些记录指向表中的相应块(ctid列)。

如果我使用新值3更新列c1,则必须更新索引。

现在,如果我更新列c2。 c1的索引会更新吗?

乍看起来,我们可能会说不,因为c1不变。

但是由于上面介绍的MVCC模型,理论上答案是肯定的:我们刚刚看到postgres将复制该行,因此其物理位置将有所不同(下一个ctid为(0.3))。

让我们来看看:

UPDATE t3 SET c2 = 3 WHERE c1=1;
SELECT lp,t_data,t_ctid FROM  heap_page_items(get_raw_page('t3',0));
 lp |       t_data       | t_ctid
----+--------------------+--------
  1 | \x0100000001000000 | (0,3)
  2 | \x0200000002000000 | (0,2)
  3 | \x0100000003000000 | (0,3)
(3 rows)


SELECT * FROM  bt_page_items(get_raw_page('t3_c1_idx',1));
 itemoffset | ctid  | itemlen | nulls | vars |          data           
------------+-------+---------+-------+------+-------------------------
          1 | (0,1) |      16 | f     | f    | 01 00 00 00 00 00 00 00
          2 | (0,2) |      16 | f     | f    | 02 00 00 00 00 00 00 00
(2 rows)

读取表块可确认该行已重复。通过仔细查看字段t_data,我们可以区分列c1的1和列c2的3。

如果阅读索引块,可以看到其内容未更改!如果我搜索WHERE c1 = 1行,则索引将我指向对应于旧行的记录(0,1)!

发生了什么?

实际上,我们刚刚揭示了一种相当特殊的机制,称为堆仅元组别名HOT。当更新一列时,没有索引指向该列,并且该记录可以插入同一块中,Postgres将仅在新旧记录之间建立一个指针。

这使postgres避免必须更新索引。所有这些暗示着:

避免读/​​写操作
减少索引碎片,从而减少其大小(很难重用旧索引位置)
如果查看表块,第一行的列t_ctid指向(0.3)。如果再次更新该行,则表的第一行将指向行(0.3),而行(0.3)将指向(0.4),形成所谓的链。抽真空将清理自由空间,但始终保持指向最后记录的第一行。

一行已更改,索引仍然没有更改:

UPDATE t3 SET c2 = 4 WHERE c1=1;
SELECT * FROM  bt_page_items(get_raw_page('t3_c1_idx',1));
 itemoffset | ctid  | itemlen | nulls | vars |          data           
------------+-------+---------+-------+------+-------------------------
          1 | (0,1) |      16 | f     | f    | 01 00 00 00 00 00 00 00
          2 | (0,2) |      16 | f     | f    | 02 00 00 00 00 00 00 00
(2 rows)

SELECT lp,t_data,t_ctid FROM  heap_page_items(get_raw_page('t3',0));
 lp |       t_data       | t_ctid
----+--------------------+--------
  1 | \x0100000001000000 | (0,3)
  2 | \x0200000002000000 | (0,2)
  3 | \x0100000003000000 | (0,4)
  4 | \x0100000004000000 | (0,4)
(4 rows)

真空清洁可用空间:

VACUUM t3;
SELECT lp,t_data,t_ctid FROM  heap_page_items(get_raw_page('t3',0));
 lp |       t_data       | t_ctid
----+--------------------+--------
  1 |                    |
  2 | \x0200000002000000 | (0,2)
  3 |                    |
  4 | \x0100000004000000 | (0,4)
(4 rows)

更新将重用第二个位置,并且索引保持不变。 查看t_ctid列的值以重建链:

UPDATE t3 SET c2 = 5 WHERE c1=1;
SELECT lp,t_data,t_ctid FROM  heap_page_items(get_raw_page('t3',0));
 lp |       t_data       | t_ctid
----+--------------------+--------
  1 |                    |
  2 | \x0200000002000000 | (0,2)
  3 | \x0100000005000000 | (0,3)
  4 | \x0100000004000000 | (0,3)
(4 rows)


SELECT * FROM  bt_page_items(get_raw_page('t3_c1_idx',1));
 itemoffset | ctid  | itemlen | nulls | vars |          data           
------------+-------+---------+-------+------+-------------------------
          1 | (0,1) |      16 | f     | f    | 01 00 00 00 00 00 00 00
          2 | (0,2) |      16 | f     | f    | 02 00 00 00 00 00 00 00
(2 rows)

嗯,第一行是空的,而postgres重用了第三位置?

实际上,信息不会出现在pageinspect中。 让我们直接使用pg_filedump读取该块:

注意:您必须首先请求CHECKPOINT,否则该块可能尚未写入磁盘。

pg_filedump  11/main/base/16606/8890510

*******************************************************************
* PostgreSQL File/Block Formatted Dump Utility - Version 10.1
*
* File: 11/main/base/16606/8890510
* Options used: None
*
* Dump created on: Sun Sep  2 13:09:53 2018
*******************************************************************

Block    0 ********************************************************
<Header> -----
 Block Offset: 0x00000000         Offsets: Lower      40 (0x0028)
 Block: Size 8192  Version    4            Upper    8096 (0x1fa0)
 LSN:  logid     52 recoff 0xc39ea148      Special  8192 (0x2000)
 Items:    4                      Free Space: 8056
 Checksum: 0x0000  Prune XID: 0x0000168b  Flags: 0x0001 (HAS_FREE_LINES)
 Length (including item array): 40

<Data> ------
 Item   1 -- Length:    0  Offset:    4 (0x0004)  Flags: REDIRECT
 Item   2 -- Length:   32  Offset: 8160 (0x1fe0)  Flags: NORMAL
 Item   3 -- Length:   32  Offset: 8096 (0x1fa0)  Flags: NORMAL
 Item   4 -- Length:   32  Offset: 8128 (0x1fc0)  Flags: NORMAL

第一行包含标志:REDIRECT。 这表明该行对应于HOT重定向。 记录在src / include / storage / itemid.h中:

/*
 * lp_flags has these possible states.  An UNUSED line pointer is available     
 * for immediate re-use, the other states are not.                              
 */                                                                             
#define LP_UNUSED       0       /* unused (should always have lp_len=0) */      
#define LP_NORMAL       1       /* used (should always have lp_len>0) */        
#define LP_REDIRECT     2       /* HOT redirect (should have lp_len=0) */       
#define LP_DEAD         3       /* dead, may or may not have storage */   

实际上,通过显示列lp_flags可以用pageinspect看到它:

SELECT lp,lp_flags,t_data,t_ctid FROM  heap_page_items(get_raw_page('t3',0));
 lp | lp_flags |       t_data       | t_ctid
----+----------+--------------------+--------
  1 |        2 |                    |
  2 |        1 | \x0200000002000000 | (0,2)
  3 |        1 | \x0100000005000000 | (0,3)
  4 |        1 | \x0100000004000000 | (0,3)

如果我们再次进行更新,则进行清理,然后执行CHECKPOINT将块写入磁盘:

SELECT lp,lp_flags,t_data,t_ctid FROM  heap_page_items(get_raw_page('t3',0));
 lp | lp_flags |       t_data       | t_ctid
----+----------+--------------------+--------
  1 |        2 |                    |
  2 |        1 | \x0200000002000000 | (0,2)
  3 |        0 |                    |
  4 |        0 |                    |
  5 |        1 | \x0100000006000000 | (0,5)
(5 rows)

CHECKPOINT;

pg_filedump  11/main/base/16606/8890510

*******************************************************************
* PostgreSQL File/Block Formatted Dump Utility - Version 10.1
*
* File: 11/main/base/16606/8890510
* Options used: None
*
* Dump created on: Sun Sep  2 13:16:12 2018
*******************************************************************

Block    0 ********************************************************
<Header> -----
 Block Offset: 0x00000000         Offsets: Lower      44 (0x002c)
 Block: Size 8192  Version    4            Upper    8128 (0x1fc0)
 LSN:  logid     52 recoff 0xc39ea308      Special  8192 (0x2000)
 Items:    5                      Free Space: 8084
 Checksum: 0x0000  Prune XID: 0x00000000  Flags: 0x0005 (HAS_FREE_LINES|ALL_VISIBLE)
 Length (including item array): 44

<Data> ------
 Item   1 -- Length:    0  Offset:    5 (0x0005)  Flags: REDIRECT
 Item   2 -- Length:   32  Offset: 8160 (0x1fe0)  Flags: NORMAL
 Item   3 -- Length:    0  Offset:    0 (0x0000)  Flags: UNUSED
 Item   4 -- Length:    0  Offset:    0 (0x0000)  Flags: UNUSED
 Item   5 -- Length:   32  Offset: 8128 (0x1fc0)  Flags: NORMAL


*** End of File Encountered. Last Block Read: 0 ***

Postgres保留了第一行(标志REDIRECT),并在位置5处写了新行。

但是,在某些情况下,postgres无法使用此机制:

当该块中没有更多空间时,他必须写另一个块。 我们可以推断出表的碎片在这里有益于HOT。
当更新的列上存在索引时。 在这种情况下,postgres必须更新索引。 Postgres可以通过在新值和前一个值之间进行二进制比较来检测是否发生了更改2。
在下一篇文章中,我们将看到一个示例,其中postgres无法使用HOT机制。 然后是postgres可以使用此机制的版本11的新功能。

  1. Disable recheck_on_update optimization to avoid crashes ^
  2. README.HOT ^

原文:https://blog.anayrat.info/en/2018/11/12/postgresql-and-heap-only-tuples-updates-part-1/

本文:http://jiagoushi.pro/node/882

讨论:请加入知识星球【首席架构师圈】或者微信圈子【首席架构师圈】

SEO Title
PostgreSQL and heap-only-tuples updates - part 1