MyCATApache / Mycat-Server

GNU General Public License v2.0
9.5k stars 3.85k forks source link

连接db2数据库,使用PartitionByFileMap分片算法select语句不能正确路由 #2850

Open cremin-dong opened 3 years ago

cremin-dong commented 3 years ago

1、bug描述 连接db2数据库,使用PartitionByFileMap分片算法select语句不能正确路由。而Insert语句路由正常

2、版本号(非常重要) v 1.6.76

3、相关表的配置信息 schema.xml (需包含表的配置信息,mysql的连接驱动是JDBC还是native方式)

<schema name="GZH" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn0">
    <table name="t_banknoteinfo_0" primaryKey="id" dataNode="dn0,dn1" rule="sharding-gzh" autoIncrement="true" fetchStoreNodeByJdbc="true">
    </table>
</schema>

<dataNode name="dn0" dataHost="db0" database="DEMO_DS_0"  />
<dataNode name="dn1" dataHost="db1" database="DEMO_DS_1" />

<dataHost name="db0" maxCon="1000" minCon="1" balance="0" writeType="0" dbType="db2" dbDriver="jdbc">
    <heartbeat>select 1 from sysibm.sysdummy1</heartbeat>
    <!--<connectionInitSql>alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss'</connectionInitSql>-->
    <writeHost host="hostM2" url="jdbc:db2://localhost:60000/GZHDB2:currentSchema=DEMO_DS_0;" user="DB2INST1" password="xxxx" />
</dataHost>
<dataHost name="db1" maxCon="1000" minCon="1" balance="0" writeType="0" dbType="db2" dbDriver="jdbc">
    <heartbeat>select 1 from sysibm.sysdummy1</heartbeat>
    <!--<connectionInitSql>alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss'</connectionInitSql>-->
    <writeHost host="hostM2" url="jdbc:db2://localhost:60000/GZHDB2:currentSchema=DEMO_DS_1;" user="DB2INST1" password="xxxx" />
</dataHost>

rule.xml (涉及到的路由函数)

<tableRule name="sharding-gzh">
    <rule>
        <columns>SECOND_LEVEL_ORG_CODE</columns>
        <algorithm>gzh-db-algorithm</algorithm>
    </rule>
</tableRule>

<function name="gzh-db-algorithm"
          class="io.mycat.route.function.PartitionByFileMap">
    <property name="mapFile">partition-gzh-db.txt</property>
    <property name="type">1</property>
</function>

3、explain结果 截图

junwen12221 commented 3 years ago

dbType="db2"改成mysql看看?

cremin-dong commented 3 years ago

1、bug描述 无论是将dbType="db2"改成mysql,还是直接配置成mysql库,仍旧存在该问题,看来和具体库没有关系,不太确定是配置有问题还是的确存在bug

2、相关表的配置信息 改成mysql连接的schema.xml

<schema name="GZH" checkSQLschema="false" sqlMaxLimit="100" randomDataNode="dn0">
    <table name="t_banknoteinfo" primaryKey="id" dataNode="dn0,dn1" rule="sharding-gzh" autoIncrement="true" fetchStoreNodeByJdbc="true">
    </table>
</schema>

<dataNode name="dn0" dataHost="localhost1" database="db1" />
<dataNode name="dn1" dataHost="localhost1" database="db2" />

<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
          writeType="0" dbType="mysql" dbDriver="jdbc" switchType="1"  slaveThreshold="100">
    <heartbeat>select user()</heartbeat>
    <writeHost host="hostM1" url="jdbc:mysql://localhost:3306" user="root" password="123456" />
</dataHost>

3、explain结果

截图2

cremin-dong commented 3 years ago

1、补充配置文件 mycat验证.zip

junwen12221 commented 3 years ago

sql写错了

explain select * from t_banknoteinfo where seconde_level_org_code='3001'; seconde_level_org_code多写了一个e

正确应该是

explain select * from t_banknoteinfo where second_level_org_code='3001';

cremin-dong commented 3 years ago

不好意思,犯了这么低级的错误。

现在的情况(希望这次没有犯下低级错误,执行前验证了sql的正确性,手动狗头): 截图3

截图4

cremin-dong commented 3 years ago

初步跟踪在DefaultDruidParser的117行执行后出现问题,db2执行完后visitor的aliasMap获取失败

mysql执行

截图11

db2执行

截图13

上述差异会导致243行跳出遍历condition ,找分片字段失败

截图1111

junwen12221 commented 3 years ago

这个适配问题,需要你自己调试改代码解决了

改写语法的一些方法是

io.mycat.route.parser.druid.impl.DruidSelectParser#isNeedChangeLimit
io.mycat.route.parser.druid.impl.DruidSelectParser#setLimitIFChange

分片条件找不到可能是因为子查询的写法导致的,这个可以根据经验规律定制

cremin-dong commented 3 years ago

下载1.6.5版本的代码,相同的配置执行分片和分页都是正常的,看来是新版本引入的问题

截图11111111

非mysql会比mysql多以下逻辑: getDruidParserForMultiDB方法 -> parseTables方法中执行 stmt.accept(), 会比mysql的多执行一次 stmt.accept(),第二次时,因为TABLE_HAS_VISIT_FLAG(// 标识一个元素是否已经被VISIT,防止多次重复访问导致添加aliasMap时报表冲突异常)逻辑,不会再赋值,导致后续分片使用时aliasMap为空

截图3

截图4

截图5555

junwen12221 commented 3 years ago

修复了可以提交PR

cremin-dong commented 3 years ago

本地修复可以解决db2的问题,但是不能从根本上解决问题,oracle也有问题,4.9号的版本ec3f0ac验证还是正常的,主要问题好像是由于后续版本升级Druid引起的。这个感觉需要重新考量Druid库的升级,或者需要对每个库逐一验证修复了

cremin-dong commented 3 years ago

通过以下方法可以临时性解决当前问题,但这个毕竟只考虑的单一场景,还是希望从Druid入手全局性的解决一下

对于DB2的路由问题 在MycatSchemaStatVisitor的visit(SQLSelectStatement x)方法中加入如下代码既可以解决

11

对于DB2分页问题 在PageSQLUtil的limitDB2方法加入如下代码可以解决

222

junwen12221 commented 3 years ago

@funnyAnt 看看这个问题