-
Notifications
You must be signed in to change notification settings - Fork 17
Tddl_Hint
hint是一种数据库提供的特殊语法,允许你干预数据库的SQL执行方式。
比如:
- oracle 常见 hint http://www.cnblogs.com/ebs-blog/archive/2011/09/05/2167730.html
- mysql index hint http://dev.mysql.com/doc/refman/5.6/en/index-hints.html
目前tddl中也保留了hint的这种方式,允许用户干预tddl5的执行方式.
说明:引入hint后,整个工作流会有所变化.
hint执行方式:
- 绕过sql解析/优化器/执行器,直接与某存储的驱动交互,比如使用mysql driver链接某个物理数据库获取数据,【可解决sqlj解析不支持的情况】
- 进行sql解析,干预优化器的rule的计算方式/索引选择,改变执行流程.
基本格式:
/*+TDDL( 文本 )*/
说明:tddl可识别的hint必须以/*+TDDL( 进行打头,末尾是)*/,注意中间别加额外的空格,目前是个严格匹配
为了支持更灵活的扩展和定义,hint中的文本的内容采用的是json格式,比如看一个例子:
/*+TDDL({"type":"condition"})*/
下面来具体介绍下目前支持的tddl hint的功能.
direct顾明思议是直接的意思,其功能也即直接执行,允许绕过sql解析/优化器/执行器.
具体案例:
/*+TDDL({"type":"direct","dbid":"xxx_group"})*/select * from real_table_0;
参数说明:
- type指定为direct模式.
- dbid中指定的即为tddl三层数据源中的groupId
SQL执行效果:
- 不做sql解析
- 不做表名替换
- 绕过优化器/执行器,直接将原始SQL发送到对应的group ds上进行执行
/*+TDDL({"type":"direct","vtab":"vtab","dbid":"xxx_group","realtabs":["rtab_0","rtab_1"]})*/select * from vtab;
参数说明:
- type指定为direct模式.
- dbid中指定的即为tddl三层数据源中的groupId
- vtab对应为rule中的逻辑表名,也即意味着我实际SQL中会使用该逻辑表名
- realtabs为逻辑表名本次需要执行的物理表名,数组格式,可以为多个表名. 【需要注意,realtabs中定义的物理表一定是dbid对应库上的表】
SQL执行效果:
- 进行sql解析
- 扫描语法树,将vtab对应的逻辑表名:rtab,使用物理表名realtabs进行替换,物理表名多个时,每个物理表名会生成一个执行sql
- 绕过优化器/执行器,直接将基于物理表名生成的SQL发送到对应的group ds上进行执行
/*+TDDL({"type":"direct","vtab":"vtab_a,vtab_b","dbid":"xxx_group","realtabs":["rtab_a_0,rtab_b_0","rtab_a_1,rtab_b_1"]})*/select * from rtab_a as a inner join rtab_b as b on a.id = b.id ;
需求:用户执行多表join sql,而且多张表都是采用了分库分表,此时需要对多张表同时进行表名替换.
参数说明:
- vtab对应为rule中的逻辑表名,可逗号分隔. 例:vtab_a,vtab_b,即代表存在两张逻辑表
- realtabs为逻辑表名本次需要执行的物理表名,数组格式,可以为多个表名,并且每个单独的表名和vtab一样,可逗号分隔。【需要注意,realtabs的逗号分隔的物理表必须和逻辑表数统一】
例:rtab_a_0,rtab_b_0,分别对应两张逻辑表的物理表名
SQL执行效果:
- 进行sql解析
- 扫描语法树,将vtab和realtabs的映射关系:[rtab_a -> rtab_a_0,rtab_a_1],[rtab_b -> rtab_b_0,rtab_b_1],替换多张逻辑表名和,每组生成一个执行sql,本例子中总共会生成2条sql.
- 绕过优化器/执行器,直接将基于物理表名生成的SQL发送到对应的group ds上进行执行
condition顾明思议是条件的意思,其功能是给定SQL的分库分表条件,基于hint给定的条件计算出需要执行的目标分库分表,然后进行执行.
具体案例:
/*+TDDL({"type":"condition","vtab":"vtabxxx","params":[{"relation":"and","expr":["pk>4","pk<10"],"paramtype":"long"},{"expr":["id=1:long"]}])*/
参数说明:
- type指定为condition模式.
- vtab对应为rule中的逻辑表名
- params对应为构造Tddl_rule条件语法树的参数,是个数组格式,支持定义多个分区字段条件.
每个分区字段的条件定义,格式:
a. relation代表对应为expr中多个条件的组合方式: and / or.
b. expr代表Tddl_rule中的一个简单Comparative
c. paramtype可以指定expr中的处理类型.
本例子展开params后即为: ( pk>4 and pk <10 ) and (id = 1)
SQL执行效果:
- 解析为Tddl_Rule的条件语法树,计算出分库分表结果
- 进行sql解析
- 扫描语法树,将vtab对应的分库分表结果替换语法树中的vtab逻辑表名,每个分表结果生成一个执行sql.
- 绕过优化器/执行器,直接将基于物理表名生成的SQL发送到对应的group ds上进行执行
暂时不支持,请使用type=direct模式的多表名替换hint.
参数模式主要是指运行sql中指定优化器/执行器中一些选择性参数,比如是否开启索引选择,是否允许使用临时表.
参数格式:
/*+TDDL({"extra":"{"ALLOW_TEMPORARY_TABLE"="TRUE","CHOOSE_INDEX"="TRUE"}}])*/
参数说明:
- extra为对应参数的名字,参数的value为一个map结构,允许定义不同key->value结构
- 具体可定义的参数内容,可参见:@com.taobao.tddl.common.model.ExtraCmd
注意: extra模式可以与direct/condition模式组合使用,也可以单独使用.
因为部分业务使用了ibatis进行数据库操作,无法动态生成,因此hint中支持了基于PrepareStatement的参数设置模式,解决hint动态构造的问题.
IBatis使用例子:
<select id="select_test" resultClass="int"> <![CDATA[ /*+TDDL({"type":"condition","vtab":"vtab","params":[{"expr":["id=#id#:long"]}])*/ select count(*) from user where id = #id# and name = #name# ]]> </select>
可使用ibatis中的#id#获取对应的PrepareStatement中设置的参数.
JdbcTemplate使用例子:
String deleteSql="/*+TDDL({\"type\":\"?\",\"realtabs\":[\"?\",\"?\",\"?\",\"?\"],\"dbid\":\"tddl_group_1\",\"vtab\":\"moddbtab\"})*/delete from moddbtab where pk=?"; Object[] objs = new Object[] {"direct","moddbtab_0000","moddbtab_0001","moddbtab_0002","moddbtab_0003",id3 }; tddlJdbcTemplate.update(deleteSql, objs);
group hint,主要是group层,允许用户强制指定库进行操作,比如强制指定备库进行查询等。
参数格式:
/*+TDDL_GROUP({groupIndex:0})*/
参数说明:
- groupIndex中指定是数组下标,对应数组为为atom数据源列表,一般常见0代表是主库(写库),>=1代表是备库(读库).
注意点:【指定group hint会忽略动态数据源定义中的读写状态定义】
- Group hint模式,对应的格式和前面的几种hint有所不同,开头为/*+TDDL_GROUP。主要的考虑:直接依赖group层目前在淘宝比较普遍,而Hint模式主要是作用于matrix层,考虑历史兼容性,保留了Group Hint这一特殊的模式.
- 如果group hint中指定了groupIndex:1,潜在的风险:
a. DBA后台将index=1的库设置为NA状态(不可读不可写),客户端不会释放请求,因为目前的策略是忽略三层数据源中读写权重的设置,请求还是会发到index=1的机器上.
b. 应用进行写操作,即使当前index=1的库设置为R/NA状态(没有可写状态),当前的写操作也会成功,因为目前的策略是忽略三层数据源中读写权重的设置,请求还是会发到index=1的机器上.
(1).遍历所有表的sql操作
假定有1024张表,按照pk切分,每个表上都做一种更新,如果想遍历所有表做个操作,可以写成类似的伪码。
for(int index=0 ; index < 1024 ; index++ ) { /*+TDDL({"type":"condition",params:[{"expr":["your_shard_key=#index#"],"paramtype":"int"}],"vtab":"moddbtab"})*/update moddbtab set v = v+1 }