暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

腾讯云数据库TDSQL MySQL版开发指南(二)

原创 腾讯云数据库 2024-03-29
718

版本日期:2024.03(最新版本以腾讯云官网产品文档为准,本链接为2024年3月版)

版权声明

本文档著作权归腾讯云计算(北京)有限责任公司(以下简称“腾讯云”)单独所有,未经腾讯云事先书面许可,任何主体不得以任何方式或理由使用本文档,包括但不限于复制、修改、传播、公开、剽窃全部或部分本文档内容。

本文档及其所含内容均属腾讯云内部资料,并且仅供腾讯云指定的主体查看。如果您非经腾讯云授权而获得本文档的全部或部分内容,敬请予以删除,切勿以复制、披露、传播等任何方式使用本文档或其任何内容,亦请切勿依本文档或其任何内容而采取任何行动。

免责声明

本文档旨在向客户介绍本文档撰写时,腾讯云相关产品、服务的当时的整体概况,部分产品或服务在后续可能因技术调整或项目设计等任何原因,导致其服务内容、标准等有所调整。因此,本文档仅供参考,腾讯云不对其准确性、适用性或完整性等做任何保证。您所购买、使用的腾讯云产品、服务的种类、内容、服务标准等,应以您和腾讯云之间签署的合同约定为准,除非双方另有约定,否则,腾讯云对本文档内容不做任何明示或默示的承诺或保证。


连接数据库

客户端连接

TDSQL MySQL版 提供和 MySQL 兼容的连接方式,用户可通过 IP 地址、端口号以及用户名、密码连接 TDSQL MySQL版:

mysql -hxxx.xxx.xxx.xxx -Pxxxx -uxxx -pxxx -c

TDSQL MySQL版 不支持4.0以下的版本以及压缩协议,建议在使用客户端的时候增加-c选项,以便于使用某些高级功能。

PHP MySQLli 连接

PHP 需要开启 MySQLli 扩展连接数据库,具体 demo 如下:

header("Content-Type:text/html;charset=utf-8");

$host="10.10.10.10"; //实例的 proxy_host_ip

$user="test"; //实例用户

$pwd="test"; //实例用户密码

$db="aaa"; //数据库名

$port="15002"; //proxy_host 端口号

$sqltool=new MySQLli($host,$user,$pwd,$db,$port);

//其他必要代码

$sqltool->close();

echo "ok"."\n";

JDBC 连接

您也可以使用 JDBC 连接 TDSQL MySQL版,例如:

private final String USERNAME = "test"; 

private final String PASSWORD = "123456"; 

private final String DRIVER = "com.mysql.jdbc.Driver"; 

private final String URL = "jdbc:mysql://10.10.10.10:3306?userunicode=true&characterEncoding=utf8mb4"; 

private Connection connection; 

private PreparedStatement pstmt; 

private ResultSet resultSet; 

其他连接方式

您也可以选择其他兼容 MySQL 的连接方式,例如 navicat、odbc 等。

JOIN 和子查询

对于分布式实例,数据水平拆分在各个节点,为提高性能,建议优先优化表结构和 SQL,尽量使用不跨节点的方式。

推荐方式

多个分表,带有分表键相等的条件

MySQL > select * from test1 join test2 where test1.a=test2.a; 

+---+------+---------+---+------+---------------+

| a | b | c | a | d | e |

+---+------+---------+---+------+---------------+

| 1 | 2 | record1 | 1 | 3 | test2_record1 |

| 2 | 3 | record2 | 2 | 3 | test2_record2 |

+---+------+---------+---+------+---------------+

2 rows in set (0.00 sec)

MySQL > select * from test1 left join test2 on test1.a<test2.a where test1.a=1;

+---+------+---------+------+------+---------------+

| a | b | c | a | d | e |

+---+------+---------+------+------+---------------+

| 1 | 2 | record1 | 2 | 3 | test2_record2 |

+---+------+---------+------+------+---------------+

1 row in set (0.00 sec)

MySQL> select * from test1 where test1.a in (select a from test2); 

+---+------+---------+

| a | b | c |

+---+------+---------+

| 1 | 2 | record1 |

| 2 | 3 | record2 |

+---+------+---------+

2 rows in set (0.00 sec)

MySQL> select a, count(1) from test1 where exists (select * from test2 where test2.a=test1.a) group by a; 

+---+----------+

| a | count(1) |

+---+----------+

| 1 | 1 |

| 2 | 1 |

+---+----------+

2 rows in set (0.00 sec)

MySQL> select distinct count(1) from test1 where exists (select * from test2 where test2.a=test1.a) group by a; 

+----------+

| count(1) |

+----------+

| 1 |

+----------+

1 row in set (0.00 sec)

MySQL> select count(distinct a) from test1 where exists (select * from test2 where test2.a=test1.a); 

+-------------------+

| count(distinct a) |

+-------------------+

| 2 |

+-------------------+

1 row in set (0.00 sec)

均为单表

mysql> create table noshard_table ( a int, b int key);

Query OK, 0 rows affected (0.02 sec)

mysql> create table noshard_table_2 ( a int, b int key);

Query OK, 0 rows affected (0.00 sec)

mysql> select * from noshard_table,noshard_table_2;

Empty set (0.00 sec)

mysql> insert into noshard_table (a,b) values(1,2),(3,4);

Query OK, 2 rows affected (0.00 sec)

Records: 2 Duplicates: 0 Warnings: 0

mysql> insert into noshard_table_2 (a,b) values(10,20),(30,40);

Query OK, 2 rows affected (0.00 sec)

Records: 2 Duplicates: 0 Warnings: 0

mysql> select * from noshard_table,noshard_table_2;

+------+---+------+----+

| a | b | a | b |

+------+---+------+----+

| 1 | 2 | 10 | 20 |

| 3 | 4 | 10 | 20 |

| 1 | 2 | 30 | 40 |

| 3 | 4 | 30 | 40 |

+------+---+------+----+

4 rows in set (0.00 sec)

广播表

MySQL> create table global_test(a int key, b int)shardkey=noshardkey_allset;

Query OK, 0 rows affected (0.00 sec)

MySQL> insert into global_test(a, b) values(1,1),(2,2);

Query OK, 2 rows affected (0.00 sec)

MySQL> select * from test1, global_test;

+---+------+---------+---+------+

| a | b | c | a | b |

+---+------+---------+---+------+

| 1 | 2 | record1 | 1 | 1 |

| 2 | 3 | record2 | 1 | 1 |

| 1 | 2 | record1 | 2 | 2 |

| 2 | 3 | record2 | 2 | 2 |

+---+------+---------+---+------+

4 rows in set (0.00 sec)

子查询带有 shardkey 的 derived table

mysql> select a from (select * from test1 where a=1) as t;

+---+

| a |

+---+

| 1 |

+---+

1 row in set (0.00 sec)

子查询时不指定 shardkey,也可查询结果。

复杂 SQL

对于不能满足推荐方式的 SQL,由于需要做跨节点的数据交互,所以性能会差一些。 包括:

  • 包含子查询的查询。
  • 多表的 join 查询,且参与查询的各表的分区字段(shardkey)不相等,或者同时涉及不同类型的表,例如单表和分表。

对于这类复杂查询,通过条件下推,将真正参与查询的数据从后端数据库中抽取出来,存放在本地的临时表中,然后对临时表中的数据进行计算。

因此用户需要明确指定参与查询的表的条件,避免因为抽取大量数据而性能受损。

mysql> create table test1 ( a int key, b int, c char(20) ) shardkey=a;

Query OK, 0 rows affected (1.56 sec)

mysql> create table test2 ( a int key, d int, e char(20) ) shardkey=a;

Query OK, 0 rows affected (1.46 sec)

mysql> insert into test1 (a,b,c) values(1,2,"record1"),(2,3,"record2");

Query OK, 2 rows affected (0.02 sec)

mysql> insert into test2 (a,d,e) values(1,3,"test2_record1"),(2,3,"test2_record2");

Query OK, 2 rows affected (0.02 sec)

mysql> select * from test1 join test2 on test1.b=test2.d;

+---+------+---------+---+------+---------------+

| a | b | c | a | d | e |

+---+------+---------+---+------+---------------+

| 2 | 3 | record2 | 1 | 3 | test2_record1 |

| 2 | 3 | record2 | 2 | 3 | test2_record2 |

+---+------+---------+---+------+---------------+

2 rows in set (0.00 sec)

MySQL> select * from test1 where exists (select * from test2 where test2.a=test1.b);

+---+------+---------+

| a | b | c |

+---+------+---------+

| 1 | 2 | record1 |

+---+------+---------+

1 row in set (0.00 sec)

分布式实例还支持丰富的复杂 update/delete/insert 操作。

需要注意的是,这类查询是在与之对应的 select 基础上实现的,因此也需要将数据加载至网关临时表,建议用户尽量在查询中指定明确的查询条件,避免大量数据的加载带来性能损耗。 另外,网关在加载数据时默认不会对加载的数据进行上锁,这与官方的 MySQL 行为存在略微的差异;如需加锁可以通过修改 proxy 配置来实现。

MySQL [th]> update test1 set test1.c="record" where exists(select 1 from test2 where test1.b=test2.d);

Query OK, 1 row affected (0.00 sec)

MySQL [th]> update test1, test2 set test1.b=2 where test1.b=test2.d;

Query OK, 1 row affected (0.00 sec)

MySQL [th]> insert into test1 select cast(rand()*1024 as unsigned), d, e from test2;

Query OK, 2 rows affected (0.00 sec)

MySQL [th]> delete from test1 where b in (select b from test2);

Query OK, 6 rows affected (0.00 sec)

MySQL [th]> delete from test2.* using test1 right join test2 on test1.a=test2.a where test1.a is null;

Query OK, 2 rows affected (0.00 sec)

Sequence

关键字 Sequence 语法和 MariaDB/Oracle 兼容,但是保证分布式全局递增且唯一,具体使用如下:

  • TDSQL_INCREMENT BY 设置序列步长,正数表示升序序列,负数表示降序序列。设置值必须在序列最大值与最小值之间,缺省则默认1。
  • START WITH 设置序列起始值,缺省则默认序列最小值。
  • TDSQL_MAXVALUE 和 TDSQL_NOMAXVALUE 设置序列最大值,TDSQL_MAXVALUE 缺省或者设置TDSQL_NOMAXVALUE 时,递增序列最大值为9223372036854775807,递减序列最大值为-1。
  • TDSQL_MINVALUE 和 TDSQL_NOMINVALUE 设置序列最小 TDSQL_MINVALUE 缺省或者设置TDSQL_NOMINVALUE 时,递增序列最小值为1,递减序列最小值为-9223372036854775808。
  • TDSQL_CYCLE 和 TDSQL_NOCYCLE 设置序列是否循环,缺省默认不循环。
  • TDSQL_CACHE 和 TDSQL_NOCACHE 设置缓存序列数量,缺省默认缓存20。
  • TDSQL_ORDER 和 TDSQL_NOORDER。设置序列对于并发请求保证完全有序。TDSQL 目前仅支持无序。

  • 在 TDSQL MySQL 版 分布式数据库当中使用 Sequence 时,须在该关键字前面加 tdsql_ 前缀,且要求 proxy 版本最低为1.19.5-M-V2.0R745D005;可通过数据库管理语句 /*Proxy*/show status 查询 proxy 版本,若 proxy 版本较老可以 提交工单 进行升级。
  • 目前 Sequence 为保证分布式全局数值唯一,导致性能较差,主要适用于并发不高的场景。

创建序列需要 CREATE SEQUENCE 系统权限。序列的创建语法如下:

  CREATE TDSQL_SEQUENCE 序列名

  [START WITH n]

  [{TDSQL_MINVALUE | TDSQL_MAXVALUE} n | TDSQL_NOMINVALUE | TDSQL_NOMAXVALUE]

  [TDSQL_INCREMENT BY n]

  [{TDSQL_CYCLE|TDSQL_NOCYCLE}]

  [{TDSQL_CACHE|TDSQL_NOCACHE} n]

创建 Sequence

create tdsql_sequence test.s1 start with 12 tdsql_minvalue 10 maxvalue 50000 tdsql_increment by 5 tdsql_nocycle

create tdsql_sequence test.s2 start with 12 tdsql_minvalue 10 maxvalue 50000 tdsql_increment by 1 tdsql_cycle

  • 以上 SQL 语句包含开始值、最小值、最大值、步长、缓存大小及是否回绕6个参数。
  • 参数默认值,开始值(1)、最小值(1)、最大值(LONGLONG_MAX-1)、步长(1)、是否回绕(0)。

删除 Sequence

drop tdsql_sequence test.s1

查询 Sequence

show create tdsql_sequence test.s1

使用 Sequence

使用 Sequence 获取下一个数值

select tdsql_nextval(test.s2)

select next value for test.s2

mysql> select tdsql_nextval(test.s1);

+----+

| 12 |

+----+

| 12 |

+----+

1 row in set (0.18 sec)

mysql> select tdsql_nextval(test.s2);

+----+

| 12 |

+----+

| 12 |

+----+

1 row in set (0.13 sec)

mysql> select tdsql_nextval(test.s1);

+----+

| 17 |

+----+

| 17 |

+----+

1 row in set (0.01 sec)

mysql> select tdsql_nextval(test.s2);

+----+

| 13 |

+----+

| 13 |

+----+

1 row in set (0.00 sec)

mysql> select next value for test.s1;

+----+

| 22 |

+----+

| 22 |

+----+

1 row in set (0.01 sec)

nextval 可以用在 insert 等地方

mysql> select * from test.t1;

+----+------+

| a | b |

+----+------+

| 11 | 2 |

+----+------+

1 row in set (0.00 sec)

mysql> insert into test.t1(a,b) values(tdsql_nextval(test.s2),3);

Query OK, 1 row affected (0.01 sec)

mysql> select * from test.t1;

+----+------+

| a | b |

+----+------+

| 11 | 2 |

| 14 | 3 |

+----+------+

2 rows in set (0.00 sec)

如需获取上一次的值以连接相关数据:如果之前没有用 nextval 命令获取过数据,数值将返回为0。

select tdsql_lastval(test.s1)

select tdsql_previous value for test.s1;

mysql> select tdsql_lastval(test.s1);

+----+

| 22 |

+----+

| 22 |

+----+

1 row in set (0.00 sec)

mysql> select tdsql_previous value for test.s1;

+----+

| 22 |

+----+

| 22 |

+----+

1 row in set (0.00 sec)

设置下一个序列数值,只能比当前数值大,否则将返回数值为0。

select tdsql_setval(test.s2,1000,bool use) // use 默认为1,表示1000这个值用过了,下一次不包含1000,如果为0,则下一个从1000开始。

设置下一个序列数值时,如果比当前数值小,则设置不生效。

mysql> select tdsql_nextval(test.s2);

+----+

| 15 |

+----+

| 15 |

+----+

1 row in set (0.01 sec)

mysql> select tdsql_setval(test.s2,10);

+---+

| 0 |

+---+

| 0 |

+---+

1 row in set (0.03 sec)

mysql> select tdsql_nextval(test.s2);

+----+

| 16 |

+----+

| 16 |

+----+

如果比当前数值大,成功返回当前设置的值。

mysql> select tdsql_setval(test.s2,20);

+----+

| 20 |

+----+

| 20 |

+----+

1 row in set (0.02 sec)

mysql> select tdsql_nextval(test.s2);

+----+

| 21 |

+----+

| 21 |

+----+

1 row in set (0.01 sec)

强制设置下一个序列数值,允许设置比当前数值小的值。

select tdsql_resetval(test.s2,1000)

如果强制设置成功,返回当前设置的值,下一个序列数值从该值开始。

mysql> select tdsql_resetval(test.s2,14);

+----+

| 14 |

+----+

| 14 |

+----+

1 row in set (0.00 sec)

mysql> select tdsql_nextval(test.s2);

+----+

| 14 |

+----+

| 14 |

+----+

1 row in set (0.01 sec)

需要注意,Sequence 的部分关键字以 TDSQL_ 前缀开始:

若已打开 oldstyle 配置项,proxy 将兼容标准 Sequence 关键字,即关键字前可不添加TDSQL_ 前缀。

TDSQL_CYCLE

TDSQL_INCREMENT

TDSQL_LASTVAL 

TDSQL_MINVALUE 

TDSQL_NEXTVAL 

TDSQL_NOCACHE 

TDSQL_NOCYCLE 

TDSQL_NOMAXVALUE

TDSQL_NOMINVALUE

TDSQL_PREVIOUS 

TDSQL_RESTART 

TDSQL_REUSE 

TDSQL_SEQUENCE 

TDSQL_SETVAL 


「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论