Skip to content
agapple edited this page Jan 27, 2014 · 1 revision

背景

  hint是一种数据库提供的特殊语法,允许你干预数据库的SQL执行方式。 

  比如: 

  目前tddl中也保留了hint的这种方式,允许用户干预tddl5的执行方式.  

工作原理


 

说明:引入hint后,整个工作流会有所变化.  

hint执行方式:

  • 绕过sql解析/优化器/执行器,直接与某存储的驱动交互,比如使用mysql driver链接某个物理数据库获取数据,【可解决sqlj解析不支持的情况】
  • 进行sql解析,干预优化器的rule的计算方式/索引选择,改变执行流程. 

Hint格式

基本格式:

 /*+TDDL(  文本  )*/

说明:tddl可识别的hint必须以/*+TDDL( 进行打头,末尾是)*/,注意中间别加额外的空格,目前是个严格匹配

为了支持更灵活的扩展和定义,hint中的文本的内容采用的是json格式,比如看一个例子:

 /*+TDDL({"type":"condition"})*/

下面来具体介绍下目前支持的tddl hint的功能. 

direct模式

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模式

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. 

extra参数模式

参数模式主要是指运行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 hint,主要是group层,允许用户强制指定库进行操作,比如强制指定备库进行查询等。 

参数格式:

/*+TDDL_GROUP({groupIndex:0})*/

参数说明:

  • groupIndex中指定是数组下标,对应数组为为atom数据源列表,一般常见0代表是主库(写库),>=1代表是备库(读库).

注意点:【指定group hint会忽略动态数据源定义中的读写状态定义】

  1. Group hint模式,对应的格式和前面的几种hint有所不同,开头为/*+TDDL_GROUP。主要的考虑:直接依赖group层目前在淘宝比较普遍,而Hint模式主要是作用于matrix层,考虑历史兼容性,保留了Group Hint这一特殊的模式.
  2. 如果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 
}