- 快召唤伙伴们来围观吧
- 微博 QQ QQ空间 贴吧
- 文档嵌入链接
- 复制
- 微信扫一扫分享
- 已成功复制到剪贴板
VOLTDB最佳实践V1.1
一、文档说明
本文档主要用于描述VoltDB使用过程中积累的最佳实践。通过阅读本文档,用户可以在数据库规划、开发和运维时,对其诸多特性做出更加合理的设置或取舍。
二、Voltdb简介
VOLTDB是一款新数据库产品。为了成为高性能业务关键型应用程序的最佳解决方案而从头开始设计,VoltDB体系结构能够实现比当前数据库产品拥有更高的吞吐量。该体系结构允许随着数据量和事务需求的增长向集群添加节点,从而方便地完成在线扩容。
当前的商业数据库产品被设计为通用数据管理解决方案,可以根据特定的应用程序需求进行调整。然而,传统数据库的一刀切架构限制了其优化程度。虽然数据库的基本架构在30年里没有发生显著的变化,但是计算已经发生了很大的变化。业务应用程序和企业的需求和期望也是如此。
展开查看详情
1 . 目录 一 文档说明.........................................................................................................................2 二 Voltdb 简介..................................................................................................................... 2 三 VoltDB 运行原理............................................................................................................ 3 四 运行环境检测.................................................................................................................5 五 时钟同步.........................................................................................................................6 六 通信端口.........................................................................................................................6 七 分区表.............................................................................................................................6 八 复制表.............................................................................................................................8 九 存储过程确定性.............................................................................................................9 十 单分区存储过程...........................................................................................................11 十一 物化视图...................................................................................................................12 十二 数据库连接...............................................................................................................13 十三 查询超时时间...........................................................................................................14 十四 数据删除...................................................................................................................15 十五 异步调用存储过程.................................................................................................. 16 十六 SitePreHost................................................................................................................18 十七 K-factor......................................................................................................................18 十八 数据持久化...............................................................................................................20 十九 数据库集群扩展...................................................................................................... 21 二十 防止网络分区...........................................................................................................22 二十一 关于系统日志...................................................................................................... 25 二十二 数据复制(DR)..................................................................................................25 1 使用同步 Command log...........................................................................................28 2 防止数据复制冲突.................................................................................................. 28 3 监控数据复制...........................................................................................................30 二十三 使用资源超限设置.............................................................................................. 31 1 内存超限设置...........................................................................................................32 2 磁盘超限设置...........................................................................................................33 二十四 磁盘设置...............................................................................................................35 二十五 运行状态监控...................................................................................................... 36
2 .一 文档说明 本文档主要用于描述 VoltDB 使用过程中积累的最佳实践。通过阅读本文档, 用户可以在数据库规划、开发和运维时,对其诸多特性做出更加合理的设置或取 舍。 二 Voltdb 简介 VOLTDB 是一款新数据库产品。为了成为高性能业务关键型应用程序的最 佳解决方案而从头开始设计,VoltDB 体系结构能够实现比当前数据库产品拥有 更高的吞吐量。该体系结构允许随着数据量和事务需求的增长向集群添加节点, 从而方便地完成在线扩容。 当前的商业数据库产品被设计为通用数据管理解决方案,可以根据特定的应 用程序需求进行调整。然而,传统数据库的一刀切架构限制了其优化程度。虽然 数据库的基本架构在 30 年里没有发生显著的变化,但是计算已经发生了很大的 变化。业务应用程序和企业的需求和期望也是如此。 VOLTDB 旨在充分利用现代计算环境。 VoltDB 使用内存存储来最大限度地提高吞吐量,避免了昂贵的磁盘访 问。 通过序列化所有数据访问,避免了传统数据库的许多时间消耗功能,如 加锁、栅栏和维护事务日志,可以进一步提高性能。 可伸缩性、可靠性和高可用性是通过跨多个服务器和地理区域的集群和 复制实现的。 VoltDB 是一个完全符合 ACID 的关系型数据库,它使应用程序开发人员不
3 .必在自己的应用程序中开发代码来执行事务和管理回滚。通过使用 ANSI 标准 SQL 进行模式定义和数据访问,VoltDB 还减少了有经验的数据库设计人员的学 习曲线。 VoltDB 并不打算解决所有数据库问题。它的目标是业务计算的特定部分, 其特别关注快速数据。也就是说,应用程序必须快速处理大量数据流。这包括金 融应用、社交媒体应用和物联网这一新兴领域。这些应用程序的关键需求是可伸 缩性、可靠性、高可用性和出色的吞吐量。目前,VoltDB 被用于传统的高性能 应用程序,如资本市场数据馈送、金融贸易、电信记录流和基于传感器的分发系 统。它还被用于无线、在线游戏、欺诈检测、数字广告交换和微交易系统等新兴 应用。任何需要高数据库吞吐量、线性扩展和不妥协的数据精度的应用程序都将 立即受益于 VoltDB。然而,VoltDB 并没有针对所有类型的查询进行优化。例如, 对于收集和整理必须跨多个表查询的非常大的历史数据集,VoltDB 不是最佳选 择。这种活动通常出现在业务智能和数据仓库解决方案中,其他数据库产品更适 合这些解决方案。 为了帮助那些既需要出色的事务性能又需要分析报告的业务,VoltDB 包含 数据集成功能,这样历史数据就可以导出到分析数据库中,以便进行更大规模的 数据挖掘。 三 VoltDB 运行原理 VoltDB 不像传统的数据库产品。每个 VoltDB 数据库都针对特定的应用程 序进行了优化,方法是对数据库表和存储过程进行分区,以便跨一个或多个主机 上的多个站点或分区访问这些表,从而创建分布式数据库。因为数据和工作都是
4 .分区的,所以可以并行运行多个查询。 同时,由于每个站点都是独立运行的,所以每个事务都可以运行到完成,而 不需要锁定单独的记录,传统数据库在这些方面消耗了大量处理时间。通过使用 序列化处理,VoltDB 确保了事务一致性,而不需要锁、栅栏和事务日志的开销, 而分区允许数据库一次处理多个请求。一般的经验法则是,集群中处理器越多(因 此分区越多),每秒完成的事务就越多,这为扩展应用程序的容量和性能提供了 一个简单的、几乎是线性的路径。
5 .四 运行环境检测 为保证 VoltDB 在最佳状态下运行,需要对操作系统进行特定的配置,并按 照例如 JDK 和 Python 等运行环境。VoltDB 提供了一键检测功能,您可以方便 的检查到在运行 VoltDB 之前还有那些选项需要配置。 您只需在 shell 窗口执行命令:voltdb check 即可查看。 请留意状态为 WARN 的行是您需要配置的选项。在文档“AdminGuide” 中您可以找到相应的配置方法。
6 .五 时钟同步 毫无疑问,使用同步时钟对任何集群系统都是非常重要的。为了协调集群节 点之间的活动,VoltDB 依赖于同步的系统时钟。VoltDB 中的许多函数(比如集 群启动、节点重新连接和模式更新等)对集群中节点之间的时间差异很敏感。因 此,在集群中保持时钟同步是很重要的。 集群节点间的时间差要保持在 200 毫秒以内,保持在 10 毫秒以内是最佳状 态,并且集群启动后您不能调整系统时间到更早的时候。 六 通信端口 VoltDB 使用许多网络端口来实现功能,比如内部通信、客户机连接、重新 连接、数据库复制等等。要使这些特性正确地执行,端口必须是打开的和可用的。 检查以下端口列表,以确保它们是开放的和可用的。 端口名称 默认端口 Client Port 21212 Admin Port 21211 Web Interface Port (httpd) 8080 Internal Server Port 3021 Replication Port 5555 Zookeeper port 7181 七 分区表 使用分区表可以充分利用 VoltDB 多分区并行处理的优势。分区表的目的是
7 .确保表上最频繁的事务与访问的数据在同一个分区中执行。我们称之为单分区事 务。因此,存储过程必须通过分区列值惟一地标识行,这对于修改数据的查询(如 INSERT、UPDATE 和 DELETE 语句)尤其重要。 定义分区表的关键是选择分区键,以下是选择分区键时应该遵循的原则。 每个表只能有一个分区列。如果需要多个列作为分区键(例如 first 和 last name),推荐添加一个附加列(fullname),该列组合了这两列的值,并 使用这个新列对表进行分区。 如果表有主键,则分区列必须包含在主键中。 任 何 整 数 或 字 符 串 列 都 可 以 标 识 分 区 。 VoltDB 可 以 在 任 何 整 数 (TINYINT、SMALLINT、integer 或 BIGINT)或字符串(VARCHAR)数据 类型的列上定义分区键。 分区列值不能为空。分区列不需要有惟一的值,但是必须在分区列的模 式中指定 not NULL。 选择值分布合理的列,以便对数据行进行均匀分区。例如在用户表中将 “性别”列作为分区键是一种不推荐的做法。因为分区键值有限,这会 导致严重的数据分布倾斜,甚至某些分区上没有数据分布。 选择一个最大化使用单分区存储过程的列。如果一个存储过程使用 A 列 查找数据,两个存储过程使用 B 列查找数据,假设前端对三个存储过程 的访问量相当,那么分区键应该选择 B 列,因为 B 列的繁忙程度两倍于 A 列。 写事务权重大于读权重。当数据表上存在多个列的访问繁忙程度相当时, 考虑访问的类型,使用写事务权重大于读权重原则。
8 . 如果在同一列属性上对多个表进行分区,VoltDB 会将它们分布到同一 个分区上。 如下是定义一张分区表的 DDL CREATE TABLE Reservation ( ReserveID INTEGER NOT NULL, FlightID INTEGER NOT NULL, CustomerID INTEGER NOT NULL, Seat VARCHAR(5) DEFAULT NULL, Confirmed TINYINT DEFAULT '0' ); PARTITION TABLE Reservation ON COLUMN FlightID; 八 复制表 VoltDB 支持两种表,分别是分区表和复制表。较小的只读表非常适合定义 为复制表。复制表在每个节点上都保存一份完整的副本,这允许存储过程在保持 单分区事务的同时将该表和另一个较大的表之间创建连接。还请注意,如果表需 要通过分区列之外的列频繁访问,则应该定义该表为复制表,因为不能保证特定 分区包含查询所需的数据。
9 . 如下是定义一张复制表的 DDL CREATE TABLE Reservation ( ReserveID INTEGER NOT NULL, FlightID INTEGER NOT NULL, CustomerID INTEGER NOT NULL, Seat VARCHAR(5) DEFAULT NULL, Confirmed TINYINT DEFAULT '0' ); 九 存储过程确定性 对于 VoltDB 而言,存储过程可能会在多个分区的多个数据副本上执行,这 些数据副本可能是复制表的多副本或者是使用 K-factor 产生的多副本。为了确 保数据的一致性和持久性,VoltDB 的存储过程必须是确定的。也就是说,给定 特定的输入值,存储过程在不同副本中的执行结果是一致的和可预测的,否则多
10 .副本的数据一致性就会被破坏。基于存储过程确定性的要求,如下几点是设计存 储过程时需要考虑的。 查询时指定排序字段 执行未排序的查询可能导致不确定的结果。除非使用树索引以特定的顺序扫 描记录,或者在查询本身中指定 order by 子句,否则 VoltDB 不能保证结果的 一致顺序。比如 SELECT TOP 10 Emp_ID FROM Employees 这种没有索引或 ORDER BY 子句的查询在不同分区上可能会返回不同的结果集。即使是像 SELECT * from Employees 这样的简单查询也可能返回不同顺序的结果。 即使非确定性查询是只读的,它的结果也可以用作存储过程中插入、更新或 删除语句的输入,从而将错误结果传递给写操作,最终导致数据不一致。如果发 生这种情况,VoltDB 会检测到不匹配,将其报告为潜在的数据损坏,并关闭集 群以保护数据库内容。 避免使用不确定值 存储过程中应避免引入任意外部数据或过程。外部数据包括磁盘文件和网络 I/O,以及许多常见的特定于系统方法,比如获取日期和时间。由于不能保证存 储过程在多个节点上同时获取这些数据,所以这些数据都是不确定值。 但是,这个限制并不意味着不能在 VoltDB 存储过程中使用外部数据。它只 是意味着您必须在调用存储过程之前生成这些数据并将其作为输入参数传递进 来。例如,如果需要从文件中加载一组记录,可以打开应用程序中的文件,并将 每一行数据传递给一个存储过程,该存储过程将数据加载到 VoltDB 数据库中。 当从可能影响延迟的源(例如文件或网络资源)检索任意数据时,这是最好的方法。 对于另外两种最常见的情况,使用时间戳和随机值,推荐使用 VoltDB 提供
11 .了以下方法来代替 java 函数,这些方法使用当前事务 ID 来生成确定的时间戳和 随机数: gettransactiontime()返回一个时间戳,可以用来代替 Java 日期或时间类。 getseededrandomnumbergenerator()返回一个伪随机数,可以用来替代 Java Util.Random 类。 十 单分区存储过程 我们希望最常用的存储过程是单分区的,单分区事务与分区表是相辅相成的, 两者结合起来意味着存储过程在一个分区中执行,该分区也有它需要的数据。单 分区存储过程不需要跨多个分区和服务器进行处理,从而浪费搜索整个表数据的 时间。实际运用的过程中,为了确保单分区效率,存储过程用于标识所需数据的 参数必须与表的分区键相同。 我们通过一个机票预订应用来说明单分区事务使用方法。首先在应用程序中, 我们定义一个分区表-“机票预订表”,该表是在 FLIGHTID(航班 ID)字段上 分区的。其次前端应用调用存储过程 MakeReservation 来完成机票预订业务, 存储过程有两个参数 flight_id 和 customer_id。其中的关键是,参数 flight_id 是与分区键 FLIGHTID 对应的。具备这些条件后,单分区事务即可以最佳状态运 行。 下图显示了存储过程将如何在具有请求行的分区中自动执行,同时多个请求 可以在多个分区上并行处理。
12 .十一 物化视图 VoltDB 中的视图均为物化视图。物化视图非常适合流数据处理,如果您需 要执行一些多参数的 GROUP BY 查询,并且这些查询需要高频的调用,那么推 荐您定义视图来替代 sql 查询。 通过物化视图,您可以将特定的查询逻辑固化到其定义中。视图作为一个特 殊的表存储在数据库中,在每次修改与之相关联的普通数据表时,物化视图中的 数据都会随着更新。这时您想要的结果只在数据发生变动时被计算一次,之后数 据就是现成的了。通过将直接 sql 查询替换成查询视图,您可能得到近百倍的性 能提升。
13 .十二 数据库连接 VoltDB 支持多语言客户端编程,如果您使用的是 JAVA 客户端,那么有两 种连接数据库方式--通过 VoltDB Java Client 接口或标准 JDBC 方式连接。 VoltDB Java Client 是一个线程安全的类库,它提供了对 VoltDB 数据库和 函数的运行时访问接口。如果您在全新的应用开发环境中使用 VoltDB,建议采 用这种方式连接数据库,这可以使您在性能上获得优势。 在集群环境中,您只需创建到数据库集群中任何一个节点的连接,您客户端 请求将被正确的路由。但我们推荐您创建到集群中所有节点的数据库连接。例如, 下面的 Java 代码创建客户机对象,然后连接到集群的所有三个节点。 try { client = ClientFactory.createClient(); client.createConnection("server1.xyz.net"); client.createConnection("server2.xyz.net"); client.createConnection("server3.xyz.net"); } catch ( java.io.IOException e) { e.printStackTrace(); System.exit(-1); } 创建多个节点的连接有如下几点好处: 多个连接将存储过程请求分布在集群中,避免了所有请求都通过一台主机排 队的瓶颈。这在使用异步过程调用或多个客户机时尤其重要。 VoltDB Java Client 中具有数据分布计算逻辑,因此,在全节点连接的情
14 .况下,客户端知道根据分区将每个请求发送到哪个服务器,从而消除了不必要的 网络跳转。 最后,如果服务器由于任何原因出现故障,在使用 K-factor 时,客户机可 以继续通过其他连接提交请求。这避免了客户机和数据库集群之间的单点故障。 另外,使用“拓扑感知”方式连接数据库也是推荐的方式,这种方式只需要 连接一个集群节点即可。开启“拓扑感知”选项后,每当拓扑发生变化时,它都 会自动重新连接。也就是说,如果服务器出现故障后重新加入集群,或者集群中 添加了新节点,客户机将自动创建到新可用服务器的连接。 org.voltdb.client.Client client = null; ClientConfig config = new ClientConfig("",""); config.setTopologyChangeAware(true); try { client = ClientFactory.createClient(config); client.createConnection("server1.xyz.net"); ... 当 setTopologyChangeAware()设置为 true 时,客户机库将自动连接到集 群中的所有服务器,并在集群拓扑发生更改时调整其连接。 十三 查询超时时间 正常情况下,SQL 查询执行得非常快。但是,构造一个执行时间超出预期 的查询是可能的。这种情况通常发生在查询过于复杂或访问非常大的表而没有适 当的过滤器或索引的情况下。
15 . 按照经验如果查询耗时太长会将后面的操作阻塞,同时可能会导致大量内存 被持续分配,不会立即回收。这会导致内存耗尽的情况发生。所以您应该设置查 询超时时间,建议设置不超过 30 秒。 要设置集群范围的查询限制,可以使用配置文件中的<systemsettings>和 <query timeout="{limit}">标记。要在 sqlcmd 实用程序中设置交互会话的限 制,可以在调用 sqlcmd 时使用--query-timeout 参数。要在调用特定存储过程 时指定限制,可以使用 callProcedure rewithtimeout 方法代替 callProcedure 方法。 为保证数据一致性,超时时间设置仅对只读查询生效。 十四 数据删除 关于数据删除,有两个关键点是需要说明。 1. 如果您需要清空表中的所有数据,并保留数据表对象。强烈推荐使用 TRUNCATE TABLE 语句,而不是使用 DELETE 从指定的表中删除所有 记录。TRUNCATE TABLE 与没有条件子句的 DELETE FROM {TABLE -name}语句功能相同。TRUNCATE 语句经过特殊优化以提高性能,并 在同等条件下减少内存使用。 2. 在实际使用场景中,Voltdb 常被用来存储热数据或温数据。这意味着随 着时间的推移,您需要不断的移除冷数据。为此,Voltdb 提供了 TTL (time to live)功能。通过在 CREATE TABLE 中使用 USING TTL 子句, 可以为表记录指定的“生存时间”值和 TTL 列,TTL 列和当前时间(以 GMT 微秒为单位)之间的差异即为每个记录的生存期。一旦行记录超过
16 . 生存期,它将被自动删除。 在最简单的情况下,可以定义一个时间戳字段,并且指定 DEFAULT NOW,这样记录将在插入之后的指定时间内过期,并被删除。举例如 下: CREATE TABLE current_alerts ( id BIGINT NOT NULL, message VARCHAR(128), created TIMESTAMP DEFAULT NOW NOT NULL, ) USING TTL 5 MINUTES ON COLUMN created; 十五 异步调用存储过程 VoltDB 支持通过同步和异步两种方式调用存储过程。同步调用存储过程简 化了程序逻辑,因为客户机应用程序总是在等到前一过程返回后,才去处理后续 逻辑。但是,对于希望最大化吞吐量的高性能应用程序,最好异步地对存储过程 进行调用。异步调用在过程调用排队后,立即将控制权返回给调用应用程序,这 样应用程序可以继续执行,无需等待。 要异步调用存储过程,请为 callProcedure()方法传入回调函数,这样系统 会自动以异步方式调用,在存储过程完成(或发生错误)时系统会通知该回调函数。 例如,要异步调用 NewCustomer()存储过程,对 callProcedure()的调用可能如 下所示: client.callProcedure(new MyCallback(), "NewCustomer",
17 . firstname, lastname, custID}; 回调函数是在服务器上的存储过程完成后调用的。下面是一个回调函数的实 现: static class MyCallback implements ProcedureCallback { @Override public void clientCallback(ClientResponse clientResponse) { if (clientResponse.getStatus() != ClientResponse.SUCCESS) { System.err.println(clientResponse.getStatusString()); } else { myEvaluateResultsProc(clientResponse.getResults()); } } } getStatus()和 getResults()方法让回调过程决定存储过程是否成功,并处理 存储过程的返回结果。 VoltDB Java 客户机是单线程的,即使是异步调用,也只能同时处理一个回 调函数。因此,保证回调中的处理逻辑尽量简单是一个很好的实践,即尽快将控 制权返回给主线程。如果回调中需要更复杂的处理,建议在回调函数中创建单独 的线程池,并在单独的线程上处理。
18 .十六 SitePreHost 在 VoltDB 中获得好的性能,需要充分利用分区机制。由于数据库在多个分 区上并发处理请求,分区数量将直接影响数据库表现。Siteprehost 用于指定在 集群中的每个服务器上创建的分区数,sitesperhost 值乘以服务器数量,得到集 群中分区的总数。默认情况下 sitesperhost=8。 每个节点所需的分区数量与每个系统拥有的处理器内核数量有关,最佳数量 大约是数量服务器 cpu 数量的 3/4。例如,如果使用双四核处理器(换句话说, 每个节点有 8 个核),那么每个节点可以设置 6 个分区。 对于超线程 cpu,操作系统报告的内核数量是物理内核数量的两倍。换句话 说,一个双四核系统将报告 16 个虚拟 cpu。但超线程内核效率实际不如非超线 程系统,因此在这种情况下,分区数量需要略小于 cpu 数量的 3/4,在 10 到 12 个之间。 同时,建议单服务器的分区数量不要超过 24 个。因为根据实际测试当分区 数大于这个数值时,数据库性能随分区数量线性增长的规律将被破坏。注意,这 里所说的 24 个分区是指单服务器拥有的,如果通过添加服务器,来增加整个集 群分区数则不受该限制。因此,您的服务器可以支持 24 分区即可。成本固定的 情况下,您可以转而考虑购买更多相同配置的服务器,采用横向扩展集群的方式 来提供系统吞吐量。 十七 K-factor K-factor 是指将数据库分区数据复制到集群其他节点上,这样数据库就可以 在不中断服务的情况下承受部分集群节点的损失,实现集群高可用性。例如,
19 .K-factor 的值为零意味着没有数据备份,任何服务器宕机将导致数据和数据库操 作的丢失。如果每个分区的数据都有两份(K-factor 值为 1),那么集群可以在不 中断服务的情况下承受至少一个节点的损失。因此在生产环境中建议开启 K-factor,即 K-factor 的值大于 0。 值 得 一 提 的 是 , K-factor 必 须 是 整 数 , 取 值 范 围 是 0 ≤ K-factor<nodes(nodes=集群节点数)。一方面 K-factor 值越大,可容忍的故 障节点越多。另一方面 K-factor 值变大时,会导致系统数据容量的减小。下面 这一系列图可以说明这点,同时在图中您可以看到启用集群高可用后数据是怎么 分布的。
20 . 从图中可以看出,K-factor=0 时,停掉任意一个节点,都无法保证数据不 丢失。K-factor=1 时,可以停掉任意一个节点,并保证数据不丢失,集群数据 容量变成 6 份。K-factor=2 时,可以停掉任意两个节点,并保证数据不丢失, 但集群数量容量只有 4 份了。 因此,K-factor>0 是强烈推荐的,这样您即获得了集群高可用性。不过根 据您的业务要求、集群规模以及硬件配置,K-factor 的精确取值仍是值得权衡的。 十八 集群节点数量 集群的所有节点都应该位于同一个网络交换机上,这有利于 VoltDB 节点间 交换数据。目前常见的交换机拥有 32 个接口,因此一个 2-32 个节点的 VoltDB 集群,通过同一交换机连接,可以提供最好的性能。 十九 数据持久化 通过在内存中执行事务,VoltDB 将自己从传统数据库产品的管理开销和 I/ O 成本中解放出来。然而,当意外发生时,保护数据库的内容,防止丢失或损坏 依旧是非常重要的。一般情况下,建议您在生产环境中开启数据持久化选项。 VoltDB 通过命令日志(Command logging)和快照(Snapshots)两种 机制实现数据持久化。命令日志记录为您的 VoltDB 数据库的持久性和可用性提 供了一个更完整的解决方案。命令日志记录了执行的每个事务(即存储过程),如 果服务器由于任何原因出现故障,数据库可以恢复最后一个快照并“重播”后续 的命令日志,从而重新完整地建立数据库内容。 命令日志的关键是它记录事务的调用,而不是结果。一个存储过程可以包含
21 .许多单独的 SQL 语句,每个 SQL 语句可以修改数百或数千个表行。通过只记录 调用 sql,命令日志被保持在最低限度,从而限制了磁盘 I/O 对性能的影响。但 是,任何额外的处理都可能影响总体性能,特别是涉及磁盘 I/O 时。您需要结合 服务器硬件情况和业务要求进行权衡。 当启用 Command logging 记录时,VoltDB 将保存每个事务(即存储过程) 调用的日志。首先,调用的日志保存在内存中。然后,在一段设置的时间间隔内, 将日志物理地写到磁盘上。另外每隔更长的时间,服务器启动生成一个 Snapshots。快照完成后,命令日志进程就删除快照生成之前的命令日志。 由于持久化涉及到频繁的磁盘交互,推荐采用如下规则平衡命令日志与性能 的矛盾: 在较慢的存储设备上使用异步日志记录。 将命令日志写入专用设备。不要将日志和快照写入同一设备。 同步记录时使用低频率(1-2 毫秒)。 在执行异步日志记录时,使用中等(100 毫秒或更高)频率。 二十 数据库集群扩展 VoltDB 的一个重要特性是易于扩展。您可以通过向集群添加服务器来提高
22 .容量和处理能力(即分区总数),从而实现几乎线性的可伸缩性。使用 VoltDB 集 群还可以提高数据库的可用性——防止服务器故障或网络故障。 当你准备扩展集群时,只需初始化并启动 VoltDB 新节点上的数据库进程即 可。使用 VoltDB init 命令初始化,VoltDB start 命令启动,并在 add 参数中 指定一个或多个现有的集群节点的主机名。例如,如果要将节点 ServerX 添加 到 ServerA 已经是成员的集群中,可以在 ServerX 上执行以下命令: $ voltdb init --config=deployment.xml $ voltdb start --add --host=ServerA 但是在 K-factor 环境中,您必须添加足够数量的节点来创建一个完整的 K-factor 单元,也就是要一次添加 K+1 个节点。例如,如果集群的 K-factor 值 为 2,则必须一次添加 3 个节点来扩展集群。如果集群不是 K-factor 的(换句话 说,它的 K-factor=0),您可以一次添加一个节点。 二十一 防止网络分区 VoltDB 通过创建一个紧密绑定的服务器网络来实现可伸缩性,该网络同时 分发和处理数据。在配置和管理自己的服务器硬件时,可以确保集群驻留在单个 网络交换机上,确保节点之间的最佳网络连接,并减少网络故障干扰通信的可能 性。然而,在某些情况下您可能无法保证网络状态。例如,如果您“在云中”运 行 VoltDB,您可能无法控制甚至不知道集群的物理配置。 危险在于,网络故障(例如交换机之间的故障)可能会中断集群中节点之间的 通信。此时集群被分为了两个(或者多个)部分,部分服务器节点可以继续运行, 甚至可以与故障一侧的其他节点通信,但不能“看到”集群的其余部分。事实上,
23 .集群的两部分都认为另一半已经失败。这种情况称为网络分区(或集群脑裂)。 例如,如果您有一个 3 个节点集群,每个节点有 2 个站点,并且 K-safety 值为 2,那么每个节点都是数据库的一个独立的、自我维护的副本,如图 10.2 所示,“Network Partition”。如果网络分区将节点 a 和节点 B 与节点 C 分开, 那么每个段都有足够的分区来维持数据库。节点 A 和 B 认为节点 C 已经失败; 节点 C 认为节点 A 和节点 B 已经失败。 脑裂出现后,您永远不会希望数据库的两个独立副本继续运行,并认为它们 是惟一可行的副本而接受请求,这会导致集群的两个部分数据不一致。如果集群 物理上位于单个网络交换机上,则可以降低网络分区的威胁。但是,如果集群位 于多个开关上,则风险会显著增加,必须考虑到这一点。 VoltDB 提供了一种机制来确保网络分区不会意外地创建数据库的两个独立 副本。该特性称为网络分区保护。由于网络分区的后果非常严重,强烈建议使用 网络分区,默认情况下,VoltDB 已开启了网络分区保护。 当启用网络故障保护,并检测到故障(由于网络故障或一台或多台服务器故 障)时,集群中任何仍可运行的部分节点都将执行以下步骤:
24 . 1. 确定缺少哪些节点。 2. 确定丢失的节点是否也是一个可行的自持续集群。如果是的话,执行第 3 点 3.确定哪个分裂后的网络分区是更大的(即包含更多节点)。 •如果当前分区较大,假设较小分区的节点已经失败,则当前分区继续运行。 •如果另外分区更大,则关闭自己,以避免创建数据库的两个单独副本。 例如,在图 10.2 所示的“网络分区”中,如果一个网络分区将节点 A 和 B 与 C 分开,较大的分区(节点 A 和 B)将继续运行,而节点 C 将关闭 大部分情况下,单点网络故障较为常见,这时集群将分裂为两个分区。建议 尽可能使用奇数个节点配置 K-safe 集群,这可以保证不会出现分区节点数对等 的情况发生,分区保护机制可以按照预期工作。当然 Voltdb 有机制可以保证在 偶数集群中发生节点对等分裂时做出选择。但如果条件允许的话,采用奇数集群 的方案仍然是最佳选择。
25 .二十二 关于系统日志 VoltDB 使用 Log4J (Apache Software Foundation 提供的开源日志服务) 提供对数据库事件信息的访问。默认情况下,当使用 VoltDB shell 命令时,控 制台的显示仅限于警告、错误和关于当前进程状态的消息。更完整的 log 被写入 到数据库根的 log 子目录中(voltdbroot/log/)。 使用如下命令可以帮助您快速收集 voltdb 的日志,其中--prefix 是您自定 义的日志文件前缀,voltdbroot 是 voltdb 的运行时根路径,请根据您的实际环 境进行指定。 voltdb collect --prefix=mylogs voltdbroot 二十三 数据复制(DR) VoltDB 的数据复制功能为数据库容灾和高可用提供支撑。数据库复制是在 多个集群之间进行的,涉及创建和维护整个数据库的完整副本。数据复制不仅仅 是一个瞬间的快照,而是实时的、持续更新的。 如果您有以下需求,那么数据复制是我们推荐的解决方案。 一、 防范业务数据受灾难性事件的影响,例如停电或自然灾害,这些事件 可能导致整个集群崩溃。但使用数据复制,您可以在不同的地理位置 搭建多个集群,当某些集群无法操作时您可以将客户端请求重定向到 其他可用集群。 二、 实现数据热备。通过数据复制实现的高可用数据库架构包含多个集群, 在不影响主集群服务性能的情况下,能非常方便地实现数据热备份。 三、 数据库读写分离,数据库请求当中,一般读操作的请求次数远大于写
26 . 操作,高可用数据库可以通过将写操作放在主数据库集群上进行,将 读操作分担到若干从集群上,来提升读操作吞吐量,进而提升读写效 率。 四、 多数据中心,实现异地多活架构。采用数据复制可以是数据更靠近客 户,减少网络延时。使用数据复制功能,您的数据库甚至可以分布在 大陆。 VoltDB 支持两种形式的数据库复制: 被动复制(单向,被动 DR) 跨数据中心复制(双向,XDCR) 被动复制(DR)将内容从一个数据库(称为主数据库)复制到另一个数据库(称 为副本)。在被动复制中,复制按一个方向进行:主数据库复制到副本数据库。客 户机可以连接到主数据库并执行所有正常的数据库操作,包括 INSERT、UPDATE 和 DELETE 语句。如图 1 所示,“被动数据库复制”更改将从主服务器复制到副 本。为了确保两个数据库之间的一致性,副本作为只读数据库启动,只有从主数 据库复制的事务才能修改数据库内容。 图 1.“被动数据库复制”
27 . 跨数据中心复制(XDCR)或活动复制在两个方向复制更改。XDCR 可以在多 个集群上设置(而不仅仅是两个集群)。然后,客户机应用程序可以对参与其中的 任何集群执行读/写操作,数据库自动将一个数据库中的更改复制并应用于所有 其他数据库。图 2“跨数据中心复制”显示了 XDCR 如何支持连接到每个数据库 实例的客户机应用程序。 图 2.“跨数据中心复制”
28 . 1 使用同步 Command log 在 DR 环境中建议使用同步 command log,以确保数据安全和完整的持久 化。因为数据复制是异步进行的,数据库复制涉及将完成的事务结果从一个数据 库传送到另一个数据库。因为数据复制发生在事务完成之后,所以不能保证集群 的内容在任何给定的时间点都是相同的。实际的情况是,生产者数据库将事务结 果以二进制日志方式分发给消费者数据库,消费者数据库在每个分区接收并应用 二进制日志之后,状态才能“赶上”发送数据库(或生产者)。 此外,由于 DR 是在每个分区的基础上发生的,因此对分区的更改可能不会 在消费者上以相同的顺序发生,因为一个分区可能比另一个分区复制得更快。通 常这不是问题,因为所有事务的结果在二进制日志中都是原子性的。但是,如果 生产者集群崩溃,则不能保证消费者已经设法检索了所有排队的日志。因此,在 生产者上完成的一些事务可能没有反映在消费者上。 幸运的是, 如果开启了同步 command log,数据将以同步的方式被持久化 到磁盘。当您重启失败的集群时,任何未确认的事务都将从失败集群的基于磁盘 的 DR 缓存中重播,从而允许集群在停止的地方恢复。 2 防止数据复制冲突 跨数据中心复制(XDCR)特有的一个问题是需要考虑数据冲突问题。被动复 制是不存在数据冲突问题的,因为更改只在一个方向上传播。但是,使用 XDCR, 可以在几乎相同的时间对两个数据库上的相同数据进行更改。然后将这些更改发 送到另一个数据库,可能导致不一致或无效的事务。例如,假设集群 A 和集群 B 正在处理事务,如下图“事务顺序和冲突解决”所示。Cluster A 执行一个修改
29 .特定记录的事务,该事务包含在二进制日志 A1 中。当集群 B 接收二进制日志并 处理 A1 时,集群 B 已经处理了自己的事务 B1 和 B2。这些事务可能修改了与 A1 中的事务相同的记录,或者与 A1 中的更改冲突的另一条记录,例如匹配的 惟一索引条目。 事务顺序和冲突解决 在这种情况下,集群 B 不能简单地应用 A1 中的更改,因为这样做可能违反 schema 的唯一性约束,更重要的是,可能导致两个数据库集群的内容出现分歧。 相反,集群 B 必须决定优先执行哪个更改。这个决策过程称为冲突解决。 VoltDB 使用定义良好的规则来解决冲突。然而,防止冲突的最佳保护措施 是首先设计应用程序以避免冲突。在客户端应用程序中,至少可以做两件事来避 免冲突: 使用主键 在可能的情况下,最好为每一个 DR 表定义一个主键。主键索引大大提高了 查找匹配行以将更改应用于消费者集群的性能。数据复制时,主键是在消费者集 群上查找和匹配记录的最精确条件。如果 DR 表上没有主键,可能会增加数据冲 突发生几率,并且数据库也只能采取拒绝操作。关于 DR 表上主键的更多描述,