AB表设计
以前在一家第三方结算的公司做运维DBA,在一次与开发人员探讨某个表的数据运维。那时我头脑想到AB表来切换使用,比如当A表写满后,就切到B表,那么A表就可以做运维工作。看到系统有管理,业务,日志WEB页面,我想应该有个数据运维页面,在页面上点击个按钮就改变应用程序从读A到读B的操作。当然没实现,比较在一个成型的系统不好做。去年面试个小额贷款公司,就被问到跨年问题,该表如何设计跨年。。。当时脑比较混沌没有回答好。出来后问了下朋友,朋友说采用RENAME方式。后来现在这公司,参加一次开发主导的头脑风暴,主要是评审。六张表每天都要装在数据,装完后由人工智能程序去分析,分析后写到结果表中。六张表每天都要清空再装新数据!为了不影响业务所以采用AB表方式。我当时感觉鸡冻,居然我的以前的想法现在有人去实现了。可惜接下来深度了解中后,有些遗憾!虽然采用了AB表,却采用RENAME去实现!
审核对方的SHELL脚本,基本流程是 清空表,删除索引,加载数据,创建索引,收集统计信息,把A表改名到C表,把装好数据的B表改名为A表,然后在对索引改名,最后把C表给删除。改表之前 SET LOCK_DDL_TIMEOUT=30,LOCK TABLE。。。
为了获得改名成功,设置了试图加DDL锁的等待时间,然后加表的独占锁,才开始改名。陪开发人员测试脚本,主要测试逻辑,还有注意事项,折腾了1个星期,从原来1个脚本变成了12个脚本。
本来我设想的AB表 额外加个路由表。当B表准备好了,就是数据装好了,索引重建了,信息收集完了.就去路由表更改标志位。 这样应用程序通过标志位获得当前该读取那张表。类似如下代码

SELECT TALBE_FLAG  INTO L_FLAG
FROM ROUT_TABLE
WHERE TABLE_NAME='XXX'

IF L_FLAG='A' THEN
   EXECUTE IMMEIDATE 'SELECT * FROM XXX_A';
ELSE 
   EXECUTE IMMEIDATE 'SELECT * FROM XXX_B';
END IF;

好处:
1 是不用删除C表 空间浪费
2 是不用REMANE 这样的DDL锁 带来的风险
3 不用更改索引名字

这样的脚本就比较单纯,就
1 清空装数的目标表;
2 删除索引;
3 装在数据;
4 重建索引;
5 收集统计信息;
6 更改路由表;

什么场景下使用AB表?
1 如上面的业务只读,数据由外部装入
2 跨年表 因为业务上新年一过就不处理去年的业务
3 日志写表 应用不断往一个表写入日志,该日志只是为了审计,不参与业务。日志表太大了,通过常规方式清理会影响日志写的程序。

总之那种与业务关联不强,也与其他表关联不强的表数据可以通过AB表+路由表方式进行数据维护。也可是说是数据优化!

如今互联网应用都很多了,尤其那种不可申请停机,也没有业务上停机维护时间窗口,也没有高峰和低谷时间,平时每分钟交易频繁,产生大量数据,没有下手做维护的机会。那么AB表设计就是一个不错的选项!!
关注微信公众号:https://mp.weixin.qq.com/s/xZycdqNelKzowcCnC5e73g

前篇链接 设计AB表
今天我跟朋友继续讨论AB表设计的应用场景, 从上面来说 就那么几个场景可以使用. DBA说可以用分区交换方式替代. 真的吗? A B表就这点生产空间吗?
非也 ! 因为2016年的灵感的激发,绝不是这些场景引起来的.

什么场景下使用AB表?
1 如上面的业务只读,数据由外部装入
2 跨年表 因为业务上新年一过就不处理去年的业务
3 日志写表 应用不断往一个表写入日志,该日志只是为了审计,不参与业务。日志表太大了,通过常规方式清理会影响日志写的程序。

难道不能应用在业务相关的表吗? 好像也可以哦,只有解决了读的问题,
比如说先读A表, 没数据,再去读B表. 这样就获得了数据,解决了业务数据不一致性问题. 只是要读两次!

很多7X24小时的业务系统, 互联网+的系统 基本上没有停机维护的机会.尤其是MYSQL 要给它增加字段,索引,哪怕是注解都要获取元数据锁 MDL。这样造成阻塞DML操作! 如果使用A B表就不会了,随时随刻都可以进行维护,在不处于服务状态的表进行添加个字段,添加索引,添加注解。不会阻塞写操作,读操作通过MVCC 去了。点击下切换按钮,更改路由表的 服务状态TABLE_FLAG=‘B’

SELECT TALBE_FLAG  INTO L_FLAG
FROM ROUT_TABLE
WHERE TABLE_NAME='XXX'

## DML 判断 一次DML
IF L_FLAG='A' THEN
   INSERT INTO TABLE_A VALUES('','');
ELSE 
   INSERT INTO TABLE_B VALUES('','');
END IF;

##读判断,读两次
IF L_FLAG='A' THEN 
 SELECT COUNT(1) L_EXITS FROM TABLE_A WHERE AGE=?;
  IF L_EXITS > 0 THEN
    SELECT NAME,AGE,MONEY FROM TABLE_A WHERE AGE=?;
  ELSE L_EXITS = 0 THEN
    SELECT NAME,AGE,MONEY FROM TABLE_B WHERE AGE=?;
END IF

IF L_FLAG='B' THEN 
 SELECT COUNT(1) L_EXITS FROM TABLE_B WHERE AGE=?;
  IF L_EXITS > 0 THEN
    SELECT NAME,AGE,MONEY FROM TABLE_B WHERE AGE=?;
  ELSE L_EXITS = 0 THEN
    SELECT NAME,AGE,MONEY FROM TABLE_A WHERE AGE=?;
END IF