注解原理

概念:
MyCat对自身不支持的Sql语句提供了一种解决方案——在要执行的SQL语句前添加额外的一段由注解SQL组织的代码,这样Sql就能正确执行,这段代码称之为“注解”。注解的使用相当于对mycat不支持的sql语句做了一层透明代理转发,直接交给目标的数据节点进行sql语句执行,其中注解SQL用于确定最终执行SQL的数据节点。注解的形式是:

/*!mycat: sql=注解Sql语句*/
  • 1

注解的使用方式是:

/*!mycat: sql=注解Sql语句*/真正执行Sql
  • 1

使用时将=号后的“注解Sql语句”替换为需要的Sql语句即可,后面会提到具体的用法。
原理:
MyCat执行SQL语句的流程是先进行SQL解析处理,解析出分片信息(路由信息)后,然后到该分片对应的物理库上去执行;若传入的SQL语句MyCat无法解析,则MyCat不会去执行;而注解则是告诉MyCat按照注解内的SQL(称之为注解SQL)去进行解析处理,解析出分片信息后,将注解后真正要执行的SQL语句(称之为原始SQL)发送到该分片对应的物理库上去执行。
从上面的原理可以看到,注解只是告诉MyCat到何处去执行原始SQL;因而使用注解前,要清楚的知道该原始SQL去哪个分片执行,然后在注解SQL中也指向该分片,这样才能使用!例子中的sharding_id=10010 即是指明分片信息的。
需要说明的是,若注解SQL没有能明确到具体某个分片,譬如例子中的注解SQL没有添加sharding_id=10010这个条件,则MyCat会将原始SQL发送到persons表所在的所有分片上去执行去,这样造成的后果若是插入语句,则在多个分片上都存在重复记录,同样查询、更新、删除操作也会得到错误的结果!
解决问题:

    • MySql不支持的语法结构,如insert …select…
  • 同一个实例内的跨库关联查询,如用户库和平台库内的表关联
  • 存储过程调用。
  • 表,存储过程创建。
    注解规范
  • 注解SQL使用select语句,不允许使用delete/update/insert等语句;虽然delete/update/insert 等语句也能用在注解中,但这些语句在Sql处理中有额外的逻辑判断,从性能考虑,请使用select语句
  • 注解SQL禁用表关联语句
  • 注解SQL尽量用最简单的SQL语句,如select id from tab_a where id=’10000’
  • 无论是原始SQL还是注解SQL,禁止DDL语句
  • 能不用注解的尽量不用
  • 详细要求见下面
    Select
    -选择能唯一确定分片的主表,如与用户表关联的时候可以选择用户表
  • 若是业务需要在主表所在的各个分片上都执行可以不加能确定分片的条件
    Insert
    对于分片表
  • 使用insert的表做注解SQL
  • 注解SQL必须能确认具体到某个分片
  • 原始SQL插入的字段必须包含分片字段
  • 原始SQL中包含的分片字段和注解SQL中的分片字段确定的分片务必要一致
  • 对于insert … select这种语句,请务必确认插入的记录都在当前查找到的分片上
    非分片表
  • 注解SQL必须能具体确认到某个分片
  • 注解SQL包含的分片字段其分片上必须包含这个非分片表
    Delete
  • 对于分片表使用要删除记录的表做注解SQL
    Update
  • 对于分片表用所要更新的表做注解SQL
  • 禁止更新分片表的分片列
  • 根据业务需要添加注解Sql的分片字段值
    Call
  • 若是要在所有的分片上都执行存储过程,则使用一个在所有分片上都包含的表,不添加任何分片条件 调用存储过程
  • 若是单个分片执行,使用能确认到这个分片的表以及分片条件
    补充说明:
    使用注解并不额外增加MyCat的执行时间;从解析复杂度以及性能考虑,注解SQL应尽量简单。至于一个SQL使用注解和不使用注解的性能对比,不存在参考意义,因为前提是MyCat不支持的SQL才使用注解。

注解使用示例

注解支持的’!’不被mysql单库兼容,
注解支持的’#’不被mybatis兼容
新增加mycat字符前缀标志Hintsql:”/* mycat: /”
从1.6开始支持三种注解方式:

/*#mycat:db_type=master*/ select * from travelrecord /*!mycat:db_type=slave*/ select * from travelrecord /**mycat:db_type=master*/ select * from travelrecord
  • 1

Mycat端执行存储创建表或存储过程为:
存储过程:

/*!mycat: sql=select 1 from test */ CREATE PROCEDURE `test_proc`() BEGIN END ;
  • 1

表:

 /*!mycat: sql=select 1 from test */create table test2(id int);
  • 1

注意注解中语句是节点的表请替换成自己表如select 1 from 表 ,注解内语句查出来的数据在哪个分片,数据在那个节点往哪个节点建.
特殊语句自定义分片:

 /*!mycat: sql=select 1 from test */insert into t_user(id,name) select id,name from t_user2;
  • 1

读写分离
配置了Mycat读写分离后,默认查询都会从读节点获取数据,但是有些场景需要获取实时数据,如果从读节点获取数据可能因延时而无法实现实时,Mycat支持通过注解/balance/来强制从写节点查询数据:

a. 事务内的SQL,默认走写节点,以注解/*balance*/开头,则会根据schema.xml的dataHost标签属性的balance=“1”或“2”去获取节点 
  • 1
b. 非事务内的SQL,开启读写分离默认根据balance=“1”或“2”去获取,以注解/*balance*/开头则会走写节点解决部分已经开启读写分离,但是需要强一致性数据实时获取的场景走写节点
 /*balance*/ select a.* from customer a where a.company_id=1;
  • 1
  • 2

多表

 ShareJoin /*!mycat:catlet=demo.catlets.ShareJoin */ select a.*,b.id, b.name as tit from customer a,company b on a.company_id=b.id;
  • 1

读写分离数据源选择

/*!mycat:db_type=master*/ select * from travelrecord /*!mycat:db_type=slave*/ select * from travelrecord /*#mycat:db_type=master*/ select * from travelrecord /*#mycat:db_type=slave*/ select * from travelrecord
  • 1

多租户支持
通过注解方式在配置多个schema情况下,指定走哪个配置的schema。
web部分修改: a.在用户登录时,在线程变量(ThreadLocal)中记录租户的id b.修改jdbc的实现:在提交sql时,从ThreadLocal中获取租户id, 添加sql 注释,把租户的schema 放到注释中。例如:/!mycat : schema = test_01 / sql ;
在db前面建立proxy层,代理所有web过来的数据库请求。proxy层是用mycat实现的,web提交的sql过来时在注释中指定schema, proxy层根据指定的schema转发sql请求。

 /*!mycat : schema = test_01 */ sql ;

Leave a Reply

Your email address will not be published. Required fields are marked *