6.4 维护


  • 停机

    Switcher这类服务可以用外部结束进程的方式关闭。

    集成了Zdb服务的应用,比如Auany,GlobalId,Provider之类的服务,有两种关闭方法。

    1. 结束Switcher后,等待一个以上的Zdb checkpoint周期,再结束进程,确保数据完全刷新到底层数据库。

    2. 正确配置服务的JmxServer参数,然后使用jmxtool的Stop子命令,确保Zdb数据刷新到底层数据库,服务按正确顺序停止。例如,结束auany服务,可以执行命令:


    java -jar limax.jar jmxtool stop -c "service:jmx:rmi://localhost:10202/jndi/rmi://localhost:10201/jmxrmi"
    

    该命令允许一个额外的-d delay参数,delay为毫秒,指令服务器在delay时限达到之后停机。


  • 备份与恢复

    备份与恢复的方式依赖于使用的底层数据库引擎。

    • 使用EDB引擎的备份

      使用jmxtool的backup子命令实现备份功能,支持全备份,增量备份两种方式。

      backup子命令参数:

      1. –d <backupdirectory> 指定备份目录

      2. –i <true or false> true表示执行增量备份,false表示不进行增量备份。

      这里备份与sqlserver之类的备份方式稍有区别,sqlserver类数据库,要求增量备份之前必须存在一个全备份。所以要做增量备份必须按照dump database,(dump transaction)+ 的方式执行。limax上实现为只要执行了backup子命令作增量备份,首先执行全备份,logrotate的时候自动复制数据库日志到备份目录。这种方式可以简化备份规则设计,例如,要求以一天为周期作增量备份,只需要在每天特定时间点执行一次增量备份即可。

      例如:增量备份auany的zdb数据库,执行如下命令即可


      java -jar limax.jar jmxtool backup -c "service:jmx:rmi://localhost:10202/jndi/rmi://localhost:10201/jmxrmi" -d c:\temp\backup –i true
      

    • 使用EDB引擎的恢复

      如果恢复的是全备份,直接将备份目录拷贝成zdb目录即可。

      如果在增量备份上恢复,则按下述步骤进行:

      1. zdb目录重命名成zdb.old

      2. 备份目录拷贝成zdb目录

      3. 将zdb.old/log目录下的文件覆盖到zdb/log中。这是由于zdb.old/log目录下的日志文件中可能存在最新的还没有提交到备份目录的checkpoint信息。

      如果要按选择的时间点恢复,上面的拷贝完成之后在zdb目录上用命令行方式执行EDB的交互工具edbtool:


      java -jar limax.jar edbtool
      #help
      rescue <src dbpath> <dst dbpath>
      list checkpoint <dbpath>
      recover checkpoint <dbpath> <recordNumber>
      out <filename> <charset> #default System.out UTF-8
      exit
      quit
      #list checkpoint c:\temp\backup
      0 : 2015-04-18 16:15:36.210
      #
      

      关键命令有两条,list checkpoint命令,列出当前数据库所有checkpoint时间点,从0开始编号,在列表中选择期望的时间点编号执行recover checkpoint命令, 数据库数据量较大,时间点较多的情况下,命令可能执行较长时间,命令完成后,数据库恢复到选择的那个时间点的状态。


    • 使用Mysql引擎的备份与恢复:

      直接使用Mysql的备份与恢复策略。


  • 数据格式转换

    应用升级以后,可能需要转换Zdb中存储的数据格式,需要实现类似sql数据库的ALTER TABLE功能。这项工作的大部分任务应该交由应用的开发者完成, 提供相应的转换程序, 交由运营者执行生产环境上的转换。

    Limax框架提供转换所需的相应的支持,这里以提供一个以前面的服务器开发章节example提供的zdb描述为例,介绍转换方法。

    • 转换实例

      1. zdb描述

      2. 添加一条记录


      import limax.util.Pair;
      import limax.util.Trace;
      import limax.zdb.DBC;
      import limax.zdb.Procedure;
      import limax.zdb.Zdb;
      import limax.zdb.tool.DataWalker;
      
      public final class ConvertTest {
      	public static void main(String[] args) throws Exception {
      		new java.io.File("zdb").mkdir();
      		Trace.set(Trace.ERROR);
      		limax.xmlgen.Zdb meta = limax.xmlgen.Zdb.loadFromClass();
      		meta.setDbHome("zdb");
      		Zdb.getInstance().start(meta);
      		Procedure.call(() -> {
      			Pair<Long, xbean.MyXbean> pair = table.Mytable.insert();
      			pair.getValue().setVar0((short) 123);
      			pair.getValue().getSlist().add("name123");
      			return true;
      		});
      		Zdb.getInstance().stop();
      		DBC.start();
      		DBC dbc = DBC.open(meta);
      		DataWalker.walk(dbc.openTable("mytable"), kv -> {
      			System.out.println(kv.getKey() + ", " + kv.getValue());
      			return true;
      		});
      		DBC.stop();
      	}
      }
      

      运行这段代码,返回如下结果

      4096, {var0:123, slist:["name123", ], }

      3. 更新zdb的xml描述


      <xbean name="MyXbean">
      	<variable name="var0" type="short" />
      	<variable name="slist" type="vector" value="string" />
      </xbean>
      <table name="mytable" key="long" value="MyXbean" autoIncrement="true"/>
      

      注意到,MyXbean.var0 类型从int改变为short

      4. 重新生成代码,刷新eclipse,这时发现代码出现错误,将pair.getValue().setVar0(123); 改为 pair.getValue().setVar0((short)123);

      5. 再次运行

      这时,运行报告错误

      Exception in thread "main" limax.zdb.XError: convert needed: {mytable=MANUAL}

      运行结果指明了,当前版本应用与前一版本应用的Zdb数据库不兼容,mytable这张表需要手动转换才能兼容。

      6. 运行转换工具生成转换代码

      在应用的当前目录下创建zdbcov目录,执行命令


      java -cp <path to limax.jar>;bin limax.zdb.tool.DBTool -e "convert zdb zdbcov"
      

      注意,这里指明了2个classpath,一个是limax.jar,另一个是当前应用的bin目录。

      这时,获得如下输出:

      mytable MANUAL

      -----COV.class not found, generate-----

      make dir [cov]

      make dir [cov\convert]

      generating cov\convert\Mytable.java

      generating cov\COV.java

      这个输出,指明了mytable表需要手动转换,创建了一cov目录,里面放置了转换用框架代码。

      刷新eclipse,配置项目属性,将cov目录设置为源码目录。

      在Mytable.java里面寻找//TODO这一行,这里就是填写手工转换代码的地方。


      // TODO var0 = s.var0;
      

      假设我们将这行修改为:


      var0 = (short) -s.var0;
      

      7. 再次运行代码转换工具


      java -cp <path to limax.jar>;bin limax.zdb.tool.DBTool -e "convert zdb zdbcov"
      

      获得如下结果

      2015-05-12 17:56:14.283 INFO <main> limax.zdb.DBC start ...

      mytable MANUAL

      -----COV.class found, manual convert start-----

      copying... _sys_

      converting... mytable

      2015-05-12 17:56:14.390 INFO <main> limax.zdb.DBC stop begin

      2015-05-12 17:56:14.397 INFO <main> limax.zdb.DBC stop end

      -----manual convert end-----

      在这里,能够看到创建了新目录zdbcov存放转换后的数据库,mytable表上执行了转换,不需要转换的_sys_表被直接拷贝。

      验证转换效果

      前一版本的zdb目录重命名为zdb.old,zdbcov目录重命名为zdb

      重新执行ConvertTest.java,获得如下结果:

      4096, {var0:-123, slist:["name123", ], }

      8192, {var0:123, slist:["name123", ], }

      注意到,key=4096行,var0变为了-123,这正是转换代码实现的功能,转换成功。


    • 转换相关的细节

      前面的例子mytable的转换类型报告为MANUAL,_sys_的转换类型报告为SAME,事实上系统中提供4种转换类型,定义在limax.zdb.tool.ConvertType中, 分别为SAME, AUTO, MAYBE_AUTO, MANUAL。

      转换类型含义如下:

      • SAME

        相同。这样的表在转换时直接拷贝。

      • AUTO

        转换不会损失精度,例如整数从短到长的转换;一个bean,去掉了某些字段,并且该bean又没有作为任何map的key存在。这类转换可以自动进行,无需用户干预。

      • MAYBE_AUTO

        可能损失精度的转换,例如整数转换为浮点;一个bean,添加了某些字段,需要初始化添加的字段。这类转换可以自动进行,用户愿意干预也可以进行干预。

      • MANUAL

        其他一切情况,必须用户干预才能进行的转换。

      如果新版应用启动失败,需要进行转换,首先应该使用命令:


      java -cp <path to limax.jar>;bin limax.zdb.tool.DBTool -e "convert zdb zdbcov"
      

      获取转换类型,如果存在MANUAL或者MAYBE_AUTO类型的转换,则该命令会生成框架代码,填写相应的TODO,编译完成后再次运行代码转换工具即可。

      对于在上述的结果中只有MAYBE_AUTO,没有MANUAL的情况下,如果允许执行损失精度的转换,则可以删除先前生成的cov目录,直接使用命令:


      java -cp <path to limax.jar>;bin limax.zdb.tool.DBTool -e "convert zdb zdbcov true"
      

      例如,将上例中的var0类型改变为float,运行上述命令获得如下结果:

      mytable MAYBE_AUTO

      -----no need generate!, auto convert start-----

      2015-05-12 23:40:52.532 INFO <main> limax.zdb.DBC start ...

      mytable MAYBE_AUTO

      copying... _sys_

      auto converting... mytable

      2015-05-12 23:40:52.623 INFO <main> limax.zdb.DBC stop begin

      2015-05-12 23:40:52.630 INFO <main> limax.zdb.DBC stop end

      -----auto convert end-----

      这里可以看到,MAYBE_AUTO的表也被自动转换了。

      事实上,convert命令的参数格式如下:


      convert [fromDB [toDB [autoConvertWhenMaybeAuto [generateSolver]]]]
      

      fromDB 指定了转换源,默认为zdb

      toDB 指定了转换目标,默认为zdbcov

      autoConvertWhenMaybeAuto 指示是否直接转换MAYBE_AUTO类型的表,默认false

      generateSolver 指定了是否生成合并代码,默认false。数据库的合并后面介绍。


    • 转换总结

      1. 对于应用开发者而言:

      完成新版本开发之后,应该在上一个版本的zdb数据库上运行应用,如果报告错误提示数据转换,则应该在当前项目目录创建zdbcov目录并运行:


      java -cp <path to limax.jar>;bin limax.zdb.tool.DBTool -e "convert zdb zdbcov"
      

      如果生成了转换代码,根据需要提供自己的记录转换实现,打包新版本应用,集成转换工具,测试通过后,提交给运营环境在生产系统中执行。

      2. 对于运营环境而言:

      获取新版本应用以后,如果获知需要转换,则执行转换


      java -cp limax.jar;application.jar limax.zdb.tool.DBTool -e "convert zdb zdbcov"
      

      执行完成以后,备份原始zdb目录,将zdbcov目录改名为zdb。

      最后,启动新版应用。

      3. convert的fromDB,toDB参数如果是MYSQL url,则需要在java –cp参数之后,追加mysql/Java连接器的jar包。

      4. 转换目标zdbcov,必须手工创建,对于EDB引擎创建一个目录,对于MYSQL引擎创建相应数据库。如果使用了MYSQL数据库,转换之后,可以将应用配置文件中dbhome直接指向新的数据库,减少数据库拷贝。


    • 注意事项

      对于关系数据库而言,大型表格的ALTER TABLE,非常耗时,同理,转换大型zdb数据库,也非常耗时。实际使用中,如果涉及到大型zdb数据库的转换,建议首先使用备份数据进行转换时长测试,确认停机时间上限,如果停机时间不可接受,则只能特别设计应用,在运行过程中逐步转换。


  • 数据库合并

    存在这样的应用,开始阶段分立运营,一段时间之后可能出现合并数据库的需求。Limax通过一系列手段支持这样的应用。

    • 合并的支持

      1. 使用GlobalId服务,同一GlobalId域之内的应用,通过GloalId服务提供唯一id,相应的id作为表的key,这样的表可以安全合并,而不会发生key冲突。

      2. Zdb的自增量key的配置,autoKeyInitValue,autoKeyStep,分立运营的同种应用,一开始配置相同的autoKeyStep,不同的autoKeyInitValue。这样,凡是使用自增量key的表可以安全合并,不会发生key冲突。

      3. 数据库合并,事实上是一种特殊类型的格式转换,与前面提到的格式转换相比,普通的格式转换目标数据库为空,执行合并时,目标数据库存在。对于同名表,如果在源数据库与目标数据库中,发现了相同的key,则认为发生冲突,这种情况下,可以生成相应代码框架,提示用户解决冲突。


    • 合并的操作

      1. 备份目标数据库, zdb -> zdb.bak

      2. 准备源数据库,假设为zdbsrc

      3. 执行合并


      java -cp limax.jar;application.jar limax.zdb.tool.DBTool -e "convert zdbsrc zdb"
      

      4. 如果没有冲突,合并完成,如果报告冲突,执行下面步骤。


      java -cp limax.jar;application.jar limax.zdb.tool.DBTool -e "convert zdbsrc zdb false true"
      

      convert的最后一个参数true指明了需要生成冲突处理代码,将生成的cov目录提交给应用

      5. 应用将cov目录作为源码目录,可以看到比起前面的数据格式转换,cov目录中多出一个solver包,里面存在所有数据库表的相关冲突解决代码。找到代码中的方法,


      public OctetsStream solve(OctetsStream sourceValue, OctetsStream targetValue, OctetsStream key)
      

      填写需要的TODO代码,重新打包应用。

      6. 提交应用,重新执行第3步。


    • 注意事项

      1. 建议尽量使用GlobalId,和正确配置Zdb自增量key,避免出现需要解决冲突的情况。

      2. 对于可能出现的冲突,设计上应该有预见。事实上,出现上一节提到的在运营阶段出现合并冲突,生成冲突解决代码,解决冲突的过程是不应该出现的。出现这一情况,应该理解为设计缺陷。

      3. 如果应用运行在MYSQL引擎上,并且能够确认合并操作决不会发生冲突,则可以直接在MYSQL上用SQL命令合并_meta_,_sys_之外的所有表,对于这两张表,保留目标数据库的版本即可。

      4. convert可以在EDB数据库和MYSQL数据库之间相互转换。

      5. convert可以同时生成格式转换代码和合并时的冲突解决代码,也就是说,格式转换与合并可以同时进行。为了避免混乱,建议按照先转换,再合并的顺序,分步操作。


上一页 下一页