NDB集群内部/ NDB架构对象版本

第六章NDB架构对象版本

NDB支持在线模式更改。一个模式对象,例如表格指数有一个4字节架构对象版本标识符的输出中可以观察到ndb_desc实用程序(见ndb_desc -描述NDB表),如下图所示(强调字体):

-d test t1—t1—版本:33554434分片类型:HashMapPartition K值:6最小负载因子:78最大负载因子:80临时表:no属性数量:3主键数量:1 frm数据长度:269 Row Checksum: 1 Row GCI: 1 SingleUserMode: 0 ForceVarPart: 1 FragmentCount: 4 ExtraRowGciBits: 0 ExtraRowAuthorBits: 0 TableStatus: retriedhashmap:DEFAULT-HASHMAP-240-4——Attributes——c1 Int PRIMARY KEY DISTRIBUTION KEY AT=FIXED ST=MEMORY AUTO_INCR c2 Int NULL AT=FIXED ST=MEMORY c4 Varchar(50;latin1_swedish_ci) NOT NULL AT=SHORT_VAR ST=MEMORY——Indexes——PRIMARY KEY(c1) - UniqueHashIndex PRIMARY(c1) - OrderedIndex NDBT_ProgramExit: 0- OK

模式对象版本标识符(或简单的模式版本)由一个大版本和一个小版本组成;主版本占用模式版本中(单个)最不重要的字节,次版本占用剩余的(3个最重要的)字节。在以十六进制表示法查看模式版本时,可以更容易地看到这两个组件。在刚刚显示的示例输出中,模式版本显示为33554434,在十六进制中(必要时填充前导零)为0 x02000002;这相当于主版本2,次版本2。向表中添加索引t1所报告的模式版本ndb_desc前进到50331650,或0 x03000002十六进制,相当于主版本2(3个最低有效字节)00 00 02),次要版本3(最高有效字节03).对于新创建的表,次要模式版本从0开始。

此外,每个NDB API数据库对象类都有自己的对象类getObjectVersion ()方法,比如对象::getObjectVersion (),返回对象的模式对象版本。这不仅包括的实例对象,但是表格指数LogfileGroup表空间数据文件,Undofile,以及事件.(然而,NdbBlob: getVersion ()具有与刚才列出的方法完全无关的目的和功能。)

被认为向后兼容的模式更改—例如添加默认的列在表的末尾会导致表对象的次要版本增加。不认为向后兼容的模式更改(例如从表中删除列)会导致主版本增加。

请注意

虽然导致模式主版本更改的操作的实现实际上可能涉及受影响表的2个副本(删除和重新创建表),但可以观察到最终结果是表的主版本增加。

来自NDB客户端的查询和DML操作也有一个相关的模式版本,在数据节点的处理开始时进行检查。如果请求的模式版本仅在其次要版本组件中与受影响的数据库对象的最新模式版本不同,则认为操作是兼容的,并允许继续进行。如果模式版本与主模式版本不同,那么它将被拒绝。

这种机制允许以各种方式在数据节点中更改模式,而不需要在客户端中同步更改模式。客户端在准备好之前不需要转移到新的模式版本。因此,查询和DML操作可以不间断地继续进行。

NDB API和模式对象的版本。NDB API应用程序通常使用NdbDictionary对象。Ndb对象检索模式对象。模式对象根据需要从数据节点检索;信令用于获取表或索引定义;然后,构建应用程序可以使用的本地内存对象。NDB在内部缓存模式对象,因此每个对相同表或索引的连续请求都不需要信号。

全局模式缓存。为了避免每个模式对象查找都需要向数据节点发送信号,每个模式对象都使用一个模式缓存Ndb_cluster_connection.这被称为全局模式缓存.就跨越多个Ndb对象而言,它是全局的。实例化的表和索引对象将自动放入此缓存中,以节省未来的信号和实例化成本。缓存为每个对象维护一个引用计数;该计数用于确定何时可以删除给定的模式对象。模式对象可以通过显式API方法调用或本地模式缓存操作修改其引用计数。

本地模式缓存。除了每个连接的全局模式缓存之外,每个Ndb对象的NdbDictionary对象具有本地模式缓存.此缓存包含指向全局模式缓存中保存的对象的指针。每个持有全局模式缓存中模式对象引用的本地模式缓存都会使全局模式缓存引用计数加1。有一个本地的模式缓存Ndb对象允许在不施加任何锁的情况下查找模式对象。在关联本地模式缓存时,通常会清空本地模式缓存(减少进程中的全局缓存引用计数)Ndb对象被删除。

不改变架构的操作。在以下情况下,正常操作如下:

  1. 某个客户端(Ndb对象)第一次请求一个表。检查本地缓存;然后全局缓存也被检查(使用锁),结果是另一个miss。

    由于没有缓存命中,数据节点被发送一个信号;节点的响应用于实例化表对象。指向实例化数据对象的指针被添加到全局缓存中;将另一个这样的指针添加到本地缓存中,并将引用计数设置为1。一个指向表的指针返回给客户端。

  2. 第二个客户端(不同的Ndb对象)请求访问同一个表,也是通过名称。对本地缓存的检查会导致失败,但对全局缓存的检查会产生成功。

    结果,一个对象指针被添加到本地缓存中,全局引用计数增加——因此它的值现在是2——一个对象指针被返回给客户端。没有新的指针被添加到全局缓存。

  3. 第二次,第二个客户端按名称请求访问同一个表。检查本地缓存,产生一个命中。对象指针立即返回给客户端。不会向本地缓存或全局缓存添加指针,对象的引用计数也不会增加(因此引用计数保持恒定为2)。

  4. 第二个客户端删除Ndb对象。此客户端本地模式缓存中的对象在全局缓存中的引用计数递减。

    这将全局缓存引用计数设置为1。因为它还不是0,所以还没有采取任何行动来删除父节点Ndb对象。

模式的变化。假设对象的模式永远不会改变,那么在应用程序进程的生命周期中,首先检索到的模式版本将被使用,只有当所有本地缓存引用(即对Ndb对象)已被删除。除了关机或集群连接重置期间,不太可能发生这种情况。

如果在应用程序运行时,对象的模式以向后兼容的方式更改,则会产生以下影响:

  • 数据节点上的小版本会递增。(使用旧模式版本进行的DML操作仍然成功。)

  • NDB API客户端随后检索模式对象的最新版本,然后获取新的模式版本。

  • 缓存旧版本的NDB API客户端不会使用新的模式版本,除非且直到它们的本地和全局缓存失效。

  • 订阅事件的NDB API客户端可以观察到TE_ALTER事件,并可以使用此事件触发模式对象缓存失效。

  • 可以通过调用来删除每个本地缓存项removeCachedTable ()removeCachedIndex ().这将从本地缓存中删除条目,并减少全局缓存中的引用计数。当(如果)全局缓存引用计数为零时,旧的缓存对象可以被删除。

  • 或者,可以删除本地缓存项,并使全局缓存项失效invalidateTable ()invalidateIndex ().后续调用可以获得的()getIndex ()对于这个客户端和其他客户端,通过向数据节点发送信号并实例化一个新对象来返回新的模式对象版本。

  • Ndb对象按需从全局表缓存中填充它们的本地表缓存。这意味着,一旦旧的模式对象在全局缓存中失效,这些对象将检索在第一次缓存表对象时已知的最新表对象。

当发生不兼容的模式变更(即模式主版本变更)时,使用旧版本的NDB API请求在提交新版本时立即失败。这也可以用作检索新模式对象版本的触发器。

管理模式版本更改处理的规则总结在以下列表中:

  • 在线模式更改(小版本更改)不会影响现有客户端(Ndb对象);客户端可以继续使用旧的模式对象版本

  • 当且仅当客户端通过API调用主动删除缓存对象时,它才能观察到新的模式对象版本。

  • 作为Ndb对象删除缓存的对象和被删除时,旧模式对象版本上的引用计数会减少。

  • 当引用计数达到0时,对象可以被删除。

模式对象生命周期的含义。模式对象(例如表格指数的生命周期所限制Ndb对象,从中获取该对象。当父节点Ndb对象的引用计数将删除Ndb活动的对象被递减。如果这Ndb对象保存对给定模式对象版本的最后一个重构引用,即删除Ndb对象也会导致模式对象的删除。因此,此时没有其他线程可以使用该对象。

当指向模式对象的指针保存在应用程序中并在多个对象之间使用时,必须小心Ndb对象。类的生命周期之后不应使用模式对象Ndb创建它的对象。

应用程序可以异步且彼此独立地响应向后兼容的模式更改,仅在必要时才迁移到新模式。不同的线程可以并发地操作不同的模式对象版本。

因此,确保模式对象不会比Ndb用于创建它们的对象。为了帮助防止这种情况发生,您可以采取以下任何操作来使旧的模式对象无效:

  • 要在需要时触发失效,请使用NDB APITE_ALTER事件(见事件:TableEvent).

  • 使用外部触发器启动失效。

  • 显式地执行周期性失效。

用任何一种方法使缓存失效都允许应用程序根据需要获得模式对象的新版本。

同样值得注意的是,并不是所有的NDB API表格Getter方法返回指针;他们中的许多人(除了表::getName ())返回表名。这些方法包括指数::可以获得的()NdbOperation: getTableName ()事件::getTableName (),NdbDictionary: getRecordTableName ()