如何优雅的遍历 Mycat 分库表
Mycat 作为数据库的中间件,被许多项目用来做关系型数据库的分库分表;我目前所在的项目由于业务和数据量的增长,也使用 Mycat 来进行分库分表。最近由于一个需求需要遍历一个分库表的数据,对数据进行相应的业务操作。
例如我们有一个 user 表,该表被分为 16 个库,那么可以如何高效而优雅的实现对 user 表中所有数据的遍历呢?
使用表的主键 ID 分页遍历这种方法是最直接了当,也是最容易想到的;例如使用如下的 sql 语句分批查询表中的数据:
select * from user order by idlimit a, 10000;
该 sql 语句以每批 1 万条纪录的方式遍历 user 表,每次查询使用上次 id 的最大值替换 a 的值,直到没有纪录返回。
这种实现固然可行,但其最大的问题在于效率,每次查询都会从所有的分库中各获取 1 万条纪录,那么 Mycat 总共得到 16 万行的数据,java培训再进行排序,取前 1 万条记录返回。如此遍历下来,总共查询了 user 表总记录数 16 倍的数据量,而且还进行排序,这种消耗无疑是巨大的。如果分库数量更多,资源的消耗和浪费更严重。此方案在性能上是不可接受的。
单独遍历每个分库 Mycat 提供了通过注解的方式指定从某个分库执行 sql 语句,注解语法为
/!mycat:dataNode = ${dn} /
其中,dn 为分片节点的名称;这样,我们就可以对上述的查询语句进行改造,指定分片节点进行查询遍历,如:
/!mycat:dataNode = dn1 /select * from user order by idlimit a, 10000;
使用以上的 sql 语句,先对名称为 dn1 分片节点的数据进行遍历,然后修改注解中的节点名称,继续遍历其他节点;把所有的节点都遍历完成后,user 表的数据也就遍历完毕。这种遍历方式大大提高了效率,不管 user 表被分成多少个分片,所有的数据都只被读取一次。
此方法的不足之处是必须事先获取所有分片节点的名称,写在代码或配置文件中;当分片进行扩容时,分片节点数量增加,配置文件或代码就必须做对应的修改,否则就获取不到表中所有的数据。
自动获取所有分片节点名称那么,有没有一种方法可以实时自动的获得分库表的所有分片节点名称呢?
Mycat 除了数据操作端口(默认 8066)外,还提供了管理端口(默认 9066),通过连接该端口,可以查看 Mycat 的运行数据并做管理操作。通过管理端口登录 Mycat 后,可以通过 show @@datanode 命令查看分片节点信息,如下所示(结果已略去部分列):
NAME 列即为节点名称,只要获取到该列所有的值即可。但是管理端口有较高的操作权限,一般情况下出于安全原因,该端口仅运维有权限登录操作,应用无权访问。
还有其他更好办法吗?
这时想到了执行计划, 对,执行计划! Mycat 支持 explain 命令查看 sql 语句的执行计划,对于分库表,会返回 sql 语句执行需要查询哪些节点;这样我们就可以通过构造一个需要查询所有节点的 sql 语句来获取全部节点信息,例如通过如下所示的语句:
DATA_NODE 列即为节点名称;节点取到之后,剩下的就简单了,无非是对各个节点单独遍历。这种方式通过查询一次 Mycat 就获得了所有分片节点的名称,java培训而且数据库的连接也可以复用,真正做到了既高效又优雅。
当然,以上的解决方案是限定于 Mycat 与 MySQL 的范围之内,如果数据可以同步到 NoSQL 中,那又是从另外的角度去解决了。
评论