数据库:类型和方法的概述。Yandex报告

这是Yandex.Tutorial的后端开发人员Tatyana Denisova的演讲的摘要。您将学习存在哪些数据库,记住哪些数据库功能很重要,在处理数据时如何考虑系统特征和扩展计划,以及需要解决哪些主题才能解决特定问题。以及在发生错误的情况下如何确定是否使用数据库是问题的根源(如果是,则从哪个方向进行挖掘)。







-我们到底要谈什么?与原始选择和联接无关-我想你们大多数人都已经了解它们。



我们将讨论数据库的实际使用,您将面临的困难以及作为后端开发人员需要了解的知识。会有很多信息,这里是内容。您无需直接彻底了解每个点的详细信息,但是您需要知道该点的存在。







并且您需要知道如何解决问题,以便在执行构建结构的任务时保存数据,您将知道选择哪种数据模型以及如何保存它。或者假设您有问题,看到数据库已关闭,运行缓慢,或者您有数据问题,即不一致。然后,您必须了解在哪里进行挖掘。也就是说,您需要知道存在哪些概念以及从哪个方面着手解决问题。







首先,我们将讨论数据。这到底是什么?我们周围有很多事实,有很多信息,但是在以任何方式收集它们之前,它们对我们毫无用处。我们收集,整理和存储它们。这种存储的结构称为数据,存储的结构称为数据库。但是,只要将这些数据收集到某个地方,它们对我们基本上也就没有用了。因此,数据库上方有一层-DBMS。这就是我们检索数据,存储和分析数据的原因。因此,我们将收到的数据转换为可以显示给用户的信息。用户接收并应用知识。







我们将讨论如何构造信息和事实,以何种数据形式,以何种模型存储它们。以及如何获取它们,以便许多用户可以同时访问数据并获得正确的结果,从而使我们对应用的最终认识是正确的。





首先,我们将讨论关系数据库。我认为关系模型是你们中许多人所熟悉的。它是表类型和表之间关系的模型。想象一下,我们有一个Messenger,可以在用户之间编写数据和消息。我们可以将它们全部写在一个如此大的宽表中,在那里,我们将有很多重复的数据-来自谁,谁,到谁,在哪个聊天室。我们可以将所有这些信息写到各种表中,即规范化我们的数据,并将其转换为第三种标准形式。



幻灯片上有注释和链接。我们现在不会深入每个概念。我将尽量不谈论您可能不熟悉的技术概念。但是我说的所有内容都可以在幻灯片注释中找到。包括规范化在内,还将有一个参考,如果您不熟悉此概念,则可以阅读。







一般而言,规范化是将数据分解为表,目的是使数据更加结构化。例如,现在有一个用户,信使聊天和消息表。这种结构可确保将来自我们认识的用户的消息以及来自我们认识的聊天的消息记录在此处。也就是说,我们确保数据完整性。我们确保可以始终收集整个图片这一事实。但是与此同时,例如,我们在消息表中仅存储ID,仅存储标识符。因此,我们减小了数据库的整体大小,使其更小。因此,我们可以更轻松地写入该数据库。我们不需要不断地写很多表。我们只需要与ID专家一起写在一张表中。







如果我们谈论规范化,它通常非常简化系统的外观,因为它非常图形化,并且可以立即使我们清楚地知道哪些表之间有什么关系。



我们减少了写数据时的错误数量,因为如果我们在Messenger中写一条消息而我们还没有这样的用户,那么我们将不得不创建一个。但是最终的图片,即一般数据,将保持完整。



我已经说过要减小数据库的大小。我们不必每次都在消息表中写入有关用户的所有数据。要查看个人资料,我们只需转到用户表。



我也警告过不一致的依赖关系。这些只是指向其他表的ID的链接,标识符是一个表中的唯一值。换句话说,它们称为主键,当我们具有这些主键的链接时,则另一个表中的链接本身称为外键。



这种结构还可以保护我们的数据免遭意外删除。我们无法删除用户,因为,例如,他有一条消息。这是一个很小但安全的网络。



看来我们已经完成了一个出色的结构,一切都清楚,一切都依赖,一切都是不可或缺的。您还需要处理什么?







让我们想象一下我们实际上已经投入运行了,我们有很多用户,因此有很多消息。他们经常互相交流。我们的消息表中发生了什么事?它在不断增长。为了搜索非数据,我们需要不停地浏览所有消息,在聊天中检查它们是否来自此用户,然后才显示它们。



自然,用户越多,消息越多,搜索查询所花费的时间就越长。我们需要一个解决方案,使我们能够快速搜索表中的消息。



在这种情况下,可以使用索引来加快搜索速度。与索引最简单的关联是书中的内容。如果需要在书中查找信息,则可以翻阅书,也可以转到目录。索引有点像一个目录。



电话簿也是一个很好的例子。您可以单击手机上的字母,然后您会立即被引用以该字母开头的姓氏所吸引。数据库索引的工作方式非常相似。让我们看一下带有消息的表以及如何获取这些数据。







请注意我们将如何处理数据。一般而言,与表中没有哪些行有关。索引基于您进行的查询而构建。



假设我们主要通过聊天发出请求,也就是说,我们发现此聊天中包含哪些消息。让我们在聊天列上完全建立索引。数据库索引是一个单独的结构。该表是独立于它的。也就是说,您可以随时删除和重建索引,该表将不会因此受到影响。



在这里您可以看到我们已经选择并在该列上添加了索引,并且我们拥有一个单独的结构,该结构已经略微减少了条目的数量,因为聊天室11中已经有几则消息。DBMS在这个小的聊天表中提供了快速搜索。怎么做的?自然,搜索不是简单的搜索。快速搜索算法有很多,我们将介绍大多数数据库默认使用的最受欢迎的算法之一。它是一棵平衡的树。







它是如何工作的?我们有一个聊天号码,这是一个整数,树是根据以下原理构建的:节点左边的值越少,节点右边的值越多。这种结构给我们带来了什么?如果您查看这棵树的摘要表,那么底部的所有值都是有序的。这是生产率提高的巨大优势。现在,我向您展示原因。







例如,我们正在寻找一个值。搜索一种含义非常容易。我们沿着树往下走,或者走到左边,走到右边-取决于此值是大还是小。







例如,如果我们要查找一个范围,那么请看结果有多简单和快速。我们到达了值,然后沿着叶子中的链接(已经沿着有序的值移动),就一直走到最后。







如果我们需要从和到之间定义一个范围,则可以完全相同。找到初始值,然后跟随叶子链接到最大值。我们只在树上走了一次。这非常方便,非常快​​。



同样,我们将搜索最大值和最小值。完全向左走,完全向右走。我们还将收到一份订购清单。也就是说,如果我们只需要以有序方式获取所有聊天记录,则可以到达第一个聊天记录,并通过叶子到达最右边的值,从而获得有序列表。正是基于这一原则,数据库非常快速地在索引表中搜索我们需要选择的那些行,并将其返回。



这里重要的是要知道什么?看起来很酷的结构-现在我们将根据这样的树为每一列构建并进行搜索。您为什么认为它不起作用?如果我们为每列建立一棵树,为什么不增加速度? (...)



我们的选择将真正加快速度。每当需要查看某个值时,我们都会进入索引,在其中找到指向自己的值的链接。索引通常完全包含对字符串的引用,而不是字符串本身。对于选择它完美地工作。但是,一旦我们要设置表数据,更新或删除数据,那么所有这些树都将必须重建。



实际上,删除不会重建,而只是将这棵树片段化,我们最终会得到许多空值。会有一棵巨大的树,树的值为空。但是随着更新和创建,这些树将每次都被重建。结果,我们将在所有这些结构上获得巨大的开销。而且,与其快速获取数据并加快数据库速度,不如降低查询速度。





还有什么重要的要知道的?使用数据库时,请查看,读取其中存在的索引,因为每个数据库都有其自己的实现,其自己的不同索引。有索引可以加快速度,有索引可以确保完整性。最简单的方法之一就是主键。这也是唯一的索引。关于数据库,请查看数据库的工作方式以及如何使用它,因为这是可以帮助您编写最佳查询的知识。



我们讨论了在插入数据时要记住哪些维护索引的开销。我忘了说,当您建立索引时,它必须具有高度的选择性。这是什么意思?



让我们看看这棵树。我们知道,如果将索引设置为true,则我们只会在左右两块木头上。而且,我们最多只能浏览50%的表格,这实际上不是很有效。最好准确索引那些具有最大不同值的列。这将加快我们的选择速度。



我说的是零散;删除数据时,需要牢记。如果我们经常删除索引中包含的数据,则可能需要对其进行碎片整理,并且还需要对其进行监视。同样重要的是要了解,您建立的索引不是基于拥有的列,而是基于如何使用该数据的。包含索引的查询需要非常仔细地编写。整洁是什么意思?当您编写查询时,将其发送到数据库,它不是直接发送到数据库,而是发送到称为查询调度程序的某个软件层。



调度程序具有一定的对应表,该表对应着多少操作成本和多少昂贵成本。在PostgreSQL示例中,有一些特殊的技术表可以收集有关您的数据和表的信息。计划者查看您有什么查询,什么数据存储在pg_stat表中。这正是存储有关以下内容的常规信息的表:您拥有多少数据,表中的哪些列,表中的索引。基于此,他查看了执行查询的计划,根据查询所需的计划计算了多少时间,并选择了最佳计划。





如果要查看查询的预计执行时间,可以使用“解释”操作。如果要实际执行,可以使用Explain分析。有什么不同?如我所说,调度程序最初是根据每个操作的估计时间来计算执行时间的。因此,实际时间可能会因机器和数据性质的不同而有所差异。因此,如果您希望实际执行,那么当然最好使用Explain分析。



您可以在此幻灯片上看到一个示例。它表明有时基于具有索引的列的查询可能不使用扫描索引,而仅使用整个表的完全扫描。如果我们的索引选择性很低,并且计划者认为对表进行全面扫描查询会更有利可图,则会发生这种情况。



假设我们有Messenger,并且想要在聊天列表中显示例如聊天名称或未读消息的数量。如果每次打开聊天时,我们都重新计算所有聊天的所有数据,那将是非常无利可图的。







有这样的事情-非规范化。这是使用的最热数据的复制或所需数据的预先计算,并将其保存到表中。







这就是用户和聊天之间的关系的样子。也就是说,除了用户ID和聊天ID外,我们还将在其中简要保存聊天名称,聊天日志和未读消息的数量。因此,每次我们都不需要加载所有表,进行选择并重新计算所有这些。







非正规化的好处是什么?我们加快了数据采样过程。也就是说,我们的选择会尽快完成,我们会尽快为用户提供答案。



困难在于,每次添加新数据时,我们都需要重新计算所有这些列,并且出错的可能性非常高。也就是说,如果我们的选择变得简单得多,并且我们不需要一直加入,那么我们的更新和创建将变得非常麻烦,因为我们需要将触发器挂在这里,重新计算并且不要忘记任何东西。



因此,仅应在真正需要时使用非规范化。正如我们现在遵循的整个逻辑一样,首先您需要对数据进行规范化,看看如何使用它,调整索引。如果您认为自己的查询效果不佳,请在反规范化之前先查看一下Explain。了解它们是如何实际执行的,调度程序是如何执行的。只有到那时,当您已经得出仍然需要非规范化的结论时,才可以这样做。但是有这种做法,在实际项目中,经常使用数据非规范化。



让我们走得更远。即使您很好地构造了数据,选择了数据模型,收集了数据,对所有数据进行了归一化,提出了索引,但在IT世界中仍然有很多地方会出错。



软件可能会发生故障,电源可能会中断,硬件或网络可能会发生故障。还有第二类问题:许多用户同时使用我们的数据库。他们可以同时更新相同的数据。我们必须能够解决所有这些问题。



让我们看一下这是什么的特定示例。







假设有两个用户想要预订会议室。用户1看到会议室此时是空闲的,并开始进行预订。他的窗口打开,他在想我该打电话给我的哪个同事。在思考的同时,用户2还看到会议室是空闲的,并为自己打开了一个编辑窗口。



结果,当用户1保存此数据时,他离开并认为一切正常,就预订了会议室。但是此时,用户2覆盖了他的数据,事实证明聊天室已分配给用户2。这称为数据冲突。我们必须能够向人们展示这些冲突并以某种方式解决它们。在这里,我们将进行重新录制。







怎么做?当用户1在思考时,我们可以简单地将会议室遮住一会儿。如果他保存了数据,那么我们将不允许用户2执行此操作。如果他释放了数据但没有保存,则用户2将能够预定会议。购买电影票时,您会看到类似的图片。您将有15分钟的时间来支付票款,否则将再次将其提供给也可以购买并付款的其他人。



这是另一个示例,它将向我们展示确保完全执行操作的重要性。假设我要从银行帐户1到帐户2进行转帐。目前,我有3个操作。我检查我是否有足够的资金,从我的第一个帐户中扣除资金,然后将其存入第二个帐户。很明显,如果在任何时候我都失败了,那么就会出问题了。



例如,如果在此阶段发生另一笔读取数据的交易,那么我帐户中的资金将不再足够,我将无法执行其他操作。例如,如果在第二时刻出现问题,那么我们从一个帐户中提取了资金,而没有在第二个帐户中提取了资金。事实证明,我的银行帐户中的所有帐户都会减少一定数量。这笔钱不能以任何方式退还。



为了解决此类问题,存在事务的概念-同时执行所有三个操作的原子,完整执行。



数据库是如何做到的?它将所有这些更改写入特定日志,并仅在提交事务后才应用它们。因此,我们保证所有这些操作将整体执行或完全不执行。



如果在这个时候的任何时候我们失败了,那么钱将不会从第一笔账户中扣除,因此,我们不会丢失它。



事务具有四个属性,对它们有四个要求。这些是原子性,一致性,隔离性和持久性-数据原子性,一致性,隔离性和持久性。这些属性是什么?



  • 原子性或原子性保证了您正在执行的操作将完全完成,而不会部分执行。因此,我们确保数据库中数据的整体一致性将在操作之前和之后。
  • Consistency — -, . (Integrity). - , , Integrity Error, : , . . — , .



    , , , , . . .



  • Isolation — , . . , .
  • Durability — , , , , .


让我们多谈谈绝缘。事务隔离是一个非常昂贵的属性,在它上面花费了大量资源,这就是为什么我们在数据库中有几个隔离级别的原因。让我们看看有什么问题,并在此基础上,我们已经讨论了如何解决它们。



问题主要有四类-更新丢失,脏读取,不可重复读取和幻像读取。让我们仔细看看。



当用户1覆盖了数据但他不知道该数据时,丢失的更新就像在聊天室的示例中一样。也就是说,我们没有阻止该用户更改的数据,因此,收到了他们的覆盖。







当用户看到另一个用户的临时更改时,就会发生脏读问题,然后可以将其回滚或简单地进行临时更改。







在这种情况下,用户1向数据库中写入了一些内容。当时的用户2正在那里计算内容,并在此数据上进行分析。用户1遇到错误,不一致,并正在回滚此数据。因此,用户2写下的分析将是伪造的,不正确的,因为他所计算出的数据不再存在。您还需要能够解决此问题。



不可重复读取是指我们的用户有1个长事务。他从数据库中获取数据,这时用户2更改了部分相同数据。







在这种情况下,事实证明用户1尚未阻止对其拥有的数据的更改。而且尽管他本人已经收到了数据快照,但是当反复要求他进行相同的选择时,他可以在这些行中获得不同的值。因此,它将产生冲突,即写入的数据不匹配。







如果用户2已添加或删除数据,可能会发生类似的问题。也就是说,用户1发出了一个请求,然后在第二次请求相同数据之后,他有或消失了行。在这种情况下,在交易的框架内,很难理解如何处理它们,如何处理它们。







为了解决这些问题,有四个隔离级别。第一和最低级别是“读未提交”。 PostgreSQL将其描述为“无锁”。当我们读取或写入数据时,我们不会阻止其他用户读取或写入该数据。事实证明,我们没有阻止任何更改。所有这四个问题仍然可能发生。但是,此隔离级别可以防止什么?它保证将执行所有进入数据库的事务。如果两个用户同时开始使用相同的数据执行查询,则这两个事务将顺序执行。



这有什么用?这种隔离级别在实践中很少使用,但是在例如大型分析查询并且您想阅读第二个查询并查看您的分析处于哪个阶段,哪些数据已经记录而哪些没有记录时很有用。然后第二个请求(用于调试,调试和检查)仅在此隔离级别上运行。他会看到您的第一个分析查询中的所有更改,这些更改最终可以回滚。还是不回滚,但是目前您可以看到系统的状态。







读取已提交,读取已提交的数据。在大多数关系数据库(包括PostgreSQL和Oracle)中,默认使用此隔离级别。它确保您永远不会读取脏数据。也就是说,另一笔交易从未看到过第一笔交易的中间阶段。优点是,它适用于小的简短查询。我们保证我们永远不会看到数据的某些部分,不完整的数据。例如,我们增加了整个部门的薪水,而只有一部分人获得加薪,而第二部分却以不带索引的薪水坐席,所以我们看不到。因为如果遇到这种情况,逻辑上我们的分析师将立即“走”。



此隔离级别不能防止什么?它不能防止您选择的数据可以更改的事实。对于小型查询,此隔离级别已足够,但是对于大型,较长的查询,复杂的分析,您当然可以使用更复杂的级别来锁定表。



可重复读取隔离级别可防止我们与您讨论的前三个问题。这是我们重新录制聊天室时丢失的更新;脏读-读取未提交的数据;以及这种不可重复的读取-读取其他事务更新的数据。







如何提供?通过锁定表,即锁定我们的选择。当我们将选择纳入交易时,它看起来像是数据的快照。目前,我们一直在使用此数据快照来查看其他用户的更改。缺点是我们阻止了数据,因此,可以处理数据的并行请求较少。这是非常重要的方面。通常,为什么会有这么多隔离级别?



级别越高,可以并行使用数据库的块越多,用户也越少。每个事务都会看到无法更改的特定数据快照。但是可能会出现新数据。因此,这种隔离级别并不能使我们摆脱适合选择的新数据的出现。



还有一个隔离级别-序列化。这通常称为订购。这是对表的完整数据锁定。它可以从幻像读取(即仅读取已添加或删除的数据)中进行保存,因为我们锁定了表,所以不允许对其进行写入。我们会全面满足我们的要求。







这对于精度和数据完整性至关重要的复杂,大型分析查询非常有用。事实证明,在某些时候我们读取了用户的数据,然后新的统计信息出现在另一个表中,结果证明是不同步的。



这是最高的隔离级别。它具有最多的锁数量和最小的查询并行化。



您需要了解什么交易?因为它们是在DBMS级别实现的,所以它们简化了我们的生活,我们只需要正确地进行查询,正确地形成查询,以便最终使数据保持一致。并准确阻止用户使用的数据。应该记住的是,在任何地方封锁所有东西都是不好的。根据您所拥有的系统以及谁可以读写多少数据,您将具有不同的隔离级别。如果您希望最快的系统出现一些错误,则可以选择最低的隔离级别。如果您有一个必须确保数据一致的银行系统,那么一切都已完成并且没有任何损失-那么,当然,您需要选择最大隔离级别。



在了解如何构建数据库以及可能发生的情况方面,我们已经取得了相当不错的进展。让我们走得更远。



存储一个数据库有多安全。当然不安全。如果她发生任何事情,我们将丢失所有数据。如果有备份,我们可以进行备份,但是会导致系统停机。如果我们的网络出现故障或该节点不可用,则系统还将在停机时间内空闲一段时间。



如何解决?有这样一个概念-复制。这是将数据库复制到其他节点和服务器。







这完全是一个完整的副本,即数据库的副本。我们如何使用这种机制?



首先,如果数据库发生问题,我们可以将请求重定向到数据库的另一个副本,这在逻辑上是合理的。这是主要的应用程序。我们还能怎么用呢?



假设用户离服务器很远。我们可以以覆盖最大数量的用户并尽可能快地向他们发出请求的方式分发服务器。这些服务器中的每一个将具有与其他服务器相同的副本,但是请求将更快地返回给用户。







另一个非常流行的用途是负载平衡。由于我们拥有相同的数据副本,因此我们无法从头脑中读取数据,也无法从一个数据库读取数据,而可以从不同的数据库读取数据。因此,我们卸载了服务器。







我们还具有OLTP查询和OLAP查询的概念。这是什么? OLTP-简短的事务查询。 OLAP是一项长期分析。这是当我们进行大量联接,进行大量选择时,我们将所有内容合并在一起,对于我们而言非常重要的一点是,此时所有数据均已锁定,因此没有任何更改且数据库已完成。



在这种情况下,您可以对数据库的单独副本进行分析。因此我们不会影响我们的用户,他们也可以在数据库中创建条目,然后这些条目才会出现在我们的副本中。







为了正确分发数据库副本,引入了主节点和从节点“主节点”和“从节点”的概念。从站通常被称为副本或从站。主节点-应用程序写入用户的节点。主服务器应用所有更改,保留更改日志,并将此日志发送到从属服务器。从站不接受用户的更改,而仅将更改应用于主服务器的日志。请注意,Master不会每次发送副本,而是发送更改。从站将这些更改滚存,并接收与主站相同的数据副本。



复制系统的一个非常重要的参数是请求是同步执行还是异步执行。什么是同步请求?这是主服务器向同步副本,同步从服务器发送请求,并等待从服务器说“是,我接受”并将确认返回给主服务器的时候。只有这样,主机才能将答案返回给用户。如果副本是异步的,则主服务器将请求发送到副本,但立即告诉用户“就是这样,我将其写下来”。让我们看看它是如何工作的。







有一个用户已向Master写入数据。主机将它们发送到两个副本,等待同步副本的响应,并立即向用户提供答案。记录了一个异步副本,然后对主数据库说:“是的,可以,数据已写入。”







就这样的主从结构而言,我们可以有一个或多个头。如果我们有一个主节点,则对其进行写入非常方便,但是您可以从同步副本中进行读取。为什么要完全同步?因为同步副本可确保数据以最大的准确性更新。



当查询应用于数据时,即从日志中进行的操作,也将花费时间。因此,如果您要接收的数据的100%准确性对您很重要,则应该阅读并在母版中进行选择。如果您不严格要求数据可能会稍有延迟到达,则可以从同步从设备读取。如果您绝对不批评数据的相关性,则可以读取数据(包括从异步副本中读取数据),从而从请求中卸载主副本和同步副本。



复制也可以有多个主服务器。不同的应用程序可以写入不同的磁头,然后这些Master相互解决冲突。







使用此类数据的一个非常简单的示例是各种脱机应用程序。例如,您的手机上有一个日历。您已断开与网络的连接并将事件记录在日历中。在这种情况下,您的本地存储(电话)就是Master。它本身已经存储了数据,当出现主控网络时,您的本地副本和服务器上的副本将解决冲突并将这些数据合并。



这是此类复制的非常简单的示例。它通常用于在线文档的协作编辑,或者很有可能失去网络。







无主复制也存在。这是什么?这是复制,客户端自己将数据发送到大多数副本,并从大多数副本中读取数据。在这里,您可以看到我们的中间副本是我们的读取和更新的交叉点。



也就是说,我们保证每次读取数据时,我们都会进入至少一个与数据最相关的副本。副本之间还建立了一种机制,用于与副本之间的更改和冲突的主要日志交换信息。在这种情况下,通常是胖客户端。如果他从一个副本接收到的数据比另一个副本包含的更新要多,则他只是将数据发送到另一个副本或解决了冲突。







有关复制的重要信息是什么?复制的主要点是系统容错能力,服务器的高可用性。无论数据库发生什么情况,系统都将可用,您的用户将能够写入数据,并且当还原与主数据库或另一个副本的连接时,所有数据也将还原。



复制对于卸载服务器以及将读取请求从主服务器重新分配到副本非常有帮助。我们可以扩展此读取,创建更多读取副本,并使我们的系统更快。您还可以复制需要大量锁定并可能影响系统可用性的复杂的长期分析查询。



以离线应用程序为例,我们研究了如何存储此类数据并解决冲突。在同步副本的情况下,可能存在复制滞后,即时间滞后。对于异步副本,它几乎总是存在。也就是说,当您从异步副本中读取数据时,必须了解它可能不相关。



根据层次结构,我忘记说了,当有一个主服务器正在等待同步副本的响应时,可以合理地假设,如果所有副本都是同步的,而一个副本突然变得不可用,那么我们的系统将无法保存请求。然后,主服务器将我们写入第一个同步从设备,接收响应,要求第二个从设备,不接收响应,最终必须回滚整个事务。



因此,通常在这样的系统中,一个副本是同步的,其余副本是异步的。同步副本可确保您的数据仍在其他地方。也就是说,除了可能发生某些事情的主服务器之外,我们保证至少还有一个包含完全相同的事务日志和相同数据的完整副本的节点。



另一方面,异步副本不保证数据完整性。如果我们只有异步副本,而主副本已断开连接,则它们可能会滞后,数据可能尚未到达那里。在这种情况下,通常,如果数据持久性对我们不重要,则它们将建立这样一个层次结构:要么拥有主数据库,一个同步副本,其余副本是异步的,要么拥有主数据库,所有副本都是异步的。



有一个“但是”:所有副本必须具有相同的配置。如果以PostgreSQL为例,它们必须具有相同版本的PostgreSQL,因为不同版本的数据库可以具有不同的日志格式。并且,如果副本来自其他版本,则它可能根本无法读取其他库所写的操作。



什么是副本?这是所有数据的完整副本。假设有太多数据,服务器无法处理。什么是第一个解决方案?



第一个解决方案是购买一台更昂贵的机器,该机器具有更多的内存,更大的CPU和更大的磁盘。只要您不面对铁价高昂的问题,这一决定在大多数情况下都是正确的。有一天,买一辆新车太贵了,或者根本就没有增长的地方。有大量数据在物理上根本无法容纳在一台计算机上。







在这种情况下,可以使用水平缩放。我们前面看到的每台计算机性能的提高是垂直缩放。机器数量的增加是水平缩放。







要按机器拆分数据,将使用分片(换句话说就是分区)。也就是说,按键,按ID,按日期将数据分为多个部分和块。我们将进一步讨论这一点,这是关键参数之一,但关键是要按照一定的标准划分数据并将其发送到不同的机器。因此,我们的机器效率可能会降低,但是系统仍可以正常运行并从其他机器接收数据。







为了大致了解什么数据,您需要某个对应的分片表,我们的副本和数据。



有时不使用特殊的数据存储,而客户端只是简单地依次浏览每个分片,然后检查是否有与其请求相匹配的数据。



有一个特殊的软件层,用于存储有关哪个分片位于哪个数据范围内的某些知识。因此,它恰好到达了必要数据所在的节点。







有一个胖客户。这是当我们不将客户自己缝到一个单独的层中时,而是在其中将有关如何拆分数据的数据缝到其中。







就是这种情况。顺便说一句,它是最常用的一种。好处是,尽管我们在配置本身中(在数据库中)进行了说明,但我们的应用程序,客户端(甚至您编写的代码)也不知道该表已被分片。我们只是告诉她-选择,并且数据库本身已经在其中进行了划分,并了解了从何处进行选择。在这里,您可以在代码本身中定义从何处读取数据。



有一些特殊服务可帮助组织和一般更新信息。很难保持其一致性和相关性。我们选择了一些东西,记录了新数据。或发生了某些变化,我们需要非常正确地路由我们的请求。提供特殊服务来协调请求。 Zookeper是其中之一。您可以大致了解它们的工作原理。一个非常有趣的结构。他们为开发人员节省了很多时间和精力。



重要的是,分区时要记住哪些方面?重要的是要了解我们将使用什么密钥来分割碎片。重新收集所有这些数据非常昂贵,因此重要的是不要误以为将来将如何使用这些数据。如果我们能够正确正确地进行分片,那么通过最常用的查询,我们将始终知道要复制到哪个副本。



例如,如果我们根据用户的ID将其所有数据存储在某些副本上,那么我们了解可以访问此副本并对其执行所有联接。但是通过ID保留它并不是最酷的主意。现在,我告诉你为什么。



如果我们错误地确定了分区的键,如果我们有一个非常复杂的查询,那么我们实际上必须转到不同的分片,合并所有数据,然后将其提供给应用程序。幸运的是,大多数DBMS都为我们做到了这一点。但是写得不好的查询会带来什么样的开销呢?还是在分片下,在错误的节点上被破坏?



关于ID。如果系统仅与新用户一起使用,并且我们的ID有所增加,则所有请求都将到达最后一个节点。



怎么了?其他三台正在运行的计算机将处于空闲状态。而且这辆车只会燃烧-所谓的热点。这是您潜在系统的瓶颈,甚至可能拒绝连接。



因此,当我们定义分片密钥时,了解这些节点之间的平衡非常重要。哈希是非常经常使用的,这或多或少是中立的,平衡的数据排列。但是,如果键上具有哈希函数,则将无法选择(例如)范围。这是合乎逻辑的,因为不能将范围分散到不同的碎片中。



按日期-相同。例如,如果我们分散分析并按日期制作碎片,那么,当然,十年前将完全不使用某个碎片。这对我们来说是无利可图的。重建数据和过度硬化总是非常昂贵的。



我将回答之前提出的问题。定义索引还是制作碎片更好?指标,当然。



看,分片是具有完整基础架构的独立机器。这个中间组件包含类似于索引的内容。通过参数可以快速搜索-位置,位置。这是比率。但是,如果有分片,最终的结果将是这样的:







有些应用程序,某种头脑清楚的去向。还有分片,每个分片上都配置了一个副本。如果没有太多数据,这将是非常大的开销。也就是说,仅当您真正达到垂直扩展限制时才需要使用分片,而购买更昂贵的机器与您的数据或收入无关。然后,您可以购买几种不同的廉价汽车,并在其上构建这样的架构。



我认为副本的用途很明确:由于碎片已损坏,因此它们是数据库的一部分,但是它们是唯一的。他们仅在这些地方被发现。我们还将它们分解为副本,这使我们的节点具有容错能力并确保不会出现问题。







最重要的是:分片用于确切的位置,而不仅仅是要将数据分解为分类,而是要在确实有很多数据的位置使用。



现在,让我们进一步了解数据模型,并了解如何存储数据。



我们之前研究过的关系数据库具有许多优势,因为首先,它们非常普遍并且每个人都可以理解。它们以视觉方式显示对象之间的关系并提供完整性。



但是有一个缺点:它们需要一个清晰的结构。有一个表,我们必须在其中推送所有数据。如果您查看我们一般收集的所有信息和事实,它们将大为不同。也就是说,我们可以使用产品数据,用户数据,消息等。这些数据确实需要一个清晰的结构,完整性。关系数据库非常适合他们。



但是,假设我们有一个操作日志或一个对象描述,其中每个对象都有不同的特征。当然,我们可以将其记录到关系数据库中的jason中,并很高兴看到它不断发展。



我们可以看看其他方案和其他存储系统。 NoSQL是一个非常浮华的缩写,甚至直接具有挑衅性-“ no SQL”。它是怎么发生的?



当人们面对关系数据库并非到处都成功的事实时,他们组织了一个需要井号的会议,于是他们提出了#NoSQL。它扎根了。后来他们开始说不是“没有SQL”,而是“不仅是SQL”。就是所有与关系数据库无关的东西:大量不同的数据库,它们不像关系数据库那样严格地结构化,示意图化和表格化。



非关系数据模型家族分为四种类型:键值数据库,面向文档的数据库,柱状数据库和图形数据库。让我们考虑这些要点,找出哪些数据更适合存储在哪些数据中以及它们的用途。







核心价值。这是最简单的。这是字典,这是比率。这是一个数据库,其中的数据是通过密钥存储的,与特定密钥下的内容无关。我们既拥有密钥本身,又拥有既简单又复杂得多的数据。这样的数据库的好处是,它像索引一样非常快速地搜索数据。这就是为什么键值经常用于缓存的原因。好处是我们的价值在不同的键中可以不同。



例如,我们可以使用密钥来存储用户会话。用户点击后,我们将其写成值。这是一个无模式的数据模型,没有特定的模式,值结构。因为它是一个非常简单的结构,所以它快速且易于扩展。我们已经有了密钥,我们可以很容易地将它们分片,进行哈希处理。它是可扩展性最高的数据库之一。



例如Redis,Memcached,Amazon DynamoDB,Riak,LevelDB。您可以看到键值存储的实现功能。







文档数据库在某些用途上与键值非常相似。但是他们的单位是一个文档。这是一个如此复杂的结构,通过它我们可以选择某些数据,进行批量操作:批量插入,批量更新。



每个文档通常可以将XML,JSON或BSON(二进制存储的JSON)存储在自身中。但是现在几乎总是JSON或BSON。这也像一个键-值对,您可以将其想象成一个表格,其中每一行都具有某些特征,并且我们可以使用这些键从中得到一些东西。



面向文档的数据库的优点:它们具有很高的数据可用性和灵活性。在任何文档中,在任何JSON中,您都可以写入任何数据集。它们非常常用-例如,当您需要创建目录时,以及目录中的每个产品可以具有不同的特征时。



或者,例如,用户个人资料。有人指出了他们最喜欢的电影,有人指出了他们最喜欢的食物。为了不将所有内容都粘贴在一个字段中(该字段尚不清楚),我们可以使用文档库的JSON编写所有内容。



便于存储数据的另一种模型是列式数据库。它们也称为列式,列数据库。







在我看来,这是一个非常有趣的结构,几乎在所有大型和复杂项目中都使用了这种结构。这样的数据库意味着我们在磁盘上的数据不是按行而是按列存储。用于非常快速地搜索大量数据。通常-对于分析,当您只需要从某些列中选择值时。







假设我们有一张大桌子。如果我们以行形式存储数据,那么将是以下内容:大量的行。要选择该表的三个参数,我们需要遍历整个表。当我们按列存储值时,然后按三个值进行选择时,我们只需要遍历三行(大致而言),因为我们的列是这样写的。通过这三行,我们立即获得所需值的序列号,并从其他列中获取它。



这样的数据库有什么优势?由于它们搜索少量数据,因此它们具有很高的查询处理速度和极大的数据灵活性,因为我们可以添加任意数量的列而无需更改结构。在这里,与在关系数据库中不同,我们不需要将数据强制放入某些框架中。



最受欢迎的栏目可能是Cassandra,HBase和ClickHouse。测试他们。反转头部中的行和列的比例非常有趣。这确实是高效且快速的访问大量数据的方式。







还有一系列图形数据库。它们还包含节点和边。边缘用于显示关系,就像在关系数据库中一样。但是图基可以在不同方向上无限增长。因此,它更加灵活。它具有很高的搜索速度,因为不需要在所有表之间进行选择和联接。我们的节点立即具有显示与所有不同对象的关系的边。



这些数据库是做什么用的?大多数情况下-仅显示关系。例如,在社交网络中,您可以回答谁在关注谁的问题。我们会立即链接到合适人员的所有关注者。仍然经常使用这些数据库来识别欺诈方案,因为这也与证明交易之间的关系有关。例如,您可以跟踪在另一个城市使用同一张银行卡的时间,或者何时其他人从同一IP地址输入另一用户的帐户的时间。



这些复杂的关系有助于解决异常情况,这些情况通常用于分析此类交互作用和关系。



非关系数据库不能代替关系数据库。他们只是不同。不同的数据格式和不同的工作逻辑,无可厚非。这只是处理其他数据的另一种方法。是的,非关系数据库被大量使用。您无需担心它们;相反,您需要尝试它们。



如果要进行缓存,那么当然要使用某种Redis,这是一种简单快捷的键值。如果您有大量要分析的日志,则可以将其放入ClickHouse或某些柱状库中,这将非常方便地进行搜索。或将其写入文档库,因为文档的含义可能有所不同。这对于选择也很有用。



根据要使用的数据选择数据模型。关系的或非关系的。描述数据。这样,您就可以找到将来可以扩展的最合适的存储。







今天,您已经学到了很多有关各种问题和数据存储方式的知识。我将再次重复我一开始所说的话:您不需要了解所有细节,也无需深入研究一件事。如果您有兴趣,当然可以。但是重要的是要知道它完全存在,那里有什么方法以及您怎么思考。如果需要容错功能,则可以制作一个副本。假设我写下了数据,但是没有看到它。然后,大概,我的话有点滞后。无需重新发明轮子-已经有许多现成的解决方案可用于不同的任务。扩大视野,如果出现错误或其他问题,您将通过错误的特征确切地了解发生故障的位置,并可以通过搜索引擎找到解决方案。感谢您的关注。



All Articles