Move the docs folder
This commit is contained in:
committed by
LINxiansheng
parent
7c6dcc6712
commit
d42f317422
@ -0,0 +1,6 @@
|
||||
关于 OceanBase 数据库开发者
|
||||
========================================
|
||||
|
||||
|
||||
|
||||
OceanBase 数据库开发人员负责创建或维护使用 OceanBase 相关产品的应用程序的数据库组件。OceanBase 数据库开发人员要么基于 OceanBase 开发新应用程序,要么将现有应用程序转换到 OceanBase 数据库环境中运行。
|
||||
@ -0,0 +1,8 @@
|
||||
关于本文档
|
||||
=====
|
||||
|
||||
本文档是为应用程序开发人员准备的 OceanBase 数据库文档的入门文档,它包含如下内容:
|
||||
|
||||
* 解释了 OceanBase 数据库开发背后的基本概念。
|
||||
* 展示并示例如何使用 OceanBase 的 MySQL 租户。
|
||||
|
||||
@ -0,0 +1,55 @@
|
||||
OceanBase 集群简介
|
||||
===================================
|
||||
|
||||
|
||||
|
||||
OceanBase 集群通常由运维人员管理,OceanBase 数据库集群将多个机器资源聚合成一个大的资源池之后再分配给不同租户(也称为实例)。OceanBase 不同租户之间彼此资源隔离,数据访问也是完全隔离的。
|
||||
|
||||
相关概念
|
||||
-------------
|
||||
|
||||
* OceanBase 集群的多副本架构
|
||||
|
||||
OceanBase 集群通常是三副本架构,少数场景可能会使用五副本。在三副本架构下,OceanBase 集群的节点数通常是三的倍数,集群节点会分为三个区域( Zone )。每个 Zone 的节点数通常保持相等,可以有1或多个节点。每个租户的数据也会分布在这三个 Zone 里,但不一定用尽每个 Zone 的所有节点,这取决于租户资源池属性的设置,由运维人员确定。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- -->
|
||||
|
||||
* OceanBase 集群的高可用能力
|
||||
|
||||
在三副本架构的 OceanBase 集群里,默认每份数据也有三份,分布在三个 Zone 中。这个数据表示的最小粒度叫分区。分区是表的子集,有关表的详细介绍请参见"创建表"章节 。OceanBase 集群的高可用的粒度就是分区,故障切换时的切换粒度也是分区。所以 OceanBase 节点故障时,应用可能是部分数据访问中断,并且会自动恢复访问。当故障节点内部分区非常多的时候,不同数据的恢复时间可能有细微的区别。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- -->
|
||||
|
||||
* OceanBase 的参数设置
|
||||
|
||||
OceanBase 支持通过参数(parameters)来影响集群和租户的功能、性能等。集群参数通常是在sys租户里设置,影响范围是整个集群,也包括集群里的租户。同时 OceanBase 针对租户也支持用变量(variables)来设置,影响范围是当前租户。
|
||||
|
||||
有关 OceanBase 常用的参数和变量请参考附录"OceanBase常用参数变量"。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- -->
|
||||
|
||||
* OceanBase 的写特点
|
||||
|
||||
当第一次向 OceanBase 的表中修改一笔数据时,OceanBase 会将该记录所在的块读入到内存中的一块只读区域中,然后在另外一块内存区域记录一笔修改记录。前者称为基线数据(SSTable),后者称为增量数据(MemTable),对应的内存称为增量内存。无论是插入、更新还是删除,OceanBase 都不会对 SSTable 进行修改,而是在原来的增量基础上追加(append)新的增量。这种设计使得 OceanBase 的写产生的 IO 非常少,性能很好。增量会一直在内存中不落盘,直到增量内存使用率超过一定阈值后触发冻结事件(Minor freeze),此时会生成新的 MemTable 供后续写入。老的增量 MemTable 会直接转储到磁盘上,或者直接跟磁盘里的 SSTable 进行合并(Major freeze)。
|
||||
|
||||
|
||||
|
||||
|
||||
**说明**
|
||||
|
||||
|
||||
|
||||
默认设置下,OceanBase 的增量和基线数据合并操作会在凌晨2点进行。由于 OceanBase 的内存写这个特点,开发和运维都需要关注自己的业务对数据库内存的消耗速度。如果增量内存写入速度远快于增量转储或合并释放内存的速度,增量内存有可能会消耗完导致后续写入报错。此时需要在应用或数据库端做内存写入速度限流操作,或者对数据库实例内存进行扩容。
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
OceanBase 租户简介
|
||||
===================================
|
||||
|
||||
|
||||
|
||||
租户目前有两种兼容模式,兼容 Oracle 或 MySQL,不同兼容模式的实例再按照相应的方式把相关的业务数据组织在一起。在 Oracle 兼容模式的租户下,这个组织方式叫模式(Schema);在 MySQL 兼容模式的租户下,这个组织方式叫数据库(DataBase)。当通过用户名、密码和 IP 连接 OceanBase 数据库时,您指定了相应的 OceanBase 集群名、租户名以及租户下的模式或者用户,以表明您是这些数据的所有者。在 Oracle 租户下,用户名和用户访问的模式名默认是相同的;在 MySQL 租户下,用户名和用户访问的数据库名默认可以不同。
|
||||
|
||||
OceanBase 数据库是支持多租户的,这里租户的概念类似于传统数据库的数据库实例。租户下可以建立数据库,在租户的数据库下可以建立表。
|
||||
|
||||
OceanBase 集群默认会有一个租户(兼容 MySQL),租户名是 **sys** ,里面存储的是集群元数据信息。sys 租户主要是集群内部管理需要,运维人员经常访问使用。sys 租户下不建议建表存储数据。OceanBase 集群除去 sys 租户占用的资源外都是未分配资源,留给业务租户使用。业务使用 OceanBase 数据库需要先创建一个业务租户。
|
||||
|
||||
创建租户的工作通常由数据库运维人员完成,可以在 sys 租户下直接创建租户,或者在 OCP(OceanBase Cloud Platform)中"新建实例"。
|
||||
**说明**
|
||||
|
||||
|
||||
|
||||
租户和实例是同义词。
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
OceanBase 的 MySQL 租户数据库对象
|
||||
==============================================
|
||||
|
||||
|
||||
|
||||
在 MySQL 租户下,每个数据库对象只属于一个数据库(DataBase),数据库名跟用户名没有直接关系,每个用户可以访问的数据库跟用户的权限有关系。用户访问数据库对象通常还需要切换到该数据库下,否则对象前面就要带上数据库对象所属的数据库名。
|
||||
|
||||
数据库下支持的数据库对象有:
|
||||
|
||||
* 表(Tables)
|
||||
|
||||
表是OceanBase数据库最基本的存储单元,容纳用户的数据。每个表包含很多行(rows),每行表示独立的数据记录(records)。每一行包含很多列(columns),每列表示记录的一个字段(fields)。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- -->
|
||||
|
||||
* 索引(Indexes 或 Keys)
|
||||
|
||||
* 视图(Views)
|
||||
|
||||
* 存储的子程序
|
||||
|
||||
存储的子程序是存储在数据库中的过程和函数,它们可以从访问数据库的客户端应用程序中调用。
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
关于 MySQL 客户端(mysql)
|
||||
========================================
|
||||
|
||||
|
||||
|
||||
mysql 命令是 MySQL 数据库命令行下客户端,需要单独安装。OceanBase 的租户兼容类型支持 MySQL 和 Oracle。当租户兼容 MySQL 时,可以通过 mysql 命令连接。
|
||||
**说明**
|
||||
|
||||
|
||||
|
||||
OceanBase 当前版本支持的 MySQL 客户端版本只有:V5.5、V5.6、V5.7。
|
||||
|
||||
连接上 OceanBase 数据库后,在 mysql 命令行环境里,可以运行一些 mysql 运维命令、SQL 语句来执行下面这些任务:
|
||||
|
||||
* 计算、存储和打印查询结果
|
||||
|
||||
* 创建数据库对象、检查和修改对象定义
|
||||
|
||||
* 执行数据库管理,修改参数等
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
关于 OceanBase 客户端(obclient)
|
||||
===============================================
|
||||
|
||||
|
||||
|
||||
obclient 是一个交互式和批处理查询工具,需要单独安装。它是一个命令行用户界面,在连接到数据库时充当客户端,支持 OceanBase 的 Oracle 租户和 MySQL 租户。
|
||||
|
||||
obclient 运行时需要指定 OceanBase 租户的连接信息,具体方法参考"通过obclient 连接 OceanBase数据库"章节。
|
||||
|
||||
连接上 OceanBase 数据库后,在 obclient 里,可以运行一些 obclient 命令(包含常用的 MySQL 命令)、SQL语句来执行下面这些任务:
|
||||
|
||||
* 计算、存储和打印查询结果
|
||||
|
||||
|
||||
|
||||
* 创建数据库对象、检查和修改对象定义
|
||||
|
||||
|
||||
|
||||
* 开发和运行批处理脚本
|
||||
|
||||
|
||||
|
||||
* 执行数据库管理,修改参数等
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
关于结构化查询语言
|
||||
==============================
|
||||
|
||||
|
||||
|
||||
结构化查询语言(SQL)是一种基于集合和关系代数理论的高级计算机语言,所有程序和用户都可以使用 SQL 访问 OceanBase 数据库中的数据。
|
||||
|
||||
SQL 是一种声明性的或非程序性的语言,也就是说,它描述了该做什么,而不是如何做。您指定所需的结果集(例如,当前员工的名称),但不指定如何获取它。不过通过一些 SQL 写法或 SQL Hint 可以对 SQL 获取数据的执行计划施加一定的影响,从而提升 SQL 的执行性能。
|
||||
@ -0,0 +1,8 @@
|
||||
Java 数据库连接驱动(JDBC)
|
||||
=======================================
|
||||
|
||||
|
||||
|
||||
Java 数据库连接(JDBC)是一种 API,它使 Java 能够将 SQL 语句发送到对象关系数据库,如 Oracle、MySQL 数据库。JDBC 支持为 Java 暴露 SQL 数据类型,并快速访问 SQL 数据。
|
||||
|
||||
OceanBase 的 MySQL 租户兼容 MySQL 的连接协议,使用标准的 MySQL JDBC(建议使用 mysql-connector-java-5.1.47 版本)可以连接 OceanBase 的 MySQL 租户。但 JDBC 默认不支持 Oracle 租户的连接协议。
|
||||
@ -0,0 +1,18 @@
|
||||
OceanBase Java数据库连接驱动
|
||||
==========================================
|
||||
|
||||
|
||||
|
||||
OceanBase 实现了自己的 JDBC 驱动,使 Java 能够将 SQL 语句发送到 OceanBase 的 MySQL 租户和 Oracle 租户。OceanBase JDBC 支持为 Java 暴露 SQL 数据类型、PL/SQL 对象,并快速访问SQL数据。
|
||||
|
||||
OceanBase JDBC 驱动文件名为:oceanbase-client-\[版本号\].jar,可以联系 OceanBase 技术支持工程师获取。
|
||||
**说明**
|
||||
|
||||
|
||||
|
||||
OceanBase 数据库驱动文件1.0 相关版本的类名为:com.alipay.oceanbase.obproxy.mysql.jdbc.Driver 。
|
||||
|
||||
OceanBase 数据库驱动文件从1.1.0 后类名更改为:com.alipay.oceanbase.jdbc.Driver ,原类名会保留,但是不推荐使用。
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
关于示例数据库 TPCC
|
||||
=================================
|
||||
|
||||
|
||||
|
||||
示例数据库安装脚本可以安装在 OceanBase 的 Oracle 租户或者 MySQL 租户上。如果是 Oracle 租户,需要先创建一个用户和 schema,然后登录这个用户执行脚本;如果是 MySQL 租户,需要先创建一个 DataBase,然后进入这个数据库下执行脚本。
|
||||
|
||||
脚本分为两部分:
|
||||
|
||||
* 数据库建表语句,文件名"create_tables_mysql.sql",创建业务表和视图。
|
||||
|
||||
* 数据初始化语句,文件名"init_data.sql",初始化相关表数据。
|
||||
|
||||
|
||||
|
||||
|
||||
示例数据库的业务是由国际事务性能委员会(TPC)制定的 TPC-C 标准,定义了商品销售的订单创建和订单支付等的基准测试标准,是数据库联机交易处理系统(OLTP)的权威基准测试标准。
|
||||
|
||||
示例数据库可以从 https://github.com/obpilot/ob-samples免费下载。
|
||||
@ -0,0 +1,103 @@
|
||||
通过 MySQL 客户端连接 OceanBase 租户
|
||||
================================================
|
||||
|
||||
|
||||
|
||||
需要使用 OceanBase 的 MySQL 租户时,可以使用 MySQL 客户端连接该租户。
|
||||
|
||||
### 操作步骤如下:
|
||||
|
||||
1. 打开一
|
||||
|
||||
个命令行终端,确保环境变量 PATH 包含了 MySQL 客户端命令所在目录。
|
||||
|
||||
|
||||
2. 参照下面格式提供 MySQL 的运行参数:
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
$mysql -h192.168.1.101 -uroot@obmysql#obdemo -P2883 -pabcABC123 -c -A oceanbase
|
||||
```
|
||||
|
||||
|
||||
**说明**
|
||||
|
||||
|
||||
|
||||
* -h:提供 OceanBase 数据库连接 IP,通常是一个 OBProxy 地址。
|
||||
|
||||
|
||||
|
||||
* -u:提供租户的连接账户,格式有两种:用户名@租户名#集群名 或者 集群名:租户名:用户名 。MySQL 租户的管理员用户名默认是 root 。
|
||||
|
||||
|
||||
|
||||
* -P:提供 OceanBase 数据库连接端口,也是 OBProxy 的监听端口,默认是2883,可以自定义。
|
||||
|
||||
|
||||
|
||||
* -p:提供账户密码,为了安全可以不提供,改为在后面提示符下输入,密码文本不可见。
|
||||
|
||||
|
||||
|
||||
* -c:表示在 MySQL 运行环境中不要忽略注释。
|
||||
|
||||
|
||||
|
||||
* -A:表示在 MySQL 连接数据库时不自动获取统计信息。
|
||||
|
||||
|
||||
|
||||
* oceanbase:访问的数据库名,可以改为业务数据库。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
3. 连接成功后,默认会有命令行提示符:
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
MySQL [oceanbase]>
|
||||
```
|
||||
|
||||
|
||||
|
||||
4. 如果要退出 OceanBase 命令行,输入 exit 后回车,或者按快捷键 ctrl + d。
|
||||
|
||||
|
||||
|
||||
|
||||
### 如下示例为通过 MySQL 客户端连接 OceanBase 的 MySQL 租户。
|
||||
|
||||
```javascript
|
||||
$mysql -h192.168.1.101 -uroot@obmysql#obdemo -P2883 -pabcABC123 -c -A oceanbase
|
||||
Welcome to the MariaDB monitor. Commands end with ; or \g.
|
||||
Your MySQL connection id is 62488
|
||||
Server version: 5.6.25 OceanBase 2.2.20 (...) (Built Aug 10 2019 15:27:33)
|
||||
|
||||
<...省略...>
|
||||
|
||||
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
|
||||
|
||||
MySQL [oceanbase]> show databases;
|
||||
+--------------------+
|
||||
| Database |
|
||||
+--------------------+
|
||||
| oceanbase |
|
||||
| information_schema |
|
||||
| mysql |
|
||||
| test |
|
||||
+--------------------+
|
||||
4 rows in set (0.00 sec)
|
||||
|
||||
MySQL [oceanbase]> exit
|
||||
Bye
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,106 @@
|
||||
通过 obclient 连接 OceanBase 租户
|
||||
================================================
|
||||
|
||||
|
||||
|
||||
obclient 是 OceanBase 专用的命令行客户端工具,通过 obclient 您可以连接 OceanBase 的 MySQL 和 ORACLE 租户。
|
||||
|
||||
### 操作步骤如下:
|
||||
|
||||
1. 打开一个命令行终端。
|
||||
|
||||
|
||||
|
||||
2. 参照下面格式提供 obclient 的运行参数:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
$ obclient -h192.168.1.101 -uroot@obmysql#obdemo -P2883 -pabcABC123 -c -A oceanbase
|
||||
```
|
||||
|
||||
|
||||
**说明**
|
||||
|
||||
|
||||
|
||||
* -h:提供 OceanBase数据库连接的IP,通常是一个 OBProxy 地址。
|
||||
|
||||
|
||||
|
||||
* -u:提供租户的连接帐户,格式有两种:"用户名@租户名#集群名"或者"集群名:租户名:用户名"。Oracle 租户的管理员用户名默认是sys。
|
||||
|
||||
|
||||
|
||||
* -P:提供 OceanBase 数据库连接端口,也是 OBProxy 的监听端口,默认是 2883,可以自定义。
|
||||
|
||||
|
||||
|
||||
* -p:提供帐户密码。为了安全可以不提供,改为在后面提示符下输入,密码文本不可见。
|
||||
|
||||
|
||||
|
||||
* -c:表示在将 SQL 语句中的注释发往数据库端。
|
||||
|
||||
|
||||
|
||||
* -A:表示在连接数据库时不去获取全部表信息,可以使登录数据库速度最快。
|
||||
|
||||
|
||||
|
||||
* oceanbase:访问的数据库名,可以改为业务数据库
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
3. 连接成功后,默认会有如下命令行提示符。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient>
|
||||
```
|
||||
|
||||
|
||||
|
||||
4. 如果要退出 OceanBase 命令行,输入 exit 后回车,或者按快捷键 ctrl + d。
|
||||
|
||||
以下示例为通过 obclient 连接 OceanBase 的 MySQL 租户。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
$obclient -h192.168.1.101 -uroot@obmysql#obdemo -P2883 -pabcABC123 -c -A oceanbase
|
||||
obclient: [Warning] Using a password on the command line interface can be insecure.
|
||||
Welcome to the OceanBase monitor. Commands end with ; or \g.
|
||||
Your OceanBase connection id is 64621
|
||||
Server version: 5.6.25 OceanBase 2.2.20 (...) (Built Aug 10 2019 15:27:33)
|
||||
|
||||
<省略>
|
||||
|
||||
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
|
||||
|
||||
obclient> select sysdate();
|
||||
+---------------------+
|
||||
| sysdate() |
|
||||
+---------------------+
|
||||
| 2020-04-01 21:53:22 |
|
||||
+---------------------+
|
||||
1 row in set (0.00 sec)
|
||||
|
||||
obclient> exit
|
||||
Bye
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
通过 ODC 连接 OceanBase 数据库
|
||||
============================================
|
||||
|
||||
|
||||
|
||||
操作步骤
|
||||
-------------
|
||||
|
||||
1. 登录ODC。ODC第一次运行时,加载时间会比较长,请耐心等待。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
2. ODC启动完成后,会提示创建连接。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
3. 点击 **新建连接** ,在创建连接页面选择 **连接模式** 为 MySQL,在页面中输入 **连接名称** 、 **主机名** 、 **端口** 、 **集群** 、 **租户** 、 **数据库用户名** 和 **数据库密码** ,点击 **保存** ,如果可以保存成功,说明连接数据库成功。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- -->
|
||||
|
||||
4. 连接创建成功后,会显示如下界面,双击该连接面板就可以打开连接。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,149 @@
|
||||
创建 OceanBase 示例数据库 TPCC
|
||||
============================================
|
||||
|
||||
|
||||
|
||||
默认情况 OceanBase 没有创建示例数据库 TPCC,需要手动创建。示例数据库必须在业务租户下创建。有关示例数据库介绍请参考 [关于示例数据库 TPCC](../../7.developer-guide-1/1.preface-2/4.about-the-sample-database-tpcc-2.md)。
|
||||
|
||||
租户创建好后,需要创建相应的 DataBase 来存放示例数据库的对象,还要分配相应的用户和访问权限。
|
||||
|
||||
示例
|
||||
-----------
|
||||
|
||||
1. 通过 obclient 连接 MySQL 租户。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
#obclient -h192.168.1.101 -uroot@obmysql#obdemo -P2883 -pabcABC123 -A oceanbase
|
||||
obclient: [Warning] Using a password on the command line interface can be insecure.
|
||||
Welcome to the OceanBase monitor. Commands end with ; or \g.
|
||||
Your OceanBase connection id is 57981
|
||||
Server version: 5.6.25 OceanBase 2.2.20 (...) (Built Aug 10 2019 15:27:33)
|
||||
|
||||
<...省略...>
|
||||
|
||||
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
|
||||
```
|
||||
|
||||
|
||||
|
||||
2. 创建一个数据库。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> create database tpccdb;
|
||||
Query OK, 1 row affected (0.02 sec)
|
||||
obclient> show databases;
|
||||
+--------------------+
|
||||
| Database |
|
||||
+--------------------+
|
||||
| oceanbase |
|
||||
| information_schema |
|
||||
| mysql |
|
||||
| test |
|
||||
| tpccdb |
|
||||
+--------------------+
|
||||
5 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
3. 创建一个用户并赋权。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> grant all privileges on tpccdb.* to tpcc identified by '123456';
|
||||
Query OK, 0 rows affected (0.02 sec)
|
||||
|
||||
obclient> show grants for tpcc;
|
||||
+----------------------------------------------+
|
||||
| Grants for tpcc@% |
|
||||
+----------------------------------------------+
|
||||
| GRANT USAGE ON *.* TO 'tpcc' |
|
||||
| GRANT SELECT ON `oceanbase`.* TO 'tpcc' |
|
||||
| GRANT ALL PRIVILEGES ON `tpccdb`.* TO 'tpcc' |
|
||||
+----------------------------------------------+
|
||||
3 rows in set (0.01 sec)
|
||||
|
||||
obclient>
|
||||
```
|
||||
|
||||
|
||||
|
||||
4. 创建数据库对象
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> use tpccdb;
|
||||
Database changed
|
||||
obclient> source create_mysql_tables.sql
|
||||
Query OK, 0 rows affected (0.01 sec)
|
||||
<...省略...>
|
||||
Query OK, 0 rows affected (0.01 sec)
|
||||
+------------------+
|
||||
| Tables_in_tpccdb |
|
||||
+------------------+
|
||||
| cust |
|
||||
| dist |
|
||||
| hist |
|
||||
| item |
|
||||
| load_hist |
|
||||
| load_proc |
|
||||
| nord |
|
||||
| ordl |
|
||||
| ordr |
|
||||
| stock_item |
|
||||
| stok |
|
||||
| ware |
|
||||
+------------------+
|
||||
12 rows in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
5. 初始化表数据。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> source init_data.sql
|
||||
Query OK, 0 rows affected (0.01 sec)
|
||||
<...省略...>
|
||||
Query OK, 0 rows affected (0.01 sec)
|
||||
+------------+----------+
|
||||
| table_name | rows_cnt |
|
||||
+------------+----------+
|
||||
| WARE | 2 |
|
||||
| DIST | 20 |
|
||||
| NORD | 40 |
|
||||
| ORDR | 60 |
|
||||
| HIST | 240 |
|
||||
| ITEM | 622 |
|
||||
| ORDL | 626 |
|
||||
| CUST | 1040 |
|
||||
| STOK | 1244 |
|
||||
+------------+----------+
|
||||
9 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,133 @@
|
||||
通过 obclient 探索 OceanBase MySQL 租户
|
||||
======================================================
|
||||
|
||||
|
||||
|
||||
在 obclient 命令行环境里,可以通过一些命令或者 SQL 查看数据库对象或者表属性和数据。
|
||||
|
||||
示例
|
||||
-----------
|
||||
|
||||
* 通过 obclient 查看 MySQL 租户的数据库对象。
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
#obclient -h192.168.1.101 -utpcc@obmysql#obdemo -P2883 -p123456 -A tpccdb
|
||||
|
||||
obclient> show tables;
|
||||
+------------------+
|
||||
| Tables_in_tpccdb |
|
||||
+------------------+
|
||||
| cust |
|
||||
| dist |
|
||||
| hist |
|
||||
| item |
|
||||
| load_hist |
|
||||
| load_proc |
|
||||
| nord |
|
||||
| ordl |
|
||||
| ordr |
|
||||
| stock_item |
|
||||
| stok |
|
||||
| ware |
|
||||
+------------------+
|
||||
12 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 通过 obclient 查看 MySQL 租户的表属性
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> desc ordl;
|
||||
+----------------+--------------+------+-----+---------+-------+
|
||||
| Field | Type | Null | Key | Default | Extra |
|
||||
+----------------+--------------+------+-----+---------+-------+
|
||||
| ol_w_id | int(11) | NO | PRI | NULL | |
|
||||
| ol_d_id | int(11) | NO | PRI | NULL | |
|
||||
| ol_o_id | int(11) | NO | PRI | NULL | |
|
||||
| ol_number | int(11) | NO | PRI | NULL | |
|
||||
| ol_delivery_d | date | YES | | NULL | |
|
||||
| ol_amount | decimal(6,2) | YES | | NULL | |
|
||||
| ol_i_id | int(11) | YES | | NULL | |
|
||||
| ol_supply_w_id | int(11) | YES | | NULL | |
|
||||
| ol_quantity | int(11) | YES | | NULL | |
|
||||
| ol_dist_info | char(24) | YES | | NULL | |
|
||||
+----------------+--------------+------+-----+---------+-------+
|
||||
10 rows in set (0.01 sec)
|
||||
|
||||
obclient> show create table ordl\G
|
||||
*************************** 1. row ***************************
|
||||
Table: ordl
|
||||
Create Table: CREATE TABLE `ordl` (
|
||||
`ol_w_id` int(11) NOT NULL,
|
||||
`ol_d_id` int(11) NOT NULL,
|
||||
`ol_o_id` int(11) NOT NULL,
|
||||
`ol_number` int(11) NOT NULL,
|
||||
`ol_delivery_d` date DEFAULT NULL,
|
||||
`ol_amount` decimal(6,2) DEFAULT NULL,
|
||||
`ol_i_id` int(11) DEFAULT NULL,
|
||||
`ol_supply_w_id` int(11) DEFAULT NULL,
|
||||
`ol_quantity` int(11) DEFAULT NULL,
|
||||
`ol_dist_info` char(24) DEFAULT NULL,
|
||||
PRIMARY KEY (`ol_w_id`, `ol_d_id`, `ol_o_id`, `ol_number`)
|
||||
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.0' REPLICA_NUM = 1 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 10 TABLEGROUP = 'tpcc_group'
|
||||
partition by hash(ol_w_id) partitions 6
|
||||
|
||||
1 row in set (0.02 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 通过 obclient 查看 MySQL 租户的表数据
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> select * from ware;
|
||||
+------+---------+--------+------------+---------------------+----------------------+--------------+---------+-----------+
|
||||
| w_id | w_ytd | w_tax | w_name | w_street_1 | w_street_2 | w_city | w_state | w_zip |
|
||||
+------+---------+--------+------------+---------------------+----------------------+--------------+---------+-----------+
|
||||
| 1 | 1200.00 | 0.1868 | n1P4zYo8OH | jTNkXKWXOdh | lf9QXTXXGoF04IZBkCP7 | srRq15uvxe5 | GQ | 506811111 |
|
||||
| 2 | 1200.00 | 0.0862 | L6xwRsbDk | xEdT1jkENtbLwoI1Zb0 | NT0j4RCQ4OqrS | vlwzndw2FPrO | XR | 063311111 |
|
||||
+------+---------+--------+------------+---------------------+----------------------+--------------+---------+-----------+
|
||||
2 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
也可以使用参数'\\G'结尾按列展示每行数据
|
||||
|
||||
```javascript
|
||||
obclient> select * from ware\G
|
||||
*************************** 1. row ***************************
|
||||
w_id: 1
|
||||
w_ytd: 1200.00
|
||||
w_tax: 0.1868
|
||||
w_name: n1P4zYo8OH
|
||||
w_street_1: jTNkXKWXOdh
|
||||
w_street_2: lf9QXTXXGoF04IZBkCP7
|
||||
w_city: srRq15uvxe5
|
||||
w_state: GQ
|
||||
w_zip: 506811111
|
||||
*************************** 2. row ***************************
|
||||
w_id: 2
|
||||
w_ytd: 1200.00
|
||||
w_tax: 0.0862
|
||||
w_name: L6xwRsbDk
|
||||
w_street_1: xEdT1jkENtbLwoI1Zb0
|
||||
w_street_2: NT0j4RCQ4OqrS
|
||||
w_city: vlwzndw2FPrO
|
||||
w_state: XR
|
||||
w_zip: 063311111
|
||||
2 rows in set (0.01 sec)
|
||||
obclient>
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
关于查询语句
|
||||
===========================
|
||||
|
||||
|
||||
|
||||
一个查询,指一个SELECT SQL 语句,从一个或多个表或者视图里查询数据。
|
||||
|
||||
最简单的SQL语句格式是:
|
||||
|
||||
```javascript
|
||||
SELECT select_list FROM table_list
|
||||
```
|
||||
|
||||
|
||||
|
||||
* *select_list 指定的可以是后面 table_list 里的列,也可以是函数值、字符常量、计算变量等。*
|
||||
|
||||
* *table_list 指定的是包含所查数据的表或者视图。*
|
||||
|
||||
|
||||
|
||||
|
||||
上面是简单的查询语句格式,实际 table_list 还可以是一个子查询语句,同时还可以带上 where 条件以限定返回的结果要符合某种条件。
|
||||
|
||||
需要注意的是,在 MySQL 租户里,FROM table_list 可以没有,这样 select_list 就不是具体的列,而是常量或者变量值。如下面这个SQL:
|
||||
|
||||
```javascript
|
||||
$obclient -h192.168.1.101 -utpcc@obmysql#obdemo -P2883 -p123456 -A tpccdb
|
||||
obclient> select '中', 6*6 ;
|
||||
+-----+-----+
|
||||
| 中 | 6*6 |
|
||||
+-----+-----+
|
||||
| 中 | 36 |
|
||||
+-----+-----+
|
||||
1 row in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
关于查询语句的详细用法请参考《OceanBase SQL参考(MySQL模式)》。
|
||||
@ -0,0 +1,34 @@
|
||||
在 ODC 中运行查询
|
||||
================================
|
||||
|
||||
|
||||
|
||||
本节展示如何在 ODC 中通过 SQL 编辑器执行查询语句。
|
||||
|
||||
前提条件
|
||||
-------------------------
|
||||
|
||||
已在 ODC 中创建了到 OceanBase 数据库的连接。
|
||||
|
||||
操作步骤
|
||||
-------------------------
|
||||
|
||||
1. 进入 ODC,找到创建好的数据库连接,单击进入对应的数据库管理页面。
|
||||
|
||||
|
||||
|
||||
2. 单击上方导航栏中的 **工作台** ,在弹出的下拉菜单中选择 **SQL 窗口** 。
|
||||
|
||||
|
||||
|
||||
3. 在打开的 **SQL 窗口** 中输入查询语句,如果有多条查询语句,每个语句都以";"结尾。单击 **运行** 或 **运行当前语句** 按钮。
|
||||
|
||||
|
||||
|
||||
4. 下面 **结果** 栏会顺序显示查询结果,如果有多条查询语句,每条查询语句对应一个结果栏。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
查询表里符合特定搜索条件的数据
|
||||
====================================
|
||||
|
||||
|
||||
|
||||
当要查询满足特定搜索条件的数据时,给 SELECT 查询语句增加一个 WHERE 子句即可。SQL 语句格式如下:
|
||||
|
||||
```javascript
|
||||
SELECT select_list FROM table_list
|
||||
WHERE query_condition
|
||||
```
|
||||
|
||||
|
||||
|
||||
以"在 ODC 中运行查询"页面的图为例,查询 2 号仓库第 5 区的订单,SQL 语句如下:
|
||||
|
||||
```javascript
|
||||
SELECT * FROM ordr WHERE o_w_id=2 and o_d_id=5 ;
|
||||
```
|
||||
|
||||
|
||||
|
||||
在 ODC 中的查询结果如下图所示:
|
||||
|
||||
执行结果如下:
|
||||
|
||||
```unknow
|
||||
+--------+--------+------+--------+--------------+----------+-------------+------------+
|
||||
| o_w_id | o_d_id | o_id | o_c_id | o_carrier_id | o_ol_cnt | o_all_local | o_entry_d |
|
||||
+--------+--------+------+--------+--------------+----------+-------------+------------+
|
||||
| 2 | 5 | 2100 | 2100 | 5 | 12 | 1 | 2020-02-15 |
|
||||
| 2 | 5 | 2101 | 4 | NULL | 11 | 1 | 2020-02-15 |
|
||||
| 2 | 5 | 2102 | 440 | NULL | 7 | 1 | 2020-02-15 |
|
||||
+--------+--------+------+--------+--------------+----------+-------------+------------+
|
||||
3 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,91 @@
|
||||
对查询的结果进行排序
|
||||
===============================
|
||||
|
||||
|
||||
|
||||
查询结果返回的顺序可能是任意顺序,如果想要根据特定字段排序返回结果集,需要在 SELECT 语句最后面增加 ORDER BY 子句指定返回顺序。SQL 语句格式如下:
|
||||
|
||||
```javascript
|
||||
SELECT select_list FROM table_list
|
||||
WHERE query_condition
|
||||
ORDER BY column_list
|
||||
```
|
||||
|
||||
|
||||
|
||||
如查看 2 号仓库第 5 区的客户,按姓名排序,SQL 语句如下:
|
||||
|
||||
```javascript
|
||||
SELECT c_id, c_last,c_first,c_middle, c_w_id, c_d_id, c_credit, c_since
|
||||
FROM cust
|
||||
WHERE c_w_id=2 AND c_d_id=5
|
||||
ORDER BY c_last, c_first;
|
||||
```
|
||||
|
||||
|
||||
|
||||
在 ODC 中查询结果如下:
|
||||
|
||||
执行结果如下:
|
||||
|
||||
```unknow
|
||||
+------+----------------+------------------+----------+--------+--------+----------+------------+
|
||||
| c_id | c_last | c_first | c_middle | c_w_id | c_d_id | c_credit | c_since |
|
||||
+------+----------------+------------------+----------+--------+--------+----------+------------+
|
||||
| 202 | ABLEBAROUGHT | omHvv5eG | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 272 | ABLECALLYOUGHT | x0ikkFsuG | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 2106 | ABLECALLYPRI | Hksk96bvVRAZ | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 2813 | ABLEPRESEING | GxtYNqeRZFIaWmN | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 2360 | ANTIATIONATION | BcIWNvikBc | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 2500 | ANTIEINGCALLY | WXBmhcRTWe | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 658 | ANTIESECALLY | ONPLFp6Htl | OE | 2 | 5 | BC | 2020-02-15 |
|
||||
| 1085 | ANTIOUGHTABLE | SstdwQNJJ | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 2247 | ANTIPRIOUGHT | R1VEFjdPlOioo | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 2100 | ATIONABLEESE | PzFg81YvfBC | OE | 2 | 5 | BC | 2020-02-15 |
|
||||
| 2196 | ATIONATIONBAR | ReFiFtMxfo1qG5 | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 871 | ATIONCALLYBAR | W7ZNcPqp5sBWNi | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 2999 | ATIONCALLYPRI | eQbIWEimKcM80xNB | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 849 | ATIONPRESATION | Z6V2PdM8B0b9hQ8d | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 8 | BARBARCALLY | jXrw6CCdzcamEi | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 4 | BARBARPRI | KKvDumYX9AwHuxX | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 1983 | BAROUGHTANTI | XeYW3WKpfBITC1aB | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 1590 | BAROUGHTCALLY | Y886PI3rFsHCSMbF | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 1529 | BAROUGHTPRI | pyP6QQj7FB | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 34 | BARPRIPRI | gXmuxvj2R | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 1785 | CALLYABLEESE | LsWK1Zs1pm | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 2765 | CALLYABLEOUGHT | b0maEo4Nm2JgW2j | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 2102 | CALLYABLEOUGHT | wPS9EgAgztLRvSuZ | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 798 | CALLYEINGCALLY | Z9T8FivHqeaO | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 2793 | CALLYEINGOUGHT | eVQqIPDaGOb7N3EN | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 1370 | CALLYESEEING | J8Cakezt | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 2660 | CALLYPRESPRES | FgxeQplwu | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 2401 | CALLYPRIANTI | fJjIGGGH7Fdn1Kp | OE | 2 | 5 | BC | 2020-02-15 |
|
||||
| 926 | EINGABLEESE | NZLtbu8Jb7Qi | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 2970 | EINGABLEOUGHT | PLnJDvKTwR0ireM | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 2137 | EINGANTIEING | bDPeSSyD7 | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 1334 | EINGBAREING | ep86qqrvFlD | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 993 | EINGEINGABLE | mxu2u3eo | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 956 | EINGESEESE | q02t2c9oR7 | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 916 | EINGOUGHTESE | ungAvrh8dWoGlQlP | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 569 | ESEANTIATION | xIZPuIwBJXxVIER | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 1433 | ESEBARESE | jpTpJk5diH | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 2405 | ESEBARESE | ZXKb7eR96iGg | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 2101 | ESEPRESATION | DQUk9dys | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 2348 | OUGHTCALLYANTI | w0rDw8txdIPfeElQ | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 1093 | OUGHTCALLYESE | GBx3akVCEuTPLa | OE | 2 | 5 | BC | 2020-02-15 |
|
||||
| 193 | OUGHTEINGABLE | ImIEJkCzrYtEpaV4 | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 1318 | PRESATIONEING | uT4TGkIdWf | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 1813 | PRESATIONESE | SsF8v3NE | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 1991 | PRESESEANTI | GhytcH1EQhY | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 414 | PRESOUGHTPRI | vTsmW2XU | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 1979 | PRESPRESANTI | GGsWcgaTLXjtXAN | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 1184 | PRESPRESEING | hs5Bd47ZlZghuD8 | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 440 | PRESPRIEING | O4Vjdc3hmk4DT | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 1766 | PRIABLEEING | hqPJlwGx6Qt0FXy | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 372 | PRICALLYOUGHT | HpbvP0rDZM3 | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
| 356 | PRIESEESE | lZlbGnZhIlYH6nP | OE | 2 | 5 | GC | 2020-02-15 |
|
||||
+------+----------------+------------------+----------+--------+--------+----------+------------+
|
||||
52 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,92 @@
|
||||
从多个表里查询数据
|
||||
==============================
|
||||
|
||||
|
||||
|
||||
如果要查询的数据需要从多个表中取时,需要在 SELECT 语句中 FROM 关键字后用 JOIN... ON 将多个表关联起来查询。通常这多个表在业务上是有联系的,如某些字段值的定义和数据相同,这个联系条件就是连接条件,会体现在 ON 后面的括号里,ON 里也可以包括过滤条件。SQL 语法格式如下:
|
||||
|
||||
```javascript
|
||||
SELECT select_list FROM table_name1 [INNER] JOIN table_name2 ON ( join_condition )
|
||||
WHERE query_condition
|
||||
ORDER BY column_list
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
返回满足连接条件的多个表数据
|
||||
-----------------------
|
||||
|
||||
默认 JOIN 返回的结果会满足 ON 后面的连接条件,这个又叫内连接(INNER JOIN),通常关键字 INNER 省略了就表示内连接。JOIN 前后的表分别称之为左表和右表,ON 里的条件描述的是左表和右表的连接条件和过滤条件。如果没有ON 子句,那 INNER JOIN 返回的就是左表和右表的全部数据,这个也称为笛卡儿积。
|
||||
|
||||
SQL 返回的结果会在 JOIN 结果上再经过 WHERE 后的查询条件过滤以及根据 ORDER BY 后的列排序。
|
||||
|
||||
**示例:** **使用** **JOIN从多个表里查询数据**
|
||||
|
||||
```javascript
|
||||
obclient> create table t1(id number not null primary key, name varchar(50));
|
||||
Query OK, 0 rows affected (0.08 sec)
|
||||
|
||||
obclient> create table t2(id number not null primary key, name varchar(50));
|
||||
Query OK, 0 rows affected (0.06 sec)
|
||||
|
||||
obclient> insert into t1 values(1,'A1'),(2,'B1'),(4,'D1'),(6,'F1'),(8,'H1'),(10,'J1');
|
||||
Query OK, 6 rows affected (0.01 sec)
|
||||
Records: 6 Duplicates: 0 Warnings: 0
|
||||
|
||||
obclient> insert into t2 values(1,'B2'),(3,'C2'),(6,'F2'),(9,'I2');
|
||||
Query OK, 4 rows affected (0.01 sec)
|
||||
Records: 4 Duplicates: 0 Warnings: 0
|
||||
|
||||
obclient> select t1.id, t1.name, t2.id, t2.name from t1 join t2 on (t1.id=t2.id) ;
|
||||
+----+------+----+------+
|
||||
| ID | NAME | ID | NAME |
|
||||
+----+------+----+------+
|
||||
| 1 | A1 | 1 | B2 |
|
||||
| 6 | F1 | 6 | F2 |
|
||||
+----+------+----+------+
|
||||
2 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
返回包含不满足连续条件的多个表数据
|
||||
--------------------------
|
||||
|
||||
当需要 JOIN 返回的数据除了符合连接条件和过滤条件的数据外,还包括左表里满足左表的过滤条件但不满足连接条件的数据时,就可以使用左外连接(LEFT OUTER JOIN),也可以简写为左连接(LEFT JOIN)。左连接返回的结果里属于右表的数据如果不存在,则该列返回 NULL。
|
||||
|
||||
反之,如果 JOIN 返回的数据除了符合连接条件和过滤条件的数据外,还包括右表里满足右表的过滤条件但不满足连接条件的数据时,就可以使用右外连接(RIGHT OUTER JOIN),或简写为右连接(RIGHT JOIN)。右连接返回的结果里属于左表的数据如果不存在,则该列返回 NULL。
|
||||
|
||||
**示例:LEFT JOIN和RIGHT JOIN示例**
|
||||
|
||||
```javascript
|
||||
obclient> select t1.id, t1.name, t2.id, t2.name from t1 left join t2 on (t1.id=t2.id) ;
|
||||
+----+------+----+------+
|
||||
| ID | NAME | ID | NAME |
|
||||
+----+------+----+------+
|
||||
| 1 | A1 | 1 | B2 |
|
||||
| 2 | B1 | NULL | NULL |
|
||||
| 4 | D1 | NULL | NULL |
|
||||
| 6 | F1 | 6 | F2 |
|
||||
| 8 | H1 | NULL | NULL |
|
||||
| 10 | J1 | NULL | NULL |
|
||||
+----+------+----+------+
|
||||
6 rows in set (0.01 sec)
|
||||
|
||||
obclient> select t1.id, t1.name, t2.id, t2.name from t1 right join t2 on (t1.id=t2.id) ;
|
||||
+----+------+----+------+
|
||||
| ID | NAME | ID | NAME |
|
||||
+----+------+----+------+
|
||||
| 1 | A1 | 1 | B2 |
|
||||
| NULL | NULL | 3 | C2 |
|
||||
| 6 | F1 | 6 | F2 |
|
||||
| NULL | NULL | 9 | I2 |
|
||||
+----+------+----+------+
|
||||
4 rows in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
查询中使用算术操作符
|
||||
===============================
|
||||
|
||||
|
||||
|
||||
算术操作符包括:+(加)、-(减)、\*(乘)、/(除)、-(取反)、MOD(取模)。这些操作符可以作用在数值列上。
|
||||
|
||||
如"从多个表里查询数据" 页面的示例查询出客户购买的每个商品的数量和价格,其中数量乘以价格就是每类商品的支付总额。所以 select_list可以增加一列 t3.ol_quantity \* t4.i_price item_sum_price 。
|
||||
|
||||
```javascript
|
||||
SELECT t1.c_first, t1.c_last, t1.c_credit, t2.o_ol_cnt, t2.o_entry_d, t3.ol_number, t3.ol_quantity, t4.i_name, t4.i_price, t3.ol_quantity * t4.i_price item_sum_price
|
||||
FROM cust t1
|
||||
JOIN ordr t2 ON (t1.c_id=t2.o_id AND t1.c_w_id=t2.o_w_id AND t1.c_d_id=t2.o_d_id)
|
||||
JOIN ordl t3 ON (t2.o_id=t3.ol_o_id AND t2.o_w_id=t3.ol_w_id AND t2.o_d_id=t3.ol_d_id)
|
||||
JOIN item t4 ON (t4.i_id=t3.ol_i_id )
|
||||
WHERE t1.c_w_id=2 AND t1.c_d_id=5 and t1.c_last LIKE 'CALLY%'
|
||||
ORDER BY t1.c_id, t2.o_id, t3.ol_number
|
||||
;
|
||||
```
|
||||
|
||||
|
||||
|
||||
查询结果如下:
|
||||
|
||||
```unknow
|
||||
+------------------+----------------+----------+----------+------------+-----------+-------------+--------------------------+---------+----------------+
|
||||
| c_first | c_last | c_credit | o_ol_cnt | o_entry_d | ol_number | ol_quantity | i_name | i_price | item_sum_price |
|
||||
+------------------+----------------+----------+----------+------------+-----------+-------------+--------------------------+---------+----------------+
|
||||
| wPS9EgAgztLRvSuZ | CALLYABLEOUGHT | GC | 7 | 2020-02-15 | 1 | 5 | FJT8fkxaUh2aUbI | 79.95 | 399.75 |
|
||||
| wPS9EgAgztLRvSuZ | CALLYABLEOUGHT | GC | 7 | 2020-02-15 | 2 | 5 | kiMk43vd9HidvmwG8x | 58.59 | 292.95 |
|
||||
| wPS9EgAgztLRvSuZ | CALLYABLEOUGHT | GC | 7 | 2020-02-15 | 3 | 5 | JnJEOLUCjunrKkt4Z1pL | 85.26 | 426.30 |
|
||||
| wPS9EgAgztLRvSuZ | CALLYABLEOUGHT | GC | 7 | 2020-02-15 | 4 | 5 | CrFVAZW3OhyekdDNc2rPH | 22.30 | 111.50 |
|
||||
| wPS9EgAgztLRvSuZ | CALLYABLEOUGHT | GC | 7 | 2020-02-15 | 5 | 5 | fJpsyG11EjWIceJWaB | 41.39 | 206.95 |
|
||||
| wPS9EgAgztLRvSuZ | CALLYABLEOUGHT | GC | 7 | 2020-02-15 | 6 | 5 | shseF8WI1VSPbWfswSsIuNC | 30.04 | 150.20 |
|
||||
| wPS9EgAgztLRvSuZ | CALLYABLEOUGHT | GC | 7 | 2020-02-15 | 7 | 5 | prjdpUDOxRvAn5WiMVoT85B1 | 18.55 | 92.75 |
|
||||
+------------------+----------------+----------+----------+------------+-----------+-------------+--------------------------+---------+----------------+
|
||||
7 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
锁定查询结果 SELECT FOR UPDATE
|
||||
=============================================
|
||||
|
||||
|
||||
|
||||
OceanBase 支持 MVCC 特性,读是快照读,不阻塞写,是 SELECT 语句还有个特殊的用法可以阻塞写。示例如下:
|
||||
|
||||
```javascript
|
||||
obclient> select w_name, w_ytd, w_tax from ware where w_id=1 for update;
|
||||
+------------+---------+--------+
|
||||
| w_name | w_ytd | w_tax |
|
||||
+------------+---------+--------+
|
||||
| n1P4zYo8OH | 1200.00 | 0.1868 |
|
||||
+------------+---------+--------+
|
||||
1 row in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
查询中使用数值函数
|
||||
==============================
|
||||
|
||||
|
||||
|
||||
常用数值函数有:sum(求和)、avg(求平均)、ceil(向上取整)、floor(向下取整)、trunc(数值取整)、round(n)(四舍五入保留n位小数)。
|
||||
|
||||
如求历史表中每个仓库和区域的总销售额和平均每单销售额,SQL 如下:
|
||||
|
||||
```javascript
|
||||
SELECT h_w_id, h_d_id, sum(h_amount) sum_h_amount , avg(h_amount) avg_h_amount
|
||||
FROM hist
|
||||
GROUP BY h_w_id, h_d_id ;
|
||||
```
|
||||
|
||||
|
||||
|
||||
查询结果如下:
|
||||
|
||||
```unknow
|
||||
+--------+--------+--------------+--------------+
|
||||
| h_w_id | h_d_id | sum_h_amount | avg_h_amount |
|
||||
+--------+--------+--------------+--------------+
|
||||
| 1 | 1 | 120.00 | 10.000000 |
|
||||
| 1 | 2 | 120.00 | 10.000000 |
|
||||
| 1 | 3 | 120.00 | 10.000000 |
|
||||
| 1 | 4 | 120.00 | 10.000000 |
|
||||
| 1 | 5 | 120.00 | 10.000000 |
|
||||
| 1 | 6 | 120.00 | 10.000000 |
|
||||
| 1 | 7 | 120.00 | 10.000000 |
|
||||
| 1 | 8 | 120.00 | 10.000000 |
|
||||
| 1 | 9 | 120.00 | 10.000000 |
|
||||
| 1 | 10 | 120.00 | 10.000000 |
|
||||
| 2 | 1 | 120.00 | 10.000000 |
|
||||
| 2 | 2 | 120.00 | 10.000000 |
|
||||
| 2 | 3 | 120.00 | 10.000000 |
|
||||
| 2 | 4 | 120.00 | 10.000000 |
|
||||
| 2 | 5 | 120.00 | 10.000000 |
|
||||
| 2 | 6 | 120.00 | 10.000000 |
|
||||
| 2 | 7 | 120.00 | 10.000000 |
|
||||
| 2 | 8 | 120.00 | 10.000000 |
|
||||
| 2 | 9 | 120.00 | 10.000000 |
|
||||
| 2 | 10 | 120.00 | 10.000000 |
|
||||
+--------+--------+--------------+--------------+
|
||||
20 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
查询中使用字符串连接符
|
||||
================================
|
||||
|
||||
|
||||
|
||||
MySQL 租户的字符串连接函数是 concat 、 concat_ws, '\|\|' 默认是表示逻辑运算符\`或\`。
|
||||
|
||||
如查看 MySQL 租户下的客户姓名,SQL语句如下:
|
||||
|
||||
```javascript
|
||||
obclient> SELECT concat_ws(' ', c_first, c_last) full_name FROM cust ORDER BY c_last LIMIT 2;
|
||||
|
||||
+---------------------------+
|
||||
| full_name |
|
||||
+---------------------------+
|
||||
| fvBZoeIV2uJh7 ABLEABLEESE |
|
||||
| dHmIgRV1IsC ABLEABLEOUGHT |
|
||||
+---------------------------+
|
||||
2 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
如果把 MySQL 租户下的变量 sql_mode 值增加一个选项 PIPES_AS_CONCAT ,则 '\|\|' 也会当作字符串连接符。SQL语句如下:
|
||||
|
||||
```javascript
|
||||
obclient> SET SESSION sql_mode='PIPES_AS_CONCAT,STRICT_TRANS_TABLES,STRICT_ALL_TABLES';
|
||||
obclient> SELECT c_first || ' ' || c_last full_name FROM cust ORDER BY c_last LIMIT 2;
|
||||
+---------------------------+
|
||||
| full_name |
|
||||
+---------------------------+
|
||||
| fvBZoeIV2uJh7 ABLEABLEESE |
|
||||
| dHmIgRV1IsC ABLEABLEOUGHT |
|
||||
+---------------------------+
|
||||
2 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
查询中使用字符串函数
|
||||
===============================
|
||||
|
||||
|
||||
|
||||
常用的字符串函数有求字符串长度(length)、字符串截取(substr)、字符串拼接、 字符串转大小写(upper lower)、字符串删除前后缀(ltrim rtrim trim)。
|
||||
|
||||
需要注意的是,在 MySQL 租户里,字符串长度函数(length)长度单位是字节,char_length 函数的字符串长度单位是字符。
|
||||
|
||||
```javascript
|
||||
$obclient -h192.168.1.101 -utpcc@obmysql#obdemo -P2883 -p123456 -A tpccdb
|
||||
|
||||
obclient> select length('中'), char_length('中');
|
||||
|
||||
+---------------+--------------------+
|
||||
| length('中') | char_length('中') |
|
||||
+---------------+--------------------+
|
||||
| 3 | 1 |
|
||||
+---------------+--------------------+
|
||||
1 row in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,99 @@
|
||||
查询中使用时间函数
|
||||
==============================
|
||||
|
||||
|
||||
|
||||
MySQL 租户常用的时间类型有 date、timestamp、 time、datetime、year 等,更多时间类型用法,请参考《OceanBase SQL参考(MySQL模式)》。
|
||||
|
||||
MySQL 租户常用的取数据库时间函数是 now() ,curdate() 和 curtime() 。
|
||||
|
||||
* 示例:格式化时间显示
|
||||
|
||||
MySQL 租户调整时间类型显示的格式,可以用date_format 函数,SQL 如下:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> select now(), date_format(now(), "%Y/%m/%d %T") new_time ;
|
||||
+---------------------+---------------------+
|
||||
| now() | new_time |
|
||||
+---------------------+---------------------+
|
||||
| 2020-04-03 15:55:37 | 2020/04/03 15:55:37 |
|
||||
+---------------------+---------------------+
|
||||
1 row in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 示例:提取时间中的年/月/日/时/分/秒
|
||||
|
||||
MySQL 租户从时间中提取年/月/日/时/分/秒,可以用 extract 函数,SQL如下:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> SET @dt = now();
|
||||
|
||||
obclient> SELECT @dt
|
||||
, extract(YEAR FROM @dt) d_year
|
||||
, extract(MONTH FROM @dt) d_month
|
||||
, extract(week FROM @dt) d_week
|
||||
, extract(DAY FROM @dt) d_day
|
||||
, extract(HOUR FROM @dt) d_hour
|
||||
, extract(MINUTE FROM @dt) d_min
|
||||
, extract(SECOND FROM @dt) d_second
|
||||
, extract(year_month FROM @dt) d_year_month
|
||||
, extract(hour_minute FROM @dt) d_hour_min
|
||||
\G
|
||||
|
||||
*************************** 1. row ***************************
|
||||
@dt: 2020-03-27 18:00:52
|
||||
d_year: 2020
|
||||
d_month: 3
|
||||
d_week: 12
|
||||
d_day: 27
|
||||
d_hour: 18
|
||||
d_min: 0
|
||||
d_second: 52
|
||||
d_year_month: 202003
|
||||
d_hour_min: 1800
|
||||
1 row in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 示例:时间类型加减
|
||||
|
||||
MySQL 租户对时间进行加减,可以使用 date_add 或 date_sub 函数,SQL 如下:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> SET @dt = now();
|
||||
|
||||
obclient> SELECT @dt
|
||||
, date_add(@dt, INTERVAL 1 DAY ) t1
|
||||
, date_add(@dt, INTERVAL 1 HOUR ) t2
|
||||
, date_add(@dt, INTERVAL -10 MINUTE ) t3
|
||||
, date_add(@dt, INTERVAL -1 MONTH ) t4
|
||||
, date_sub(@dt, INTERVAL 1 YEAR ) t5
|
||||
\G
|
||||
|
||||
*************************** 1. row ***************************
|
||||
@dt: 2020-03-27 18:03:44
|
||||
t1: 2020-03-28 18:03:44
|
||||
t2: 2020-03-27 19:03:44
|
||||
t3: 2020-03-27 17:53:44
|
||||
t4: 2020-02-27 18:03:44
|
||||
t5: 2019-03-27 18:03:44
|
||||
1 row in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,62 @@
|
||||
查询中使用类型转换函数
|
||||
================================
|
||||
|
||||
|
||||
|
||||
类型转换函数可以将一种数据类型转换为另外一种数据类型,如数值类型和时间类型到字符串类型的相互转换。
|
||||
|
||||
* 示例:时间字符串转换为时间类型
|
||||
|
||||
MySQL 租户中,时间字符串可以直接复制给 date 类型,MySQL 可以自动转换为时间类型,另外也可以使用 convert 或 cast 函数做类型转换。SQL语句如下:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> SELECT CONVERT('2020-02-02 14:30:45', date) t1
|
||||
, CONVERT('2020-02-02 14:30:45', time) t2
|
||||
, CONVERT('2020-02-02 14:30:45', datetime) t3
|
||||
, CAST('2020-02-02 14:30:45' AS date) t4
|
||||
, CAST('2020-02-02 14:30:45' AS time) t5
|
||||
, CAST('2020-02-02 14:30:45' AS datetime) t6
|
||||
\G
|
||||
|
||||
*************************** 1. row ***************************
|
||||
t1: 2020-02-02
|
||||
t2: 14:30:45
|
||||
t3: 2020-02-02 14:30:45
|
||||
t4: 2020-02-02
|
||||
t5: 14:30:45
|
||||
t6: 2020-02-02 14:30:45
|
||||
1 row in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
MySQL 租户中时间类型转换为字符串类型,可以使用函数 date_format。
|
||||
|
||||
* 示例:数值类型和字符串类型互相转换
|
||||
|
||||
MySQL 租户中,数值类型和字符串类型互相转换,可以用函数 convert 、cast。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> SELECT convert('3.1415926', decimal) n1
|
||||
, cast('3.1415926' AS decimal) n2
|
||||
, convert(3.1415926, char(10)) s1
|
||||
, cast(3.1414926 AS char(10)) s2
|
||||
;
|
||||
|
||||
+------+------+-----------+-----------+
|
||||
| n1 | n2 | s1 | s2 |
|
||||
+------+------+-----------+-----------+
|
||||
| 3 | 3 | 3.1415926 | 3.1414926 |
|
||||
+------+------+-----------+-----------+
|
||||
1 row in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,78 @@
|
||||
查询中使用聚合函数
|
||||
==============================
|
||||
|
||||
|
||||
|
||||
聚合函数扫描一组记录,然后返回单行记录。这组记录可以是一个表或者视图、或者一个子查询的结果。OceanBase 支持的聚合函数详情请参考手册《OceanBase SQL参考(MySQL模式)》。
|
||||
|
||||
聚合函数通常跟 GROUP BY 子句一起使用,按照一个或多个列的值分组,然后每组返回单笔记录。
|
||||
|
||||
**示例:分组统计每个仓库的销售额**
|
||||
|
||||
MySQL 租户中,聚合函数跟 GROUP BY 子句一起使用的时候,对 select_list 里的列没有要求。这个可能会导致结果集很奇怪。如果要求 select_list 里的列跟 GROUP BY 子句中的列保持一致,需要设置 MySQL 命令行下的 sql_mode 为 'ONLY_FULL_GROUP_BY'。SQL 查询如下:
|
||||
|
||||
```javascript
|
||||
obclient> SELECT ol_w_id
|
||||
, count(*) order_count
|
||||
, sum(ol_amount) sum_amount
|
||||
, round(avg(ol_amount),2) avg_amount
|
||||
, min(ol_amount) min_amount
|
||||
,max(ol_amount) max_amount
|
||||
FROM ordl
|
||||
GROUP BY ol_w_id
|
||||
ORDER BY ol_w_id ;
|
||||
|
||||
+---------+-------------+------------+------------+------------+------------+
|
||||
| ol_w_id | order_count | sum_amount | avg_amount | min_amount | max_amount |
|
||||
+---------+-------------+------------+------------+------------+------------+
|
||||
| 1 | 297 | 917174.33 | 3088.13 | 0.00 | 9876.11 |
|
||||
| 2 | 329 | 1153354.23 | 3505.64 | 0.00 | 9979.34 |
|
||||
+---------+-------------+------------+------------+------------+------------+
|
||||
2 rows in set (0.01 sec)
|
||||
|
||||
obclient> SELECT ol_w_id, ol_d_id
|
||||
, count(*) order_count
|
||||
, sum(ol_amount) sum_amount
|
||||
, round(avg(ol_amount),2) avg_amount
|
||||
, min(ol_amount) min_amount
|
||||
, max(ol_amount) max_amount
|
||||
FROM ordl
|
||||
GROUP BY ol_w_id
|
||||
ORDER BY ol_w_id
|
||||
;
|
||||
|
||||
+---------+---------+-------------+------------+------------+------------+------------+
|
||||
| ol_w_id | ol_d_id | order_count | sum_amount | avg_amount | min_amount | max_amount |
|
||||
+---------+---------+-------------+------------+------------+------------+------------+
|
||||
| 1 | 1 | 297 | 917174.33 | 3088.13 | 0.00 | 9876.11 |
|
||||
| 2 | 1 | 329 | 1153354.23 | 3505.64 | 0.00 | 9979.34 |
|
||||
+---------+---------+-------------+------------+------------+------------+------------+
|
||||
2 rows in set (0.00 sec)
|
||||
|
||||
obclient> show variables like '%sql_mode%';
|
||||
+---------------+-------------------------------------------------------+
|
||||
| Variable_name | Value |
|
||||
+---------------+-------------------------------------------------------+
|
||||
| sql_mode | PIPES_AS_CONCAT,STRICT_TRANS_TABLES,STRICT_ALL_TABLES |
|
||||
+---------------+-------------------------------------------------------+
|
||||
1 row in set (0.00 sec)
|
||||
|
||||
obclient> SET SESSION sql_mode='STRICT_ALL_TABLES,ONLY_FULL_GROUP_BY'; Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
|
||||
obclient> SELECT ol_w_id, ol_d_id
|
||||
, count(*) order_count
|
||||
, sum(ol_amount) sum_amount
|
||||
, round(avg(ol_amount),2) avg_amount
|
||||
, min(ol_amount) min_amount
|
||||
, max(ol_amount) max_amount
|
||||
FROM ordl
|
||||
GROUP BY ol_w_id
|
||||
ORDER BY ol_w_id
|
||||
;
|
||||
|
||||
ERROR 1055 (42000): 'tpccdb.ordl.ol_d_id' is not in GROUP BY
|
||||
obclient>
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
查询中使用 NULL 相关函数
|
||||
====================================
|
||||
|
||||
|
||||
|
||||
NULL 相关的函数用于处理 NULL 值。NULL 值的特点是任何数值都不能等于 NULL 或不等于 NULL,可以通过 IS NULL 判断,也可以使用 NVL 函数将 NULL 值转换为可识别的字符串。下面示例如何识别和转换 NULL 值。
|
||||
|
||||
**示例:NULL 值转换**
|
||||
|
||||
MySQL 租户中,如果一个列可能有 NULL 值,可以使用 NVL 或 IFNULL 函数探测并转换为特殊字符。SQL查询如下:
|
||||
|
||||
```javascript
|
||||
CREATE TABLE t_null(id number NOT NULL PRIMARY KEY, name varchar(10));
|
||||
INSERT INTO t_null(id, name) values(1,'A'), (2,NULL), (3,'NULL');
|
||||
SELECT id, name, nvl(name, 'NOT APPLICABLE') n_name, IFNULL(name, 'NOT APPLICABLE') n2_name
|
||||
FROM t_null;
|
||||
```
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
执行结果如下:
|
||||
|
||||
```unknow
|
||||
+----+------+----------------+----------------+
|
||||
| id | name | n_name | n2_name |
|
||||
+----+------+----------------+----------------+
|
||||
| 1 | A | A | A |
|
||||
| 2 | NULL | NOT APPLICABLE | NOT APPLICABLE |
|
||||
| 3 | NULL | NULL | NULL |
|
||||
+----+------+----------------+----------------+
|
||||
3 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,94 @@
|
||||
查询中使用 CASE 函数
|
||||
==================================
|
||||
|
||||
|
||||
|
||||
CASE 表达式可以实现类似"IF...ELSE...THEN"的逻辑而不用调用子程序。CASE 表达式有两种使用方法,简单的和带搜索条件的。
|
||||
|
||||
* 示例:在查询中使用简单的 CASE 表达式,将国家代码缩写翻译为全称。
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> CREATE TABLE t_case(id number NOT NULL PRIMARY KEY, abbr varchar(5));
|
||||
Query OK, 0 rows affected (0.08 sec)
|
||||
|
||||
obclient> INSERT INTO t_case(id, abbr) VALUES (1,'US'),(2,'UK'),(3,'CN'),(4,'JP');
|
||||
Query OK, 4 rows affected (0.02 sec)
|
||||
Records: 4 Duplicates: 0 Warnings: 0
|
||||
|
||||
obclient>
|
||||
obclient> SELECT id, abbr,
|
||||
CASE abbr
|
||||
WHEN 'US' THEN 'America'
|
||||
WHEN 'UK' THEN 'English'
|
||||
WHEN 'CN' THEN 'China'
|
||||
ELSE 'UNKOWN'
|
||||
END full_name
|
||||
FROM t_case ;
|
||||
|
||||
+----+------+-----------+
|
||||
| id | abbr | full_name |
|
||||
+----+------+-----------+
|
||||
| 1 | US | America |
|
||||
| 2 | UK | English |
|
||||
| 3 | CN | China |
|
||||
| 4 | JP | UNKOWN |
|
||||
+----+------+-----------+
|
||||
4 rows in set (0.00 sec)
|
||||
|
||||
obclient>
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
* 示例:在查询中使用带搜索条件的 CASE 表达式
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> DROP TABLE IF EXISTS t_case2;
|
||||
Query OK, 0 rows affected (0.02 sec)
|
||||
|
||||
obclient> CREATE TABLE t_case2(id number NOT NULL PRIMARY KEY, c_date date );
|
||||
Query OK, 0 rows affected (0.14 sec)
|
||||
|
||||
obclient> INSERT INTO t_case2(id,c_date)
|
||||
VALUES (1,'2019-03-01')
|
||||
,(2,'2019-05-08')
|
||||
,(3,'2019-07-07')
|
||||
,(4,'2019-10-11')
|
||||
,(5,'2019-12-12')
|
||||
,(6,'2020-01-05');
|
||||
Query OK, 6 rows affected (0.01 sec)
|
||||
Records: 6 Duplicates: 0 Warnings: 0
|
||||
|
||||
obclient>
|
||||
obclient> SELECT id, c_date,
|
||||
CASE
|
||||
WHEN datediff(now(), c_date) > 12*30 THEN 'More than one year ago'
|
||||
WHEN datediff(now(), c_date) > 9*30 THEN 'More than three quarters ago'
|
||||
WHEN datediff(now(), c_date) > 6*30 THEN 'More than half a year ago'
|
||||
WHEN datediff(now(), c_date) > 3*30 THEN 'More than a quarter ago'
|
||||
WHEN datediff(now(), c_date) >= 0 THEN 'Within a quarter'
|
||||
ELSE 'Illegal'
|
||||
END "Duration"
|
||||
FROM t_case2;
|
||||
+----+------------+------------------------------+
|
||||
| id | c_date | Duration |
|
||||
+----+------------+------------------------------+
|
||||
| 1 | 2019-03-01 | More than one year ago |
|
||||
| 2 | 2019-05-08 | More than three quarters ago |
|
||||
| 3 | 2019-07-07 | More than three quarters ago |
|
||||
| 4 | 2019-10-11 | More than a quarter ago |
|
||||
| 5 | 2019-12-12 | More than a quarter ago |
|
||||
| 6 | 2020-01-05 | Within a quarter |
|
||||
+----+------------+------------------------------+
|
||||
6 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
查看查询执行计划
|
||||
=============================
|
||||
|
||||
|
||||
|
||||
OceanBase 的 SQL 引擎支持执行计划解析和缓存技术,可以通过分析查询语句的执行计划来分析查询性能。执行计划查看的语法格式如下:
|
||||
|
||||
```javascript
|
||||
explain [extended] query_statment ;
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**示例:查看查询执行计划**
|
||||
|
||||
```javascript
|
||||
obclient> EXPLAIN select count(*) from stok \G
|
||||
*************************** 1. row ***************************
|
||||
Query Plan: ========================================================
|
||||
|ID|OPERATOR |NAME |EST. ROWS|COST |
|
||||
--------------------------------------------------------
|
||||
|0 |SCALAR GROUP BY | |1 |600337|
|
||||
|1 | PX COORDINATOR | |1 |485729|
|
||||
|2 | EXCHANGE OUT DISTR |:EX10000|1 |485729|
|
||||
|3 | MERGE GROUP BY | |1 |485729|
|
||||
|4 | PX PARTITION ITERATOR| |600000 |371122|
|
||||
|5 | TABLE SCAN |stok |600000 |371122|
|
||||
========================================================
|
||||
|
||||
Outputs & filters:
|
||||
-------------------------------------
|
||||
0 - output([T_FUN_COUNT_SUM(T_FUN_COUNT(*))]), filter(nil),
|
||||
group(nil), agg_func([T_FUN_COUNT_SUM(T_FUN_COUNT(*))])
|
||||
1 - output([T_FUN_COUNT(*)]), filter(nil)
|
||||
2 - output([T_FUN_COUNT(*)]), filter(nil), dop=1
|
||||
3 - output([T_FUN_COUNT(*)]), filter(nil),
|
||||
group(nil), agg_func([T_FUN_COUNT(*)])
|
||||
4 - output([stok.s_i_id]), filter(nil)
|
||||
5 - output([stok.s_i_id]), filter(nil),
|
||||
access([stok.s_i_id]), partitions(p[0-5])
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,87 @@
|
||||
在查询中使用 SQL Hint
|
||||
====================================
|
||||
|
||||
|
||||
|
||||
初次使用 OceanBase 的用户,经常会用大表测试各种查询,有时会感觉性能不是很快,此时需要在查询中使用 SQL Hint。
|
||||
|
||||
关于 SQL Hint
|
||||
--------------------
|
||||
|
||||
OceanBase SQL 的执行性能跟 SQL 的执行计划有关,执行计划跟表的连接方式、查询条件和表的索引都有关系,通常这是数据库的 SQL 引擎内部逻辑。通过在 SQL 里添加注释,您可能改变执行计划的内容,从而改变 SQL 的执行性能。SQL Hint 的格式是:
|
||||
|
||||
```javascript
|
||||
/*+ hint_text */
|
||||
```
|
||||
|
||||
|
||||
|
||||
常用 SQL Hint 有:
|
||||
|
||||
* **read_consistency(weak)** :弱一致性读,指引SQL读取相关表的分区的备副本。
|
||||
|
||||
* **index** ( *table_name* , *index_name* ):指引SQL使用某个表的某个索引读取数据。
|
||||
|
||||
* **query_timeout(int_num):** 设置当前SQL的查询超时时间,单位是us。
|
||||
|
||||
|
||||
|
||||
|
||||
其他常用 SQL Hints 请参考附录 **OceanBase 常用 SQL Hints** 。
|
||||
|
||||
|
||||
|
||||
使用 SQL Hint
|
||||
--------------------
|
||||
|
||||
SQL Hint 通常用在 SQL 里,并不限于查询 SQL。这里以查询 SQL 为例,简单的语法格式如下:
|
||||
|
||||
```javascript
|
||||
SELECT /*+ hint_text [, hint_text] */ select_items FROM table_name
|
||||
```
|
||||
|
||||
|
||||
**说明**
|
||||
|
||||
|
||||
|
||||
* 多个 SQL Hint 可以叠加使用,注意功能不要冲突。
|
||||
|
||||
|
||||
|
||||
* 在 obclient 命令行环境下,默认会忽略注释语法,导致 SQL Hint 不起作用,所以启动 obclient 时需要增加参数"-c"。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
示例:在查询 SQL 中使用 SQL Hint,下面 SQL 指定查询超时时间为 10 秒。
|
||||
|
||||
```javascript
|
||||
obclient> select /*+ query_timeout(10000000) */ o_id,o_c_id,o_carrier_id,o_ol_cnt,o_all_local,o_entry_d
|
||||
from ordr
|
||||
where o_w_id=1 and o_d_id=2 and o_id=2100;
|
||||
+------+--------+--------------+----------+-------------+------------+
|
||||
| o_id | o_c_id | o_carrier_id | o_ol_cnt | o_all_local | o_entry_d |
|
||||
+------+--------+--------------+----------+-------------+------------+
|
||||
| 2100 | 8 | 8 | 11 | 1 | 2020-02-15 |
|
||||
+------+--------+--------------+----------+-------------+------------+
|
||||
1 row in set (0.00 sec)
|
||||
|
||||
|
||||
obclient> SELECT /*+ no_use_px paratLel(8) */ * FROM(
|
||||
SELECT /*+ no_use_px parallel(8) */ no_w_id, no_d_id, MAX(no_o_id) max_no_o_id, MIN(no_o_id) min_no_o_id, COUNT(*) count_no
|
||||
FROM nord
|
||||
GROUP BY no_w_id, no_d_Id
|
||||
) x
|
||||
WHERE max_no_o_id - min_no_o_id+ 1!= count_no;
|
||||
Empty set (0.01 sec)
|
||||
|
||||
obclient>
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
关于查询超时设计
|
||||
=============================
|
||||
|
||||
|
||||
|
||||
OceanBase 租户内部执行查询的工作线程数跟租户的 CPU 个数相关,所以工作线程是很稀有的资源。如果查询长时间不返回,就会一直占用这个工作线程资源。OceanBase 为避免长时间不返回的查询长期占用工作线程资源以及可能占用 CPU 资源,设计了查询超时功能。这个超时时间默认由租户变量 ob_query_timeout 控制,默认值是100000000(单位微秒)。当查询时间超过这个变量值后,会返回错误-4012(HY000): Timeout 。
|
||||
|
||||
默认的超时参数对于 OLTP 类业务来说是合理的,但是对于 OLAP 类业务就不一定。此时可以选择在会话级别调整租户变量的值,或者使用 SQL Hint 设置超时参数。
|
||||
|
||||
**示例:设置查询超时**
|
||||
|
||||
```javascript
|
||||
#obclient -h127.1 -uroot@obmysql#obdemo -P3883 -p123456 -c -A
|
||||
|
||||
obclient> show variables like 'ob_query_timeout';
|
||||
+------------------+----------+
|
||||
| Variable_name | Value |
|
||||
+------------------+----------+
|
||||
| ob_query_timeout | 10000000 |
|
||||
+------------------+----------+
|
||||
1 row in set (0.01 sec)
|
||||
|
||||
obclient> set session ob_query_timeout=10000000;
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
obclient> select sleep(11);
|
||||
ERROR 4012 (HY000): Timeout
|
||||
obclient>
|
||||
obclient> select /*+ query_timeout(100000000) */ sleep(11);
|
||||
+-----------+
|
||||
| sleep(11) |
|
||||
+-----------+
|
||||
| 0 |
|
||||
+-----------+
|
||||
1 row in set (11.00 sec)
|
||||
|
||||
obclient>
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
DML 语句概述
|
||||
=============================
|
||||
|
||||
|
||||
|
||||
DML(Data Manipulation Language,数据操作语言)语句可以操作表中的数据,如增加、修改、删除等。
|
||||
**说明**
|
||||
|
||||
|
||||
|
||||
在 obclient 命令行环境下,可以在"SQL提示符\>"后输入 DML 语句。
|
||||
|
||||
DML 语句对数据的修改效果只有在提交事务时才永久生效。一个事务是一序列被 OceanBase 数据库当作整体的多个 SQL 语句,要么全部执行成功,要么全部不成功。单个 DML 语句也可以是一个事务。事务在没有提交之前,还可以回滚事务。
|
||||
@ -0,0 +1,145 @@
|
||||
关于 INSERT 语句
|
||||
=================================
|
||||
|
||||
|
||||
|
||||
INSERT 语句用来向表中插入行记录。
|
||||
|
||||
最简单的 INSERT 语句语法格式如下:
|
||||
|
||||
```javascript
|
||||
INSERT INTO table_name (list_of_columns) VALUES (list_of_values);
|
||||
```
|
||||
|
||||
|
||||
|
||||
其中,list_of_columns 指定表的 table_name 列,list_of_values 是 list_of_columns 提到的列的对应值,必须一一对应。因此,在向一个表插入记录之前,需要了解这个表所有的列信息,以及列类型和有效值、是否允许为空等。在 obclient 命令行环境下,可以直接用 DESC 命令查看列属性,如下所示:
|
||||
|
||||
```javascript
|
||||
obclient> desc ordl;
|
||||
+----------------+-------------+------+-----+---------+-------+
|
||||
| FIELD | TYPE | NULL | KEY | DEFAULT | EXTRA |
|
||||
+----------------+-------------+------+-----+---------+-------+
|
||||
| OL_W_ID | NUMBER(38) | NO | PRI | NULL | NULL |
|
||||
| OL_D_ID | NUMBER(38) | NO | PRI | NULL | NULL |
|
||||
| OL_O_ID | NUMBER(38) | NO | PRI | NULL | NULL |
|
||||
| OL_NUMBER | NUMBER(38) | NO | PRI | NULL | NULL |
|
||||
| OL_DELIVERY_D | DATE | YES | NULL | NULL | NULL |
|
||||
| OL_AMOUNT | NUMBER(6,2) | YES | NULL | NULL | NULL |
|
||||
| OL_I_ID | NUMBER(38) | YES | NULL | NULL | NULL |
|
||||
| OL_SUPPLY_W_ID | NUMBER(38) | YES | NULL | NULL | NULL |
|
||||
| OL_QUANTITY | NUMBER(38) | YES | NULL | NULL | NULL |
|
||||
| OL_DIST_INFO | CHAR(24) | YES | NULL | NULL | NULL |
|
||||
+----------------+-------------+------+-----+---------+-------+
|
||||
10 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
在 INSERT 语句中,您不需要知道表的所有列的值,但需要知道所有列属性为
|
||||
|
||||
*NOT* *NULL 的列的值。如果列属性为 NOT NULL 有默认值时,您可以不指定该列的值;如果列为*
|
||||
|
||||
*NULL,您也可以不指定该列的值,OceanBase 会在该列上插入一个 NULL 值。*
|
||||
|
||||
当插入多条件记录时,可以分多条 INSERT 语句,也可以用一个 INSERT 多个 VALUES 语句。
|
||||
|
||||
示例:当所有列信息都知道时,使用INSERT语句
|
||||
---------------------------------
|
||||
|
||||
如下示例创建有默认值列的表,SQL 插入两笔记录,所有字段信息都有值。
|
||||
|
||||
```javascript
|
||||
obclient> CREATE TABLE t_insert(
|
||||
id number NOT NULL PRIMARY KEY
|
||||
, name varchar(10) NOT NULL, value number
|
||||
, gmt_create DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
Query OK, 0 rows affected (0.07 sec)
|
||||
|
||||
obclient> INSERT INTO t_insert(id, name, value, gmt_create)
|
||||
values(1,'CN',10001, current_timestamp);
|
||||
Query OK, 1 row affected (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
示例:当不是所有列信息都知道时,使用INSERT语句
|
||||
-----------------------------------
|
||||
|
||||
下面 SQL 插入两笔记录,gmt_create 字段没有提供。两笔记录使用一个 INSERT 多个 VALUES 子句。
|
||||
|
||||
```javascript
|
||||
obclient> INSERT INTO t_insert(id, name, value)
|
||||
VALUES (2,'US', 10002) ,(3,'EN', 10003);
|
||||
Query OK, 2 rows affected (0.00 sec)
|
||||
Records: 2 Duplicates: 0 Warnings: 0
|
||||
```
|
||||
|
||||
|
||||
|
||||
示例:使用INSERT语句违反唯一约束冲突
|
||||
------------------------------
|
||||
|
||||
当表上有唯一性约束的时候,插入相同的记录,数据库会报错。
|
||||
|
||||
```javascript
|
||||
obclient> INSERT INTO t_insert(id, name, value)
|
||||
VALUES (3,'UK', 10003)
|
||||
,(4, 'JP', 10004);
|
||||
ERROR 1062 (23000): Duplicate entry '3' for key 'PRIMARY'
|
||||
```
|
||||
|
||||
|
||||
|
||||
这个报错可以通过 INSERT IGNORE INTO、MERGE INTO、INSERT INTO ON DUPLICATE KEY UPDATE 避免。
|
||||
|
||||
#### 关于INSERT IGNORE INTO 语句
|
||||
|
||||
下面示例是 MySQL 租户下使用 INSERT IGNORE INTO 避免约束冲突,IGNORE 关键字可以忽略由于约束冲突导致的 INSERT 失败的影响。
|
||||
|
||||
```javascript
|
||||
obclient> INSERT IGNORE INTO t_insert(id, name, value)
|
||||
VALUES (3,'UK', 10003) ,(4, 'JP', 10004);
|
||||
Query OK, 1 row affected (0.00 sec)
|
||||
|
||||
obclient> select * from t_insert;
|
||||
+----+------+-------+---------------------+
|
||||
| id | name | value | gmt_create |
|
||||
+----+------+-------+---------------------+
|
||||
| 1 | CN | 10001 | 2020-04-03 16:05:45 |
|
||||
| 2 | US | 10002 | 2020-04-03 16:05:54 |
|
||||
| 3 | EN | 10003 | 2020-04-03 16:05:54 |
|
||||
| 4 | JP | 10004 | 2020-04-03 16:06:08 |
|
||||
+----+------+-------+---------------------+
|
||||
4 rows in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
示例:使用查询语句充当 INSERT 的 values 子句
|
||||
---------------------------------------
|
||||
|
||||
当需要备份一个表的备份或者全部记录时,可以使用 `INSERT INTO ... SELECT ... FROM` 语句。
|
||||
|
||||
```javascript
|
||||
obclient> create table ware_bak(
|
||||
w_id int
|
||||
, w_ytd decimal(12,2)
|
||||
, w_tax decimal(4,4)
|
||||
, w_name varchar(10)
|
||||
, w_street_1 varchar(20)
|
||||
, w_street_2 varchar(20)
|
||||
, w_city varchar(20)
|
||||
, w_state char(2)
|
||||
, w_zip char(9)
|
||||
, primary key(w_id)
|
||||
);
|
||||
Query OK, 0 rows affected (0.17 sec)
|
||||
|
||||
obclient> insert into ware_bak select * from ware;
|
||||
Query OK, 2 rows affected (0.02 sec)
|
||||
Records: 2 Duplicates: 0 Warnings: 0
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,107 @@
|
||||
关于 UPDATE 语句
|
||||
=================================
|
||||
|
||||
|
||||
|
||||
UPDATE语句用来更新表的行记录。
|
||||
|
||||
简单的UPDATE语句语法格式如下:
|
||||
|
||||
```javascript
|
||||
UPDATE table_name
|
||||
SET column_name = value [, column_name = value]...
|
||||
[ WHERE condition ];
|
||||
```
|
||||
|
||||
|
||||
|
||||
其中,column_name 是要更新的列,等号后面的 value 是要更新的目标值,必须符合列的类型定义。WHERE 条件子句指定要更新的行记录必须满足的条件,没有 WHERE 条件子句就是更新表对应列的所有记录。
|
||||
|
||||
示例:更新所有记录
|
||||
------------------
|
||||
|
||||
```javascript
|
||||
obclient> update t_insert set value=value+1 ;
|
||||
Query OK, 4 rows affected (0.00 sec)
|
||||
Rows matched: 4 Changed: 4 Warnings: 0
|
||||
|
||||
obclient> select * from t_insert;
|
||||
+----+------+-------+---------------------+
|
||||
| id | name | value | gmt_create |
|
||||
+----+------+-------+---------------------+
|
||||
| 1 | CN | 10002 | 2020-04-03 17:18:06 |
|
||||
| 2 | US | 10003 | 2020-04-03 17:18:47 |
|
||||
| 3 | EN | 10004 | 2020-04-03 17:18:47 |
|
||||
| 4 | JP | 10005 | 2020-04-03 17:28:21 |
|
||||
+----+------+-------+---------------------+
|
||||
4 rows in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
不带条件更新的时候,如果记录数达到几十万或者几百万,会有大事务产生,可能会失败。所以 UPDATE 要注意控制事务不要太大。
|
||||
|
||||
示例:更新部分记录,违反约束报错
|
||||
-------------------------
|
||||
|
||||
```javascript
|
||||
obclient> create unique index uk_name on t_insert(name);
|
||||
Query OK, 0 rows affected (1.99 sec)
|
||||
|
||||
obclient> update t_insert set name='US' where id=3;
|
||||
ERROR 1062 (23000): Duplicate entry 'US' for key 'uk_name'
|
||||
```
|
||||
|
||||
|
||||
|
||||
除了显式的 UPDATE 语句外,还有几类语句也可以更新数据。比如说 INSERT 因为约束冲突失败的时候,可以使用 ON DUPCLICATE KEY UPDATE 子句转变为 UPDATE 语句更新相关字段。
|
||||
|
||||
#### 关于 INSERT ON DUPLICATE KEY UPDATE 子句
|
||||
|
||||
使用 ON DUPLICATE KEY UPDATE 子句时,要求表上面要有主键或唯一约束(索引)。
|
||||
|
||||
**示例:使用** **INSERT ON DUPLICATE KEY UPDATE** **避免数据插入冲突**
|
||||
|
||||
```javascript
|
||||
obclient> INSERT INTO t_insert(id, name, value) VALUES (3,'UK', 10003);
|
||||
ERROR 1062 (23000): Duplicate entry '3' for key 'PRIMARY'
|
||||
|
||||
obclient> INSERT INTO t_insert(id, name, value) VALUES (3,'UK', 10003)
|
||||
ON DUPLICATE KEY UPDATE name='UK', value=10003 ;
|
||||
Query OK, 2 rows affected (0.01 sec)
|
||||
|
||||
obclient> select * from t_insert;
|
||||
+----+------+-------+---------------------+
|
||||
| id | name | value | gmt_create |
|
||||
+----+------+-------+---------------------+
|
||||
| 1 | CN | 10002 | 2020-04-03 18:05:45 |
|
||||
| 2 | US | 10003 | 2020-04-03 18:05:54 |
|
||||
| 3 | UK | 10003 | 2020-04-03 18:05:54 |
|
||||
| 4 | JP | 10005 | 2020-04-03 18:06:08 |
|
||||
+----+------+-------+---------------------+
|
||||
4 rows in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 关于 SELECT ... FOR UPDATE 子句
|
||||
|
||||
使用 SELECT ... FOR UPDATE 可以在读取记录的时候就对记录加锁,避免其他 DML 语句对该笔记录进行同时修改,这种设计通常也称为"悲观锁策略"。
|
||||
|
||||
**示例:使用** **SELECT ... FOR UPDATE 先锁定记录后修改**
|
||||
|
||||
```javascript
|
||||
obclient> select id,name,value from t_insert where id=2 for update;
|
||||
+----+------+-------+
|
||||
| id | name | value |
|
||||
+----+------+-------+
|
||||
| 2 | US | 10003 |
|
||||
+----+------+-------+
|
||||
1 row in set (0.01 sec)
|
||||
|
||||
obclient> update t_insert set value=value+100 where id=2;
|
||||
Query OK, 1 row affected (0.00 sec)
|
||||
Rows matched: 1 Changed: 1 Warnings: 0
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
关于 DELETE 语句
|
||||
=================================
|
||||
|
||||
|
||||
|
||||
DELETE 语句用来删除表中的记录。
|
||||
|
||||
简单的 DELETE 语句格式如下:
|
||||
|
||||
```javascript
|
||||
DELETE FROM table_name [ WHERE condition ] ;
|
||||
```
|
||||
|
||||
|
||||
|
||||
其中,WHERE 条件子句是可选的,如果没有提供就全表删除。如果表记录数多达几百万以上,会形成大事务,可能会有性能问题。建议带上 WHERE 条件分批删除;或者使用 TRUNCATE TABLE 语句。
|
||||
|
||||
示例:删除表中数据
|
||||
------------------
|
||||
|
||||
```javascript
|
||||
delete from ordr where o_w_id=2;
|
||||
|
||||
delete from ordr ;
|
||||
```
|
||||
|
||||
|
||||
|
||||
除了 DELETE 语句外,REPLACE INTO 语句也可能会删除数据。
|
||||
@ -0,0 +1,43 @@
|
||||
关于 REPLACE INTO 语句
|
||||
=======================================
|
||||
|
||||
|
||||
|
||||
REPLACE INTO 语句会判断行记录是否存在(根据主键索引或唯一索引判断)。如果不存在,则插入记录;如果存在,则删除已有记录,并插入新行记录。目标表建议有主键或者唯一索引,否则容易插入重复的记录。
|
||||
|
||||
示例如下:
|
||||
|
||||
```javascript
|
||||
obclient> CREATE TABLE t_replace(
|
||||
id number NOT NULL PRIMARY KEY
|
||||
, name varchar(10) NOT NULL
|
||||
, value number
|
||||
,gmt_create timestamp NOT NULL DEFAULT current_timestamp
|
||||
);
|
||||
Query OK, 0 rows affected (0.06 sec)
|
||||
|
||||
obclient> REPLACE INTO t_replace values(1,'CN',2001, current_timestamp ());
|
||||
Query OK, 1 row affected (0.00 sec)
|
||||
|
||||
obclient> REPLACE INTO t_replace
|
||||
SELECT id,name,value,gmt_create FROM t_insert;
|
||||
Query OK, 5 rows affected (0.00 sec)
|
||||
Records: 4 Duplicates: 1 Warnings: 0
|
||||
|
||||
obclient> REPLACE INTO t_replace(id, name, value) values(6,'DE',20006);
|
||||
Query OK, 1 row affected (0.01 sec)
|
||||
|
||||
obclient> select * from t_replace;
|
||||
+----+------+-------+---------------------+
|
||||
| id | name | value | gmt_create |
|
||||
+----+------+-------+---------------------+
|
||||
| 1 | CN | 10002 | 2020-04-03 18:05:45 |
|
||||
| 2 | US | 10103 | 2020-04-03 18:05:54 |
|
||||
| 3 | UK | 10003 | 2020-04-03 18:05:54 |
|
||||
| 4 | JP | 10005 | 2020-04-03 18:06:08 |
|
||||
| 6 | DE | 20006 | 2020-04-03 18:09:19 |
|
||||
+----+------+-------+---------------------+
|
||||
5 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
关于事务控制语句
|
||||
=============================
|
||||
|
||||
|
||||
|
||||
事务指的是一序列 SQL 语句,OceanBase 将这组 SQL 语句当作一个整体,要么全部执行成功,要么全部不成功;不存在部分 SQL 语句执行成功,或者部分 SQL 没有执行成功的情形。
|
||||
|
||||
举个例子,当一个客户下单购买了一批商品时,订单表会新增几行记录购买信息和金额,库存表会更新这批商品的库存数量(扣减库存)。应用程序中为这个业务做设计时,就需要把 INSERT 和 UPDATE 语句放到一个事务里。
|
||||
|
||||
通常事务中的 SQL 会包含 DML 语句,也会包含查询语句。如果一个事务中的 SQL 只有查询语句,这个事务通常称为只读事务。
|
||||
|
||||
基本的事务控制语句有:
|
||||
|
||||
* BEGIN,显式开启一个事务。这个命令是可选的,如果租户会话的参数 autocommit 值是 off (关闭自动提交),就不需要显式发出这个命令;如果参数值是 on (开启自动提交),那每条 SQL 就是一个独立的事务;如果要多个 SQL 组成一个事务,需要显式发起 BEGIN 命令。
|
||||
|
||||
|
||||
|
||||
* SAVEPOINT,在事务过程中标记一个"保存点",事务可以事后选择回滚到这个点。保存点是可选的,一个事务过程中也可以有多个保存点。
|
||||
|
||||
|
||||
|
||||
* COMMIT,结束当前事务,让事务所有修改持久化并生效,清除所有保存点和释放事务持有的锁。
|
||||
|
||||
|
||||
|
||||
* ROLLBACK,回滚整个事务已做的修改或者只回滚某个保存点之后事务已做的修改,清除回滚部分包含的所有保存点和释放事务持有的锁。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
在 obclient 命令环境下,可以在 SQL 提示符后发起事务控制命令,也可以修改会话级别的 autocommit 参数。如果是修改租户级别的 autocommit 参数,需要断开会话重新连接才会生效。
|
||||
**说明**
|
||||
|
||||
|
||||
|
||||
如果会话的 autocommit 参数值是 off 时,并且没有显式的提交事务,程序异常中断时,OceanBase 数据库会自动回滚最后一个未提交的事务。
|
||||
|
||||
建议显式的提交事务或者回滚事务。
|
||||
|
||||
@ -0,0 +1,79 @@
|
||||
提交事务
|
||||
=========================
|
||||
|
||||
|
||||
|
||||
提交一个事务会让事务的修改持久化生效,清除保存点并释放事务所持有的所有锁。
|
||||
|
||||
要显式的提交事务,使用 COMMIT 语句或者使用提交按钮(在图形化客户端工具中)。
|
||||
**说明**
|
||||
|
||||
|
||||
|
||||
OceanBase 会在 DDL 语句前和后隐式的发起一个 COMMIT 语句,这个也会提交事务。
|
||||
|
||||
* 如果使用 BEGIN 开启一个事务,执行 DML 语句后需要使用 COMMIT 提交事务。
|
||||
|
||||
在您提交事务之前:
|
||||
* 您的修改只对当前会话可见,对其他数据库会话是不可见的。
|
||||
|
||||
|
||||
|
||||
* 您的修改没有持久化,所以不是最终的,可以用 ROLLBACK 语句回滚这些修改。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
在您提交事务之后:
|
||||
* 您的修改对所有数据库会话可见。
|
||||
|
||||
|
||||
|
||||
* 您的修改持久化成功,不可以用 ROLLBACK 语句回滚这些修改。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
* 如果不显示地使用 BEGIN 开启事务,则一条 SQL 就是一个事务,不再需要提交事务。SQL 执行后您的修改即持久化成功,不可以用 ROLLBACK 语句回滚这些修改。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
示例:提交事务
|
||||
----------------
|
||||
|
||||
```javascript
|
||||
obclient> begin;
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
obclient> insert into t_insert(id,name) values(4,'JP');
|
||||
Query OK, 1 row affected (0.01 sec)
|
||||
|
||||
obclient> commit;
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
obclient> ^Bye
|
||||
|
||||
[qing.meiq@h07g12088.sqa.eu95 /home/qing.meiq/bmsql]
|
||||
$obclient -h192.168.1.101 -utpcc@obbmsql#obdemo -P2883 -p123456 TPCC
|
||||
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
|
||||
|
||||
obclient> select * from t_insert;
|
||||
+----+------+-------+---------------------+
|
||||
| ID | NAME | VALUE | GMT_CREATE |
|
||||
+----+------+-------+---------------------+
|
||||
| 1 | CN | 10001 | 2020-04-02 17:52:31 |
|
||||
| 2 | US | 10002 | 2020-04-02 17:52:38 |
|
||||
| 3 | EN | 10003 | 2020-04-02 17:52:38 |
|
||||
| 4 | JP | NULL | 2020-04-02 17:53:34 |
|
||||
+----+------+-------+---------------------+
|
||||
4 rows in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,103 @@
|
||||
回滚事务
|
||||
=========================
|
||||
|
||||
|
||||
|
||||
回滚一个事务指将事务的修改全部撤销。可以回滚当前整个未提交事务,也可以回滚到事务中任意一个保存点。如果要回滚到某个保存点,必须结合使用 ROLLBACK 和 TO SAVEPOINT 语句。
|
||||
|
||||
如果回滚整个事务:
|
||||
|
||||
* 事务会结束。
|
||||
|
||||
|
||||
|
||||
* 所有的修改会被丢弃。
|
||||
|
||||
|
||||
|
||||
* 清除所有保存点。
|
||||
|
||||
|
||||
|
||||
* 释放事务持有的所有锁。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
如果回滚到某个保存点:
|
||||
|
||||
* 事务不会结束。
|
||||
|
||||
|
||||
|
||||
* 保存点之前的修改被保留,保存点之后的修改被丢弃。
|
||||
|
||||
|
||||
|
||||
* 清除保存点之后的保存点(不包括保存点自身)。
|
||||
|
||||
|
||||
|
||||
* 释放保存点之后事务持有的所有锁。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
示例:ROLLBACK 回滚事务的全部修改
|
||||
------------------------------
|
||||
|
||||
```javascript
|
||||
obclient> select * from t_insert;
|
||||
+----+------+-------+---------------------+
|
||||
| ID | NAME | VALUE | GMT_CREATE |
|
||||
+----+------+-------+---------------------+
|
||||
| 1 | CN | 10001 | 2020-04-02 17:52:31 |
|
||||
| 2 | US | 10002 | 2020-04-02 17:52:38 |
|
||||
| 3 | EN | 10003 | 2020-04-02 17:52:38 |
|
||||
+----+------+-------+---------------------+
|
||||
3 rows in set (0.00 sec)
|
||||
|
||||
obclient> begin;
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
obclient> insert into t_insert(id, name, value) values(4,'JP',10004);
|
||||
Query OK, 1 row affected (0.00 sec)
|
||||
|
||||
obclient> insert into t_insert(id, name, value) values(5,'FR',10005),(6,'RU',10006);
|
||||
Query OK, 2 rows affected (0.00 sec)
|
||||
Records: 2 Duplicates: 0 Warnings: 0
|
||||
|
||||
obclient> select * from t_insert;
|
||||
+----+------+-------+---------------------+
|
||||
| ID | NAME | VALUE | GMT_CREATE |
|
||||
+----+------+-------+---------------------+
|
||||
| 1 | CN | 10001 | 2020-04-02 17:52:31 |
|
||||
| 2 | US | 10002 | 2020-04-02 17:52:38 |
|
||||
| 3 | EN | 10003 | 2020-04-02 17:52:38 |
|
||||
| 4 | JP | NULL | 2020-04-02 17:53:34 |
|
||||
| 5 | FR | 10005 | 2020-04-02 17:54:53 |
|
||||
| 6 | RU | 10006 | 2020-04-02 17:54:53 |
|
||||
+----+------+-------+---------------------+
|
||||
6 rows in set (0.00 sec)
|
||||
|
||||
obclient> rollback;
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
obclient> select * from t_insert;
|
||||
+----+------+-------+---------------------+
|
||||
| ID | NAME | VALUE | GMT_CREATE |
|
||||
+----+------+-------+---------------------+
|
||||
| 1 | CN | 10001 | 2020-04-02 17:52:31 |
|
||||
| 2 | US | 10002 | 2020-04-02 17:52:38 |
|
||||
| 3 | EN | 10003 | 2020-04-02 17:52:38 |
|
||||
+----+------+-------+---------------------+
|
||||
3 rows in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,145 @@
|
||||
事务保存点
|
||||
==========================
|
||||
|
||||
|
||||
|
||||
SAVEPOINT 语句可以在事务过程中标记一个"保存点",事务可以选择回滚到这个点。保存点是可选的,一个事务过程中也可以有多个保存点。
|
||||
|
||||
示例:将一个事务回滚到一个保存点
|
||||
-------------------------
|
||||
|
||||
下面示例展示了一个事务中包含多个 DML 语句和多个保存点,然后回滚到其中一个保存点,只丢弃了保存点后面的那部份修改。
|
||||
|
||||
* 查看表当前记录
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> select * from t_insert;
|
||||
+----+------+-------+---------------------+
|
||||
| id | name | value | gmt_create |
|
||||
+----+------+-------+---------------------+
|
||||
| 1 | CN | 10001 | 2020-04-03 16:05:45 |
|
||||
| 2 | US | 10002 | 2020-04-03 16:05:54 |
|
||||
| 3 | UK | 10003 | 2020-04-03 16:05:54 |
|
||||
+----+------+-------+---------------------+
|
||||
3 rows in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 开启一个事务,设置多个保存点信息。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> set session autocommit=off;
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
obclient> begin;
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
obclient> insert into t_insert(id, name) values(6,'FR');
|
||||
Query OK, 1 row affected (0.00 sec)
|
||||
|
||||
obclient> savepoint fr;
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
obclient> insert into t_insert(id, name) values(7,'RU');
|
||||
Query OK, 1 row affected (0.00 sec)
|
||||
|
||||
obclient> savepoint ru;
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
obclient> insert into t_insert(id, name) values(8,'CA');
|
||||
Query OK, 1 row affected (0.00 sec)
|
||||
|
||||
obclient> savepoint ca;
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 当前会话能看到事务未提交的所有修改。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> select * from t_insert;
|
||||
+----+------+-------+---------------------+
|
||||
| id | name | value | gmt_create |
|
||||
+----+------+-------+---------------------+
|
||||
| 1 | CN | 10001 | 2020-04-03 16:05:45 |
|
||||
| 2 | US | 10002 | 2020-04-03 16:05:54 |
|
||||
| 3 | UK | 10003 | 2020-04-03 16:05:54 |
|
||||
| 6 | FR | NULL | 2020-04-03 16:26:22 |
|
||||
| 7 | RU | NULL | 2020-04-03 16:26:32 |
|
||||
| 8 | CA | NULL | 2020-04-03 16:26:42 |
|
||||
+----+------+-------+---------------------+
|
||||
6 rows in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 回滚事务到其中一个保存点。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> rollback to savepoint ru;
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
obclient> select * from t_insert;
|
||||
+----+------+-------+---------------------+
|
||||
| id | name | value | gmt_create |
|
||||
+----+------+-------+---------------------+
|
||||
| 1 | CN | 10001 | 2020-04-03 16:05:45 |
|
||||
| 2 | US | 10002 | 2020-04-03 16:05:54 |
|
||||
| 3 | UK | 10003 | 2020-04-03 16:05:54 |
|
||||
| 6 | FR | NULL | 2020-04-03 16:26:22 |
|
||||
| 7 | RU | NULL | 2020-04-03 16:26:32 |
|
||||
+----+------+-------+---------------------+
|
||||
5 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 提交事务,确认表最新修改包含保存点之前的修改。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> commit;
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
obclient> select * from t_insert;
|
||||
+----+------+-------+---------------------+
|
||||
| id | name | value | gmt_create |
|
||||
+----+------+-------+---------------------+
|
||||
| 1 | CN | 10001 | 2020-04-03 16:05:45 |
|
||||
| 2 | US | 10002 | 2020-04-03 16:05:54 |
|
||||
| 3 | UK | 10003 | 2020-04-03 16:05:54 |
|
||||
| 6 | FR | NULL | 2020-04-03 16:26:22 |
|
||||
| 7 | RU | NULL | 2020-04-03 16:26:32 |
|
||||
+----+------+-------+---------------------+
|
||||
5 rows in set (0.00 sec)
|
||||
|
||||
obclient>
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,173 @@
|
||||
关于事务超时
|
||||
===========================
|
||||
|
||||
|
||||
|
||||
OceanBase 为了避免事务长时间不提交持有锁影响其他会话,设计了两个超时逻辑。一个是事务空闲超时,一个是事务未提交超时。分别由租户变量 ob_trx_idle_timeout 和 ob_trx_timeout 控制,默认值分别是 120 秒和 100 秒。通常只会有一个超时机制被触发。
|
||||
**说明**
|
||||
|
||||
|
||||
|
||||
建议变量 ob_trx_idle_timeout 使用默认值。
|
||||
|
||||
```javascript
|
||||
obclient> show variables where variable_name in ('ob_trx_idle_timeout','ob_trx_timeout');
|
||||
+---------------------+-----------+
|
||||
| VARIABLE_NAME | VALUE |
|
||||
+---------------------+-----------+
|
||||
| ob_trx_idle_timeout | 120000000 |
|
||||
| ob_trx_timeout | 100000000 |
|
||||
+---------------------+-----------+
|
||||
2 rows in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
关于事务空闲超时
|
||||
-----------------
|
||||
|
||||
OceanBase 的事务空闲时间超过一段时间还没有提交时,会自动断开连接并回滚事务,此时会话需要重新连接。
|
||||
|
||||
会话事务空闲超时时间阈值由租户变量 ob_trx_idle_timeout 控制,这个参数值建议使用默认值 120 秒,实际空闲会话断开的时间会是在 \[100s, 100s + ob_trx_idle_timeout \] 之间。
|
||||
|
||||
#### 示例:事务空闲超时报错
|
||||
|
||||
下面示例先设置事务空闲超时时间为 120 秒,事务未提交超时时间为 1000 秒。当事务空闲时间超过 120 秒后,连接会被自动断开,事务也自动被 ROLLBACK 了。
|
||||
|
||||
```javascript
|
||||
obclient> DROP TABLE IF EXISTS t_insert;
|
||||
Query OK, 0 rows affected (0.01 sec)
|
||||
|
||||
obclient> CREATE TABLE t_insert(
|
||||
id bigint NOT NULL PRIMARY KEY auto_increment
|
||||
, name varchar(10) NOT NULL
|
||||
, value bigint
|
||||
,gmt_create timestamp NOT NULL DEFAULT current_timestamp
|
||||
);
|
||||
Query OK, 0 rows affected (0.05 sec)
|
||||
|
||||
obclient> INSERT INTO t_insert(name, value) values('CN',NULL),('UK',NULL),('US',NULL);
|
||||
Query OK, 3 rows affected (0.00 sec)
|
||||
Records: 3 Duplicates: 0 Warnings: 0
|
||||
|
||||
obclient> select now(), * from t_insert t;
|
||||
+---------------------+----+------+-------+---------------------+
|
||||
| now() | id | name | value | gmt_create |
|
||||
+---------------------+----+------+-------+---------------------+
|
||||
| 2020-04-03 16:54:51 | 1 | CN | NULL | 2020-04-03 16:54:49 |
|
||||
| 2020-04-03 16:54:51 | 2 | UK | NULL | 2020-04-03 16:54:49 |
|
||||
| 2020-04-03 16:54:51 | 3 | US | NULL | 2020-04-03 16:54:49 |
|
||||
+---------------------+----+------+-------+---------------------+
|
||||
3 rows in set (0.00 sec)
|
||||
|
||||
obclient> set session ob_trx_timeout=1000000000;
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
obclient> set session ob_trx_idle_timeout=120000000;
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
obclient> begin;
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
obclient> update t_insert set gmt_create=now() where id=3;
|
||||
Query OK, 1 row affected (0.00 sec)
|
||||
Rows matched: 1 Changed: 1 Warnings: 0
|
||||
|
||||
obclient> select now(), t.* from t_insert t;
|
||||
+---------------------+----+------+-------+---------------------+
|
||||
| now() | id | name | value | gmt_create |
|
||||
+---------------------+----+------+-------+---------------------+
|
||||
| 2020-04-03 16:55:30 | 1 | CN | NULL | 2020-04-03 16:54:49 |
|
||||
| 2020-04-03 16:55:30 | 2 | UK | NULL | 2020-04-03 16:54:49 |
|
||||
| 2020-04-03 16:55:30 | 3 | US | NULL | 2020-04-03 16:55:25 |
|
||||
+---------------------+----+------+-------+---------------------+
|
||||
3 rows in set (0.00 sec)
|
||||
|
||||
<<等 100 秒不操作>>
|
||||
|
||||
obclient> select now(), t.* from t_insert t;
|
||||
ERROR-02013: Lost connection to MySQL server during query
|
||||
obclient> select now(), * from t_insert t;
|
||||
ERROR-02006: MySQL server has gone away
|
||||
No connection. Trying to reconnect...
|
||||
Connection id: 53246
|
||||
Current database: TPCC
|
||||
|
||||
+---------------------+----+------+-------+---------------------+
|
||||
| now() | id | name | value | gmt_create |
|
||||
+---------------------+----+------+-------+---------------------+
|
||||
| 2020-04-03 16:57:41 | 1 | CN | NULL | 2020-04-03 16:54:49 |
|
||||
| 2020-04-03 16:57:41 | 2 | UK | NULL | 2020-04-03 16:54:49 |
|
||||
| 2020-04-03 16:57:41 | 3 | US | NULL | 2020-04-03 16:54:49 |
|
||||
+---------------------+----+------+-------+---------------------+
|
||||
3 rows in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
关于事务未提交超时
|
||||
------------------
|
||||
|
||||
OceanBase 的事务持续时间超过一段时间还没有提交,会报超时错误。此时会话需要明确发出 ROLLBACK 命令才可以继续在会话里执行 SQL。
|
||||
|
||||
会话事务的未提交超时时间阈值是由租户变量 ob_trx_timeout 控制。
|
||||
|
||||
#### 示例:事务未提交超时报错
|
||||
|
||||
下面示例先设置事务空闲超时时间为 120 秒,事务超时时间为 100 秒。当一个事务未提交时间持续到 100 秒时,事务内部状态就变为超时状态,同时锁会释放。此后会话需要显式发出 ROLLBACK 语句。
|
||||
|
||||
```javascript
|
||||
obclient> set session ob_trx_timeout=100000000;
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
obclient> set session ob_trx_idle_timeout=120000000;
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
obclient> begin;
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
obclient> update t_insert set gmt_create=sysdate() where id=3;
|
||||
Query OK, 1 row affected (0.00 sec)
|
||||
Rows matched: 1 Changed: 1 Warnings: 0
|
||||
|
||||
obclient> select now(), t.* from t_insert t ;
|
||||
+---------------------+----+------+-------+---------------------+
|
||||
| now() | id | name | value | gmt_create |
|
||||
+---------------------+----+------+-------+---------------------+
|
||||
| 2020-04-03 16:59:56 | 1 | CN | NULL | 2020-04-03 16:54:49 |
|
||||
| 2020-04-03 16:59:56 | 2 | UK | NULL | 2020-04-03 16:54:49 |
|
||||
| 2020-04-03 16:59:56 | 3 | US | NULL | 2020-04-03 16:59:51 |
|
||||
+---------------------+----+------+-------+---------------------+
|
||||
3 rows in set (0.00 sec)
|
||||
|
||||
<<等 120 秒不操作>>
|
||||
|
||||
obclient> select now(), t.* from t_insert t ;
|
||||
ERROR-00600: internal error code, arguments: -6210, Transaction is timeout
|
||||
obclient> commit;
|
||||
ERROR-00600: internal error code, arguments: -6210, Transaction is timeout
|
||||
obclient> rollback;
|
||||
Query OK, 0 rows affected (0.00 sec)
|
||||
|
||||
obclient> select now(), t.* from t_insert t ;
|
||||
+---------------------+----+------+-------+---------------------+
|
||||
| now() | id | name | value | gmt_create |
|
||||
+---------------------+----+------+-------+---------------------+
|
||||
| 2020-04-03 17:04:13 | 1 | CN | NULL | 2020-04-03 16:54:49 |
|
||||
| 2020-04-03 17:04:13 | 2 | UK | NULL | 2020-04-03 16:54:49 |
|
||||
| 2020-04-03 17:04:13 | 3 | US | NULL | 2020-04-03 16:54:49 |
|
||||
+---------------------+----+------+-------+---------------------+
|
||||
3 rows in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
**说明**
|
||||
|
||||
|
||||
|
||||
建议不要将事务未提交超时参数设置小于 1 秒。
|
||||
|
||||
@ -0,0 +1,46 @@
|
||||
关于 DDL 语句
|
||||
==============================
|
||||
|
||||
|
||||
|
||||
DDL(Data Definition Language,数据定义语言)语句用来创建、修改和删除数据库对象。在 DDL 开始之前和之后,OceanBase 数据库会发出一个隐式的 COMMIT 语句,所以不可以回滚 DDL 语句。
|
||||
**说明**
|
||||
|
||||
|
||||
|
||||
在 obclient 命令行环境下,可以在"SQL提示符\>"后输入 DDL 语句。
|
||||
|
||||
在 ODC 的 SQL 编辑器窗口,也可以输入 DDL 语句。
|
||||
|
||||
OceanBase DDL 支持的数据库对象有:
|
||||
|
||||
* 表,包括约束、索引
|
||||
|
||||
表是最基础的数据存储单元。表包含所有用户可以访问的数据,每个表包含多行记录,每个记录由多个列组成。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- -->
|
||||
|
||||
* 视图
|
||||
|
||||
视图表示表的查询结果。大部分能使用表的地方,都可以使用视图。如果经常访问的数据分布在多个表里时,使用视图是最好的方法。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- -->
|
||||
|
||||
* 自定义函数
|
||||
|
||||
|
||||
|
||||
* 自定义类型
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
关于 SQL 数据类型
|
||||
================================
|
||||
|
||||
|
||||
|
||||
当您创建表的时候,必须指定表记录行的每一列的数据类型,数据类型定义了该列存储数据的合法格式。比如说一个 DATE 类型的列,能存储值"2020-02-20",但是不能存储值为 2 的数字或者字符串"hello"。
|
||||
|
||||
有关 SQL 数据类型的详细描述,请参考《SQL参考(MySQL模式)》,这里介绍一些常用的数据类型。
|
||||
|
||||
|
||||
| **分类** | **类型** | **备注** |
|
||||
|----------------|---------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| 数值类型-整形 | bigint | 有符号:\[-2\^63, 2\^63 - 1\] 无符号:\[0, 2\^64 - 1\] |
|
||||
| 数值类型-整形 | int integer | 有符号:\[-2\^31, 2\^31-1\] 无符号:\[0, 2\^32-1\] |
|
||||
| 数值类型-整形 | smallint | 有符号:\[-2\^15, 2\^15-1\] 无符号:\[0, 2\^16-1\] |
|
||||
| 数值类型-整形 | bool boolean tinyint | 有符号:\[-2\^7, 2\^7-1\] 无符号:\[0, 2\^8-1\] |
|
||||
| 数值类型-定点 | decimal(p, s) | decimal 等同于 numeric |
|
||||
| 数值类型-浮点 | float | 有符号:\[-2\^128, 2\^128\] 无符号:\[-2\^1024, 2\^1024\] 精度7位 |
|
||||
| 数值类型-浮点 | double | 有符号:\[-2\^1024, 2\^1024\] 无符号:\[0, 2\^1024\] 精度15位 |
|
||||
| 数值类型- 整形/定点/浮点 | number number(p) number(p, s) | p(precision) 为精度,s(scale) 表示小数点右边的数字个数,精度最大值为 38,scale 的取值范围为 -84 到 127。 * p 和 s 都有表示定点数。 * s 为 0 表示整形。 * p 和 s 都不指定,表示浮点数,最大精度 38。 |
|
||||
| 字符类型-变长 | varchar(N) | 最长 256K,字符集 UTF8MB4 |
|
||||
| 字符类型-变长 | varbinary | 最初 256K,字符集 BINARY |
|
||||
| 字符类型-变长 | enum | 最多 65535 个元素,每个元素最长 255 个字符,字符集 UTF8MB4 |
|
||||
| 字符类型-变长 | set | 最多 64 个元素,每个元素最长255 个字符,字符集 UTF8MB4 |
|
||||
| 字符类型-定长 | char(N) | 最大 256,字符集 UTF8MB4 |
|
||||
| 字符类型-定长 | binary | 最大 256,字符集 BINARY |
|
||||
| 时间类型 | date | YYYY-MM-DD,只包含日期 |
|
||||
| 时间类型 | time | HH:MM:SS\[.fraction\],只包含时间。 |
|
||||
| 时间类型 | datetime | YYYY-MM-DD HH:MM:SS\[.fraction\],包含日期时间(不考虑时区)。 |
|
||||
| 时间类型 | Timestamp | 日期时间(考虑时区)。 |
|
||||
| 时间类型 | year | YYYY,\[1901, 2155\] |
|
||||
| 大对象 | Text / blob | 最大 64K |
|
||||
| 大对象 | Longtext /longblob | 最大 48M |
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,265 @@
|
||||
创建表
|
||||
========================
|
||||
|
||||
|
||||
|
||||
可以通过执行 DDL 语句 CREATE TABLE 来创建一个表。
|
||||
|
||||
使用 CREATE TABLE 语句建表
|
||||
-----------------------------
|
||||
|
||||
* 下面示例使用 CREATE TABLE 语句创建订单表 ware 和 cust 表。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> create table ware(w_id int
|
||||
, w_ytd decimal(12,2)
|
||||
, w_tax decimal(4,4)
|
||||
, w_name varchar(10)
|
||||
, w_street_1 varchar(20)
|
||||
, w_street_2 varchar(20)
|
||||
, w_city varchar(20)
|
||||
, w_state char(2)
|
||||
, w_zip char(9)
|
||||
, unique(w_name, w_city)
|
||||
, primary key(w_id)
|
||||
);
|
||||
Query OK, 0 rows affected (0.06 sec)
|
||||
|
||||
obclient> create table cust (c_w_id int NOT NULL
|
||||
, c_d_id int NOT null
|
||||
, c_id int NOT null
|
||||
, c_discount decimal(4, 4)
|
||||
, c_credit char(2)
|
||||
, c_last varchar(16)
|
||||
, c_first varchar(16)
|
||||
, c_middle char(2)
|
||||
, c_balance decimal(12, 2)
|
||||
, c_ytd_payment decimal(12, 2)
|
||||
, c_payment_cnt int
|
||||
, c_credit_lim decimal(12, 2)
|
||||
, c_street_1 varchar(20)
|
||||
, c_street_2 varchar(20)
|
||||
, c_city varchar(20)
|
||||
, c_state char(2)
|
||||
, c_zip char(9)
|
||||
, c_phone char(16)
|
||||
, c_since date
|
||||
, c_delivery_cnt int
|
||||
, c_data varchar(500)
|
||||
, index icust(c_last, c_d_id, c_w_id, c_first, c_id)
|
||||
, FOREIGN KEY (c_w_id) REFERENCES ware(w_id)
|
||||
, primary key (c_w_id, c_d_id, c_id)
|
||||
);
|
||||
Query OK, 0 rows affected (0.06 sec)
|
||||
```
|
||||
|
||||
|
||||
**说明**
|
||||
|
||||
|
||||
|
||||
由于 ALTER TABLE 语法不支持后期增加主键,所以需要在建表的时候设置主键。
|
||||
|
||||
|
||||
|
||||
使用 CREATE TABLE 复制表数据
|
||||
------------------------------
|
||||
|
||||
在 MySQL 租户里,可以使用 CREATE TABLE AS SELECT 复制表的数据,但是结构并不完全一致,会丢失约束、索引、默认值、分区等信息。使用 CREATE TABLE LIKE 可以复制表结构,但是不包括数据。
|
||||
|
||||
* 示例:MySQL 租户的 CREATE TABLE 复制表结构和数据的区别
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> create table t1(
|
||||
id bigint not null primary KEY
|
||||
, name varchar(50) not NULL
|
||||
, gmt_create timestamp not null default current_timestamp
|
||||
) partition by hash(id) partitions 8;
|
||||
Query OK, 0 rows affected (0.10 sec)
|
||||
|
||||
obclient> insert into t1(id,name) values(1,'A'),(2,'B'),(3,'C');
|
||||
Query OK, 3 rows affected (0.03 sec)
|
||||
Records: 3 Duplicates: 0 Warnings: 0
|
||||
|
||||
obclient> create table t1_like like t1;
|
||||
Query OK, 0 rows affected (0.11 sec)
|
||||
|
||||
obclient> create table t1_copy as select * from t1;
|
||||
Query OK, 3 rows affected (0.12 sec)
|
||||
|
||||
obclient> show create table t1_like\G
|
||||
*************************** 1. row ***************************
|
||||
Table: t1_like
|
||||
Create Table: CREATE TABLE `t1_like` (
|
||||
`id` bigint(20) NOT NULL,
|
||||
`name` varchar(50) NOT NULL,
|
||||
`gmt_create` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.0' REPLICA_NUM = 3 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 10 PROGRESSIVE_MERGE_NUM = 2
|
||||
partition by hash(id)
|
||||
(partition p0,
|
||||
partition p1,
|
||||
partition p2,
|
||||
partition p3,
|
||||
partition p4,
|
||||
partition p5,
|
||||
partition p6,
|
||||
partition p7)
|
||||
1 row in set (0.02 sec)
|
||||
|
||||
obclient> show create table t1_copy\G
|
||||
*************************** 1. row ***************************
|
||||
Table: t1_copy
|
||||
Create Table: CREATE TABLE `t1_copy` (
|
||||
`id` bigint(20) DEFAULT NULL,
|
||||
`name` varchar(50) DEFAULT NULL,
|
||||
`gmt_create` timestamp NULL DEFAULT NULL
|
||||
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.0' REPLICA_NUM = 3 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 10
|
||||
1 row in set (0.00 sec)
|
||||
|
||||
obclient> show create table t1\G
|
||||
*************************** 1. row ***************************
|
||||
Table: t1
|
||||
Create Table: CREATE TABLE `t1` (
|
||||
`id` bigint(20) NOT NULL,
|
||||
`name` varchar(50) NOT NULL,
|
||||
`gmt_create` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.0' REPLICA_NUM = 3 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 10 PROGRESSIVE_MERGE_NUM = 2
|
||||
partition by hash(id)
|
||||
(partition p0,
|
||||
partition p1,
|
||||
partition p2,
|
||||
partition p3,
|
||||
partition p4,
|
||||
partition p5,
|
||||
partition p6,
|
||||
partition p7)
|
||||
1 row in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
关于表和分区
|
||||
---------------
|
||||
|
||||
在 OceanBase 数据库中,数据存储在表中,而数据表示的最小粒度是分区。普通的非分区表,就只有一个分区;而分区表,通常有多个分区,分区名默认以 p 开头,按数字顺序从 0 开始编号。所以分区是表的子集。
|
||||
|
||||
通常分区对用户的应用是透明的,应用只需要使用 SQL 读写表即可。只有某些场景下,为了提升分区表的查询性能,应用也可以使用 SQL 直接访问某个具体的分区,SQL 语法格式是:
|
||||
|
||||
```javascript
|
||||
SELECT ... FROM parted_table PARTITION (pN) WHERE query_condition ;
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 示例:通过 SQL 直接访问分区表的分区
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> select o_id,o_c_id,o_carrier_id,o_ol_cnt,o_all_local,o_entry_d from ordr partition (p1) where o_w_id=1 and o_d_id=2 and o_id=2100;
|
||||
+------+--------+--------------+----------+-------------+------------+
|
||||
| o_id | o_c_id | o_carrier_id | o_ol_cnt | o_all_local | o_entry_d |
|
||||
+------+--------+--------------+----------+-------------+------------+
|
||||
| 2100 | 8 | 8 | 11 | 1 | 2020-02-15 |
|
||||
+------+--------+--------------+----------+-------------+------------+
|
||||
1 row in set (0.01 sec)
|
||||
|
||||
obclient> select ol_o_id, ol_number,ol_delivery_d,ol_amount,ol_i_id,ol_supply_w_id,ol_quantity from ordl partition (p1) where ol_w_id=1 and ol_d_id=2 and ol_o_id=2100;
|
||||
+---------+-----------+---------------+-----------+---------+----------------+-------------+
|
||||
| ol_o_id | ol_number | ol_delivery_d | ol_amount | ol_i_id | ol_supply_w_id | ol_quantity |
|
||||
+---------+-----------+---------------+-----------+---------+----------------+-------------+
|
||||
| 2100 | 1 | 2020-02-15 | 0.00 | 87133 | 1 | 5 |
|
||||
| 2100 | 2 | 2020-02-15 | 0.00 | 47413 | 1 | 5 |
|
||||
| 2100 | 3 | 2020-02-15 | 0.00 | 9115 | 1 | 5 |
|
||||
| 2100 | 4 | 2020-02-15 | 0.00 | 42985 | 1 | 5 |
|
||||
| 2100 | 5 | 2020-02-15 | 0.00 | 43621 | 1 | 5 |
|
||||
| 2100 | 6 | 2020-02-15 | 0.00 | 5787 | 1 | 5 |
|
||||
| 2100 | 7 | 2020-02-15 | 0.00 | 62576 | 1 | 5 |
|
||||
| 2100 | 8 | 2020-02-15 | 0.00 | 91592 | 1 | 5 |
|
||||
| 2100 | 9 | 2020-02-15 | 0.00 | 34452 | 1 | 5 |
|
||||
| 2100 | 10 | 2020-02-15 | 0.00 | 13792 | 1 | 5 |
|
||||
| 2100 | 11 | 2020-02-15 | 0.00 | 94326 | 1 | 5 |
|
||||
+---------+-----------+---------------+-----------+---------+----------------+-------------+
|
||||
11 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
**说明**
|
||||
|
||||
|
||||
|
||||
如果是组合分区,可以访问更细粒度的分区,详细描述请参考"分区路由"章节。
|
||||
|
||||
在 OceanBase 数据库里,节点间的数据迁移的最小粒度是分区,每个分区在集群里有三个副本,内容保持同步,角色上有区分。三副本会有一个主副本(Leader 副本)和两个备副本(Follower 副本),只有主副本可以提供写服务,默认也只有主副本可以提供读服务。主副本上的事务提交时会将事务日志同步到两个备副本,三副本使用 Paxos 协议表决事务是否提交成功。有时候为了不影响主副本,可以让备副本承担部分读请求,这就是应用常用的读写分离的解决方案,这种读备称为 **弱一致性读** 。使用这种方案,应用读需要承担读延时的风险,这个延时最大允许值会通过参数(max_stale_time_for_weak_consistency)控制。
|
||||
|
||||
* 示例:使用 SQL Hint 实现读写分离。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
弱一致读的 Hint 语法是 /\*+ read_consistency(weak) \*/ 。通常的读默认是强一致性读,就不用 Hint 了。
|
||||
|
||||
```javascript
|
||||
obclient> select /*+ read_consistency(weak) */ o_id,o_c_id,o_carrier_id,o_ol_cnt,o_all_local,o_entry_d from ordr where o_w_id=1 and o_d_id=2 and o_id=2100;
|
||||
+------+--------+--------------+----------+-------------+------------+
|
||||
| o_id | o_c_id | o_carrier_id | o_ol_cnt | o_all_local | o_entry_d |
|
||||
+------+--------+--------------+----------+-------------+------------+
|
||||
| 2100 | 8 | 8 | 11 | 1 | 2020-02-15 |
|
||||
+------+--------+--------------+----------+-------------+------------+
|
||||
1 row in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
复制表
|
||||
------------
|
||||
|
||||
复制表是分布式数据库 OceanBase 的高级优化手段。
|
||||
|
||||
通常 OceanBase 集群是三副本架构,默认每个表的每个分区在 OceanBase 中会有三个副本数据,角色上分为一个主副本(Leader 副本)和两个备副本(Follower副本),默认提供读写服务的是主副本。
|
||||
|
||||
复制表可以指定在租户的每台机器上都有一个备副本,并且主副本跟所有备份的数据使用全同步策略保持强同步。这样做的目的是为了让业务有些 SQL 关联查询时能在同一节点内部执行,以获取更好的性能。
|
||||
|
||||
复制表的语法是在 CREATE TABLE 语句后增加 `DUPLICATE_SCOPE` 选项。
|
||||
|
||||
* 示例:创建复制表。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> create table item (i_id int
|
||||
, i_name varchar(24)
|
||||
, i_price decimal(5,2)
|
||||
, i_data varchar(50)
|
||||
, i_im_id int
|
||||
, primary key(i_id)) pctfree=0 BLOCK_SIZE=16384
|
||||
duplicate_scope='cluster' locality='F@zone1,F@zone2,R{all_server}@zone3' primary_zone='zone1';
|
||||
Query OK, 0 rows affected (0.06 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,81 @@
|
||||
关于自增列
|
||||
==========================
|
||||
|
||||
|
||||
|
||||
如果创建表时需要某个数值列的值不重复并且保持递增,这就是自增列。在 MySQL 租户里,列的类型可以定义为 AUTO_INCREMENT,即 MySQL 租户的自增列。
|
||||
|
||||
自增列有三个重要属性:自增起始值、自增步长、自增列缓存大小,通过以下三个租户变量参数控制。
|
||||
|
||||
```javascript
|
||||
obclient> show variables where variable_name in ('auto_increment_increment','auto_increment_offset','auto_increment_cache_size');
|
||||
+---------------------------+---------+
|
||||
| Variable_name | Value |
|
||||
+---------------------------+---------+
|
||||
| auto_increment_cache_size | 1000000 |
|
||||
| auto_increment_increment | 1 |
|
||||
| auto_increment_offset | 1 |
|
||||
+---------------------------+---------+
|
||||
3 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**示例:CREATE TABLE 使用自增列**
|
||||
|
||||
下面创建了一个自增列,在使用 INSERT 插入记录的时候不需要指定自增列,OceanBase 数据库会自动为该列填充值。
|
||||
|
||||
```javascript
|
||||
obclient> create table t1(id bigint not null auto_increment primary key, name varchar(50), gmt_create timestamp not null default current_timestamp);
|
||||
Query OK, 0 rows affected (0.08 sec)
|
||||
|
||||
obclient> insert into t1(name) values('A'),('B'),('C');
|
||||
Query OK, 3 rows affected (0.01 sec)
|
||||
Records: 3 Duplicates: 0 Warnings: 0
|
||||
|
||||
obclient> select * from t1;
|
||||
+----+------+---------------------+
|
||||
| id | name | gmt_create |
|
||||
+----+------+---------------------+
|
||||
| 1 | A | 2020-04-03 17:09:55 |
|
||||
| 2 | B | 2020-04-03 17:09:55 |
|
||||
| 3 | C | 2020-04-03 17:09:55 |
|
||||
+----+------+---------------------+
|
||||
3 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
假如在 INSERT 时指定了自增列的值,如果这个值是0,则 OceanBase 数据库会用自增列的下一个值填充列的值;如果这个值比当前最大值小,则不影响自增列的下一个值的计算;如果这个值比当前值最大值大,则自增列会把插入值和自增列缓存值的和作为下次自增的起始值。
|
||||
|
||||
```javascript
|
||||
obclient> insert into t1(id, name) values(0, 'D');
|
||||
Query OK, 1 row affected (0.00 sec)
|
||||
|
||||
obclient> insert into t1(id, name) values(-1,'E');
|
||||
Query OK, 1 row affected (0.00 sec)
|
||||
|
||||
obclient> insert into t1(id, name) values(10,'F');
|
||||
Query OK, 1 row affected (0.01 sec)
|
||||
|
||||
obclient> insert into t1(name) values('G');
|
||||
Query OK, 1 row affected (0.00 sec)
|
||||
|
||||
obclient> select * from t1;
|
||||
+---------+------+---------------------+
|
||||
| id | name | gmt_create |
|
||||
+---------+------+---------------------+
|
||||
| -1 | E | 2020-04-03 17:10:24 |
|
||||
| 1 | A | 2020-04-03 17:09:55 |
|
||||
| 2 | B | 2020-04-03 17:09:55 |
|
||||
| 3 | C | 2020-04-03 17:09:55 |
|
||||
| 4 | D | 2020-04-03 17:10:19 |
|
||||
| 10 | F | 2020-04-03 17:10:29 |
|
||||
| 1000011 | G | 2020-04-03 17:10:34 |
|
||||
+---------+------+---------------------+
|
||||
7 rows in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,87 @@
|
||||
关于列的约束类型
|
||||
=============================
|
||||
|
||||
|
||||
|
||||
为了确保表里的数据符合业务规则,可以在列上定义约束。
|
||||
|
||||
约束定义在列上,限制了列里存储的值。当尝试在该列上写入或更新为违反约束定义的值时,会触发一个错误并回滚这个操作;当尝试在已有的表的列上加上一个跟现有数据相冲突的约束时,也会触发一个错误并回滚这个操作。
|
||||
|
||||
约束的类型有:
|
||||
|
||||
* 非空约束(NOT NULL),不允许约束包含的列的值包含 NULL。
|
||||
|
||||
如 ware 表的 w_name 列类型后面有 not null 约束,表示业务约束每个仓库必须有个名称。有非空约束的列,在 INSERT 语句中必须指明该列的值,除非该列还定义了默认值。如 cust 表的列 c_discount 定义了默认值 0.99,即业务上每个人默认折扣是 0.99。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- -->
|
||||
|
||||
* 唯一约束(UNIQUE),不允许约束包含的列的值有重复值,但是可以有多个 NULL 值。
|
||||
|
||||
如 ware 表的 (w_name, w_city) 列上有个唯一约束,表示每个城市里仓库的名称必须是不重复的。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- -->
|
||||
|
||||
* 主键约束(PRIMARY KEY),是 NOT NULL 约束和唯一约束的组合。
|
||||
|
||||
如 ware 表和 cust 表都有个主键 w_id 和 c_id ,这两列不允许为 NULL 并且必须是不重复的。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- -->
|
||||
|
||||
* 外键约束(FOREIGN KEY),要求约束的列的值取自于另外一个表的主键列。
|
||||
|
||||
如 cust 表的 c_w_id 上有个外键约束引用了 ware 表的 w_id 列,表示业务上顾客归属的仓库必须是属于仓库表里的仓库。
|
||||
|
||||
OceanBase 数据库默认是开启外键约束的,通过租户变量 foreign_key_checks 控制。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
关于时间列的默认时间设置
|
||||
---------------------
|
||||
|
||||
当列上有 NOT NULL 约束时,通常建议设置默认值。当列类型是日期或时间类型时,可以设置默认值为数据库当前时间。
|
||||
|
||||
* 示例:为表的时间列设置默认值,可以使用 current_timestamp 函数。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> create table t1(
|
||||
id bigint not null primary KEY
|
||||
, gmt_create datetime not null default current_timestamp
|
||||
, gmt_modified datetime not null default current_timestamp
|
||||
);
|
||||
Query OK, 0 rows affected (0.07 sec)
|
||||
|
||||
obclient> insert into t1(id) values(1),(2),(3);
|
||||
Query OK, 3 rows affected (0.01 sec)
|
||||
Records: 3 Duplicates: 0 Warnings: 0
|
||||
|
||||
obclient> select * from t1;
|
||||
+----+---------------------+---------------------+
|
||||
| id | gmt_create | gmt_modified |
|
||||
+----+---------------------+---------------------+
|
||||
| 1 | 2020-02-27 17:09:23 | 2020-02-27 17:09:23 |
|
||||
| 2 | 2020-02-27 17:09:23 | 2020-02-27 17:09:23 |
|
||||
| 3 | 2020-02-27 17:09:23 | 2020-02-27 17:09:23 |
|
||||
+----+---------------------+---------------------+
|
||||
3 rows in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,128 @@
|
||||
关于表的索引
|
||||
===========================
|
||||
|
||||
|
||||
|
||||
可以在表的一个或多个列上创建索引以加速表上的 SQL 语句执行速度。索引使用正确的话,可以减少物理 IO 或者逻辑 IO。
|
||||
|
||||
如果创建表时同时设置了主键,OceanBase 数据库会默认创建一个唯一索引。以下面的 SQL 为例:
|
||||
|
||||
```javascript
|
||||
obclient> DROP TABLE IF EXISTS t1;
|
||||
Query OK, 0 rows affected (0.01 sec)
|
||||
|
||||
obclient> CREATE TABLE t1(id bigint not null primary key, name varchar(50));
|
||||
Query OK, 0 rows affected (0.05 sec)
|
||||
|
||||
obclient> show indexes from t1\G
|
||||
*************************** 1. row ***************************
|
||||
Table: t1
|
||||
Non_unique: 0
|
||||
Key_name: PRIMARY
|
||||
Seq_in_index: 1
|
||||
Column_name: id
|
||||
Collation: A
|
||||
Cardinality: NULL
|
||||
Sub_part: NULL
|
||||
Packed: NULL
|
||||
Null:
|
||||
Index_type: BTREE
|
||||
Comment: available
|
||||
Index_comment:
|
||||
Visible: YES
|
||||
1 row in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
新增索引
|
||||
-------------
|
||||
|
||||
为表增加索引可以通过 CREATE INDEX 语句。OceanBase 能在普通表和分区表上创建索引,索引可以是本地索引或者全局索引。同时索引可以是唯一索引或者普通索引,如果是分区表的唯一索引,唯一索引必须包含表分区的拆分键。
|
||||
|
||||
创建索引的 SQL 语法格式如下:
|
||||
|
||||
```javascript
|
||||
CREATE [UNIQUE] INDEX index_name ON table_name ( column_list ) [LOCAL | GLOBAL] [ PARTITION BY column_list PARTITIONS N ] ;
|
||||
```
|
||||
|
||||
|
||||
|
||||
MySQL 租户里,索引名称在表范围内不能重复,查看索引可以通过命令 SHOW INDEXES 。
|
||||
|
||||
在 MySQL 租户里,新增索引还有一种方法,SQL 语法格式如下:
|
||||
|
||||
```javascript
|
||||
ALTER TABLE table_name
|
||||
ADD|DROP INDEX|KEY index_name ( column_list ) ;
|
||||
```
|
||||
|
||||
|
||||
|
||||
可以一次增加多个索引,索引关键字用 INDEX 或 KEY 都可以。
|
||||
|
||||
* 示例:对分区表新增索引
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> create table t3(
|
||||
id bigint not null primary KEY
|
||||
, name varchar(50)
|
||||
, gmt_create timestamp not null default current_timestamp
|
||||
) partition by hash(id) partitions 8;
|
||||
Query OK, 0 rows affected (0.14 sec)
|
||||
|
||||
obclient> alter table t3 add unique key t3_uk (name) local;
|
||||
ERROR 1503 (HY000): A UNIQUE INDEX must include all columns in the table's partitioning function
|
||||
|
||||
obclient> alter table t3
|
||||
add unique key t3_uk (name, id) LOCAL
|
||||
, add key t3_ind3(gmt_create) global;
|
||||
Query OK, 0 rows affected (18.03 sec)
|
||||
|
||||
obclient> show indexes from t3;
|
||||
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+-----------+---------------+---------+
|
||||
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible |
|
||||
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+-----------+---------------+---------+
|
||||
| t3 | 0 | PRIMARY | 1 | id | A | NULL | NULL | NULL | | BTREE | available | | YES |
|
||||
| t3 | 0 | t3_uk | 1 | name | A | NULL | NULL | NULL | YES | BTREE | available | | YES |
|
||||
| t3 | 0 | t3_uk | 2 | id | A | NULL | NULL | NULL | | BTREE | available | | YES |
|
||||
| t3 | 1 | t3_ind3 | 1 | gmt_create | A | NULL | NULL | NULL | | BTREE | available | | YES |
|
||||
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+-----------+---------------+---------+
|
||||
4 rows in set (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
删除索引
|
||||
-------------
|
||||
|
||||
删除索引的语法格式如下:
|
||||
|
||||
```javascript
|
||||
ALTER TABLE table_name DROP key|index index_name ;
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 示例:删除表的索引
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> alter table t3 drop key t3_uk, drop key t3_ind3;
|
||||
Query OK, 0 rows affected (0.07 sec)
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
闪回被删除的表
|
||||
============================
|
||||
|
||||
|
||||
|
||||
OceanBase 支持回收站功能,通过回收站可以闪回已删除的表。默认回收站是开启的,回收站是否开启由租户变量 recyclebin 控制。
|
||||
|
||||
开启/关闭回收站的语法是:
|
||||
|
||||
```javascript
|
||||
set global recyclebin = ON | OFF ;
|
||||
```
|
||||
|
||||
|
||||
|
||||
修改回收站的开启状态后,只对后续新连接会话生效。
|
||||
|
||||
* 示例:闪回被删除的表。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
obclient> drop table if exists t1;
|
||||
Query OK, 0 rows affected (0.02 sec)
|
||||
|
||||
obclient> create table t1(id bigint not null primary key, gmt_create datetime not null default current_timestamp);
|
||||
Query OK, 0 rows affected (0.09 sec)
|
||||
|
||||
obclient> insert into t1(id) values(1),(2),(3);
|
||||
Query OK, 3 rows affected (0.02 sec)
|
||||
Records: 3 Duplicates: 0 Warnings: 0
|
||||
|
||||
obclient> select * from t1;
|
||||
+----+---------------------+
|
||||
| ID | GMT_CREATE |
|
||||
+----+---------------------+
|
||||
| 1 | 2020-02-28 09:47:07 |
|
||||
| 2 | 2020-02-28 09:47:07 |
|
||||
| 3 | 2020-02-28 09:47:07 |
|
||||
+----+---------------------+
|
||||
3 rows in set (0.00 sec)
|
||||
|
||||
obclient> drop table t1;
|
||||
Query OK, 0 rows affected (0.03 sec)
|
||||
|
||||
obclient> show recyclebin\G
|
||||
*************************** 1. row ***************************
|
||||
OBJECT_NAME: __recycle_$_20200102_1585650066255592
|
||||
ORIGINAL_NAME: t1
|
||||
TYPE: TABLE
|
||||
CREATETIME: 2020-03-31 18:21:06.255716
|
||||
1 row in set (0.03 sec)
|
||||
|
||||
obclient> flashback table __recycle_$_20200102_1585650066255592 to before drop rename to t1;
|
||||
Query OK, 0 rows affected (0.02 sec)
|
||||
|
||||
obclient> select * from t1;
|
||||
+----+---------------------+
|
||||
| ID | GMT_CREATE |
|
||||
+----+---------------------+
|
||||
| 1 | 2020-02-28 09:47:07 |
|
||||
| 2 | 2020-02-28 09:47:07 |
|
||||
| 3 | 2020-02-28 09:47:07 |
|
||||
+----+---------------------+
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
概述
|
||||
=======================
|
||||
|
||||
|
||||
|
||||
分区技术(Partitioning)是 OceanBase 非常重要的分布式能力之一,它能解决大表的容量问题和高并发访问时性能问题,主要思想就是将大表拆分为更多更小的结构相同的独立对象,即分区。普通的表只有一个分区,可以看作分区表的特例。每个分区只能存在于一个节点内部,分区表的不同分区可以分散在不同节点上。
|
||||
@ -0,0 +1,34 @@
|
||||
分区路由
|
||||
=========================
|
||||
|
||||
|
||||
|
||||
OceanBase 的分区表是内建功能,您只需要在建表的时候指定分区策略和分区数即可。分区表的查询 SQL 跟普通表是一样的,OceanBase 的 OBProxy 或 OBServer 会自动将用户 SQL 路由到相应节点内,因此,分区表的分区细节对业务是透明的。
|
||||
|
||||
如果知道要读取的数据所在的分区号,可以通过 SQL 直接访问分区表的某个分区。简单语法格式如下:
|
||||
|
||||
```javascript
|
||||
part_table partition ( p[0,1,...][sp[0,1,...]] )
|
||||
```
|
||||
|
||||
|
||||
|
||||
默认情况下,除非表定义了分区名,分区名都是按一定规则编号,例如:
|
||||
|
||||
* 一级分区名为:p0 , p1 , p2 , ...
|
||||
|
||||
* 二级分区名为:p0sp0 , p0sp1 , p0sp2 , ... ; p1sp0 , p1sp1 , p1sp2 , ...
|
||||
|
||||
|
||||
|
||||
|
||||
示例:访问分区表的具体分区。
|
||||
|
||||
```javascript
|
||||
select * from t_log partition (p0) ;
|
||||
|
||||
select * from t_log partition (p5sp0) ;
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,380 @@
|
||||
分区策略
|
||||
=========================
|
||||
|
||||
|
||||
|
||||
OceanBase 支持多种分区策略:
|
||||
|
||||
* 范围(RANGE)分区
|
||||
|
||||
|
||||
|
||||
* RANGE COLUMNS 分区
|
||||
|
||||
|
||||
|
||||
* 列表(LIST)分区
|
||||
|
||||
|
||||
|
||||
* LIST COLUMNS 分区
|
||||
|
||||
|
||||
|
||||
* 哈希(HASH)分区
|
||||
|
||||
|
||||
|
||||
* 组合分区
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
范围(RANGE)分区
|
||||
--------------------
|
||||
|
||||
RANGE 分区根据分区表定义时为每个分区建立的分区键值范围,将数据映射到相应的分区中。它是常见的分区类型,经常跟日期类型一起使用。比如说,可以将业务日志表按日/周/月分区。RANGE 分区简单的语法格式如下:
|
||||
|
||||
```javascript
|
||||
CREATE TABLE table_name (
|
||||
column_name1 column_type
|
||||
[, column_nameN column_type]
|
||||
) PARTITION BY RANGE ( expr(column_name1) )
|
||||
(
|
||||
PARTITION p0 VALUES LESS THAN ( expr )
|
||||
[, PARTITION pN VALUES LESS THAN (expr ) ]
|
||||
[, PARTITION pX VALUES LESS THAN (maxvalue) ]
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
|
||||
当使用 RANGE 分区时,需要遵守如下几个规则:
|
||||
|
||||
* PARTITION BY RANGE ( expr ) 里的 expr 表达式的结果必须为整形。
|
||||
|
||||
|
||||
|
||||
* 每个分区都有一个 VALUES LESS THAN 子句,它为分区指定一个非包含的上限值。分区键的任何值等于或大于这个值时将被映射到下一个分区中。
|
||||
|
||||
|
||||
|
||||
* 除第一个分区外,所有分区都隐含一个下限值,即上一个分区的上限值。
|
||||
|
||||
|
||||
|
||||
* 允许且只允许最后一个分区上限定义为 MAXVALUE ,这个值没有具体的数值,比其他所有分区的上限都要大,也包含空值。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
示例:创建一个 RANGE 分区表。
|
||||
|
||||
```javascript
|
||||
CREATE TABLE t_log_part_by_range (
|
||||
log_id bigint NOT NULL
|
||||
, log_value varchar(50)
|
||||
, log_date timestamp NOT NULL
|
||||
) PARTITION BY RANGE(UNIX_TIMESTAMP(log_date))
|
||||
(
|
||||
PARTITION M202001 VALUES LESS THAN(UNIX_TIMESTAMP('2020/02/01'))
|
||||
, PARTITION M202002 VALUES LESS THAN(UNIX_TIMESTAMP('2020/03/01'))
|
||||
, PARTITION M202003 VALUES LESS THAN(UNIX_TIMESTAMP('2020/04/01'))
|
||||
, PARTITION M202004 VALUES LESS THAN(UNIX_TIMESTAMP('2020/05/01'))
|
||||
, PARTITION M202005 VALUES LESS THAN(UNIX_TIMESTAMP('2020/06/01'))
|
||||
, PARTITION M202006 VALUES LESS THAN(UNIX_TIMESTAMP('2020/07/01'))
|
||||
, PARTITION M202007 VALUES LESS THAN(UNIX_TIMESTAMP('2020/08/01'))
|
||||
, PARTITION M202008 VALUES LESS THAN(UNIX_TIMESTAMP('2020/09/01'))
|
||||
, PARTITION M202009 VALUES LESS THAN(UNIX_TIMESTAMP('2020/10/01'))
|
||||
, PARTITION M202010 VALUES LESS THAN(UNIX_TIMESTAMP('2020/11/01'))
|
||||
, PARTITION M202011 VALUES LESS THAN(UNIX_TIMESTAMP('2020/12/01'))
|
||||
, PARTITION M202012 VALUES LESS THAN(UNIX_TIMESTAMP('2021/01/01'))
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
|
||||
RANGE 分区可以新增、删除分区。如果最后一个 RANGE 分区指定了 MAXVALUE ,则不能新增分区。
|
||||
|
||||
RANGE 分区要求表拆分键表达式的结果必须为整型,如果要按时间类型列做 RANGE 分区,则必须使用 timestamp 类型,并且使用函数 UNIX_TIMESTAMP 将时间类型转换为数值。这个需求也可以使用 RANGE COLUMNS 分区实现,就没有整型这个要求。
|
||||
|
||||
RANGE COLUMNS 分区
|
||||
-------------------------
|
||||
|
||||
RANGE COLUMNS 分区作用跟 RANGE 分区基本类似,不同之处在于:
|
||||
|
||||
* RANGE COLUMNS 拆分列结果不要求是整型,可以是任意类型。
|
||||
|
||||
|
||||
|
||||
* RANGE COLUMNS 拆分列不能使用表达式。
|
||||
|
||||
|
||||
|
||||
* RANGE COLUMNS 拆分列可以写多个列(即列向量)。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
RANGE COLUMNS 分区的简单语法格式如下:
|
||||
|
||||
```javascript
|
||||
CREATE TABLE table_name (
|
||||
column_name1 column_type
|
||||
[, column_nameN column_type]
|
||||
) PARTITION BY RANGE ( column_name1 [, column_name2] )
|
||||
(
|
||||
PARTITION p0 VALUES LESS THAN ( expr )
|
||||
[, PARTITION pN VALUES LESS THAN (expr ) ]
|
||||
[, PARTITION pX VALUES LESS THAN (maxvalue) ]
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
|
||||
示例:创建一个 RANGE COLUMNS 分区
|
||||
|
||||
```javascript
|
||||
CREATE TABLE t_log_part_by_range_columns (
|
||||
log_id bigint NOT NULL
|
||||
, log_value varchar(50)
|
||||
, log_date date NOT NULL
|
||||
) PARTITION BY RANGE COLUMNS(log_date)
|
||||
(
|
||||
PARTITION M202001 VALUES LESS THAN('2020/02/01')
|
||||
, PARTITION M202002 VALUES LESS THAN('2020/03/01')
|
||||
, PARTITION M202003 VALUES LESS THAN('2020/04/01')
|
||||
, PARTITION M202004 VALUES LESS THAN('2020/05/01')
|
||||
, PARTITION M202005 VALUES LESS THAN('2020/06/01')
|
||||
, PARTITION M202006 VALUES LESS THAN('2020/07/01')
|
||||
, PARTITION M202007 VALUES LESS THAN('2020/08/01')
|
||||
, PARTITION M202008 VALUES LESS THAN('2020/09/01')
|
||||
, PARTITION M202009 VALUES LESS THAN('2020/10/01')
|
||||
, PARTITION M202010 VALUES LESS THAN('2020/11/01')
|
||||
, PARTITION M202011 VALUES LESS THAN('2020/12/01')
|
||||
, PARTITION M202012 VALUES LESS THAN('2021/01/01')
|
||||
, PARTITION MMAX VALUES LESS THAN MAXVALUE
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
|
||||
列表(LIST)分区
|
||||
-------------------
|
||||
|
||||
LIST 分区使得您可以显式的控制记录行如何映射到分区,具体方法是为每个分区的分区键指定一组离散值列表,这点跟 RANGE 分区和 HASH 分区都不同。LIST 分区的优点是可以方便的对无序或无关的数据集进行分区。LIST 分区的简单语法格式如下:
|
||||
|
||||
```javascript
|
||||
CREATE TABLE table_name (
|
||||
column_name1 column_type
|
||||
[, column_nameN column_type]
|
||||
) PARTITION BY LIST ( expr(column_name1) )
|
||||
(
|
||||
PARTITION p0 VALUES IN ( v01 [, v0N] )
|
||||
[, PARTITION pN VALUES IN ( vN1 [, vNN] ) ]
|
||||
[, PARTITION pX VALUES IN (default) ]
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
|
||||
当使用列表分区时,需要遵守以下规则:
|
||||
|
||||
* 分区表达式结果必须是整型。
|
||||
|
||||
|
||||
|
||||
* 分区表达式只能引用一列,不能有多列(即列向量)。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
示例:创建一个 LIST 分区表。
|
||||
|
||||
```javascript
|
||||
CREATE TABLE t_part_by_list (
|
||||
c1 BIGINT PRIMARY KEY
|
||||
, c2 VARCHAR(50)
|
||||
) PARTITION BY list(c1)
|
||||
(
|
||||
PARTITION p0 VALUES IN (1, 2, 3)
|
||||
, PARTITION p1 VALUES IN (5, 6)
|
||||
, PARTITION p2 VALUES IN (DEFAULT)
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
|
||||
LIST 分区可以新增分区,指定新的不重复的列表。也可以删除分区。
|
||||
|
||||
LIST COLUMNS 分区
|
||||
------------------------
|
||||
|
||||
LIST COLUMNS 分区作用跟 LIST 分区基本相同,不同之处在于:
|
||||
|
||||
* LIST COLUMNS 的拆分列不能是表达式。
|
||||
|
||||
|
||||
|
||||
* LIST COLUMNS 的拆分列可以是多列(即列向量)。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
LIST COLUMNS 分区简单的语法格式如下:
|
||||
|
||||
```javascript
|
||||
CREATE TABLE table_name (
|
||||
column_name1 column_type
|
||||
[, column_nameN column_type]
|
||||
) PARTITION BY LIST COLUMNS ( column_name1 [, column_nameN ] )
|
||||
(
|
||||
PARTITION p0 VALUES IN ( v01 [, v0N] )
|
||||
[, PARTITION pN VALUES IN ( vN1 [, vNN] ) ]
|
||||
[, PARTITION pX VALUES IN (default) ]
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
|
||||
示例:创建一个 LIST COLUMNS 分区
|
||||
|
||||
```javascript
|
||||
CREATE TABLE t2 (
|
||||
id varchar(64),
|
||||
type varchar(16),
|
||||
info varchar(512),
|
||||
gmt_create datetime(6),
|
||||
gmt_modified datetime(6),
|
||||
partition_id varchar(2) GENERATED ALWAYS AS (substr(`id`,19,20)) VIRTUAL,
|
||||
PRIMARY KEY (id)
|
||||
) partition by list columns(partition_id)
|
||||
(partition p0 values in ('00','01'),
|
||||
partition p1 values in ('02','03'),
|
||||
partition p2 values in (default));
|
||||
```
|
||||
|
||||
|
||||
|
||||
哈希(HASH)分区
|
||||
-------------------
|
||||
|
||||
HASH 分区适合于对不能用 RANGE 分区、LIST 分区方法的场景,它的实现方法简单,通过对分区键上的 HASH 函数值来散列记录到不同分区中。如果您的数据符合下列特点,使用 HASH 分区是个很好的选择:
|
||||
|
||||
* 不能指定数据的分区键的列表特征。
|
||||
|
||||
|
||||
|
||||
* 不同范围内的数据大小相差非常大,并且很难手动调整均衡。
|
||||
|
||||
|
||||
|
||||
* 使用 RANGE 分区后数据聚集严重。
|
||||
|
||||
|
||||
|
||||
* 并行 DML、分区剪枝和分区连接等性能非常重要。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
示例:创建一个 HASH 分区表。
|
||||
|
||||
```javascript
|
||||
CREATE TABLE ware(
|
||||
w_id int
|
||||
, w_ytd number(12,2)
|
||||
, w_tax number(4,4)
|
||||
, w_name varchar(10)
|
||||
, w_street_1 varchar(20)
|
||||
, w_street_2 varchar(20)
|
||||
, w_city varchar(20)
|
||||
, w_state char(2)
|
||||
, w_zip char(9)
|
||||
, primary key(w_id)
|
||||
) PARTITION by hash(w_id) partitions 60;
|
||||
```
|
||||
|
||||
|
||||
|
||||
HASH 分区不适合做删除操作。
|
||||
|
||||
组合分区
|
||||
-------------
|
||||
|
||||
组合分区通常是先使用一种分区策略,然后在子分区再使用另外一种分区策略,适合于业务表的数据量非常大时。使用组合分区能发挥多种分区策略的优点。
|
||||
|
||||
在指定二级分区分区策略细节时,可以使用 SUBPARTITION TEMPLATE 子句。
|
||||
|
||||
示例:创建组合分区表。
|
||||
|
||||
```javascript
|
||||
CREATE TABLE t_ordr_part_by_hash_range (o_w_id int
|
||||
, o_d_id int
|
||||
, o_id int
|
||||
, o_c_id int
|
||||
, o_carrier_id int
|
||||
, o_ol_cnt int
|
||||
, o_all_local int
|
||||
, o_entry_d TIMESTAMP NOT NULL
|
||||
, index idx_ordr(o_w_id, o_d_id, o_c_id, o_id) LOCAL
|
||||
, primary key ( o_w_id, o_d_id, o_id, o_entry_d )
|
||||
)
|
||||
PARTITION BY hash(o_w_id)
|
||||
SUBPARTITION BY RANGE(UNIX_TIMESTAMP(o_entry_d))
|
||||
SUBPARTITION template
|
||||
(
|
||||
SUBPARTITION M202001 VALUES LESS THAN(UNIX_TIMESTAMP('2020/02/01'))
|
||||
, SUBPARTITION M202002 VALUES LESS THAN(UNIX_TIMESTAMP('2020/03/01'))
|
||||
, SUBPARTITION M202003 VALUES LESS THAN(UNIX_TIMESTAMP('2020/04/01'))
|
||||
, SUBPARTITION M202004 VALUES LESS THAN(UNIX_TIMESTAMP('2020/05/01'))
|
||||
, SUBPARTITION M202005 VALUES LESS THAN(UNIX_TIMESTAMP('2020/06/01'))
|
||||
, SUBPARTITION M202006 VALUES LESS THAN(UNIX_TIMESTAMP('2020/07/01'))
|
||||
, SUBPARTITION M202007 VALUES LESS THAN(UNIX_TIMESTAMP('2020/08/01'))
|
||||
, SUBPARTITION M202008 VALUES LESS THAN(UNIX_TIMESTAMP('2020/09/01'))
|
||||
, SUBPARTITION M202009 VALUES LESS THAN(UNIX_TIMESTAMP('2020/10/01'))
|
||||
, SUBPARTITION M202010 VALUES LESS THAN(UNIX_TIMESTAMP('2020/11/01'))
|
||||
, SUBPARTITION M202011 VALUES LESS THAN(UNIX_TIMESTAMP('2020/12/01'))
|
||||
, SUBPARTITION M202012 VALUES LESS THAN(UNIX_TIMESTAMP('2021/01/01'))
|
||||
, SUBPARTITION MMAX VALUES LESS THAN MAXVALUE
|
||||
)
|
||||
partitions 16;
|
||||
|
||||
CREATE TABLE t_log_part_by_range_hash (
|
||||
log_id int NOT NULL
|
||||
, log_value varchar(50)
|
||||
, log_date TIMESTAMP NOT NULL
|
||||
, PRIMARY key(log_id, log_date)
|
||||
) PARTITION BY RANGE(UNIX_TIMESTAMP(log_date))
|
||||
SUBPARTITION BY HASH(log_id) SUBPARTITIONS 16
|
||||
(
|
||||
PARTITION M202001 VALUES LESS THAN(UNIX_TIMESTAMP('2020/02/01'))
|
||||
, PARTITION M202002 VALUES LESS THAN(UNIX_TIMESTAMP('2020/03/01'))
|
||||
, PARTITION M202003 VALUES LESS THAN(UNIX_TIMESTAMP('2020/04/01'))
|
||||
, PARTITION M202004 VALUES LESS THAN(UNIX_TIMESTAMP('2020/05/01'))
|
||||
, PARTITION M202005 VALUES LESS THAN(UNIX_TIMESTAMP('2020/06/01'))
|
||||
, PARTITION M202006 VALUES LESS THAN(UNIX_TIMESTAMP('2020/07/01'))
|
||||
, PARTITION M202007 VALUES LESS THAN(UNIX_TIMESTAMP('2020/08/01'))
|
||||
, PARTITION M202008 VALUES LESS THAN(UNIX_TIMESTAMP('2020/09/01'))
|
||||
, PARTITION M202009 VALUES LESS THAN(UNIX_TIMESTAMP('2020/10/01'))
|
||||
, PARTITION M202010 VALUES LESS THAN(UNIX_TIMESTAMP('2020/11/01'))
|
||||
, PARTITION M202011 VALUES LESS THAN(UNIX_TIMESTAMP('2020/12/01'))
|
||||
, PARTITION M202012 VALUES LESS THAN(UNIX_TIMESTAMP('2021/01/01'))
|
||||
, PARTITION MMAX VALUES LESS THAN MAXVALUE
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
分区表的索引
|
||||
===========================
|
||||
|
||||
|
||||
|
||||
分区表的查询性能跟 SQL 中条件有关。当 SQL 中带上拆分键时,OceanBase 会根据条件做分区剪枝,只用搜索特定的分区即可;如果没有拆分键,则要扫描所有分区。
|
||||
|
||||
分区表也可以通过创建索引来提升性能。跟分区表一样,分区表的索引也可以分区或者不分区。
|
||||
|
||||
* 如果分区表的索引不分区,就是一个全局索引(GLOBAL),是一个独立的分区,索引数据覆盖整个分区表。
|
||||
|
||||
* 如果分区表的索引分区了,根据分区策略又可以分为两类。一是跟分区表保持一致的分区策略,则每个索引分区的索引数据覆盖相应的分区表的分区,这个索引又叫本地索引(LOCAL)。
|
||||
|
||||
|
||||
|
||||
|
||||
建议尽可能的使用本地索引,只有在有必要的时候才使用全局索引。其原因是全局索引会降低 DML 的性能,DML 可能会因此产生分布式事务。
|
||||
|
||||
通常创建索引时默认都是全局索引,本地索引需要在后面增加关键字 LOCAL 。
|
||||
|
||||
示例:创建分区表的本地索引。
|
||||
|
||||
```javascript
|
||||
CREATE INDEX idx_log_date ON t_log_part_by_range_hash(log_date) LOCAL;
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
分区表使用建议
|
||||
============================
|
||||
|
||||
|
||||
|
||||
通常当表的数据量非常大,以致于可能使数据库空间紧张,或者由于表非常大导致相关 SQL 查询性能变慢时,可以考虑使用分区表。
|
||||
|
||||
使用分区表时要选择合适的拆分键以及拆分策略。如果是日志类型的大表,根据时间类型的列做 RANGE 分区是最合适的。如果是并发访问非常高的表,结合业务特点选择能满足绝大部分核心业务查询的列作为拆分键是最合适的。无论选哪个列做为分区键,都不大可能满足所有的查询性能。
|
||||
|
||||
分区表中的全局唯一性需求可以通过主键约束和唯一约束实现。OceanBase 数据库的分区表的主键约束和唯一键约束必须包含拆分键。唯一约束也是一个全局索引。全局唯一的需求也可以通过本地唯一索引实现,只需要在唯一索引里包含拆分键。
|
||||
|
||||
**示例:创建有唯一性需求的分区表**
|
||||
|
||||
```javascript
|
||||
obclient> CREATE TABLE account(
|
||||
id bigint NOT NULL PRIMARY KEY
|
||||
, name varchar(50) NOT NULL UNIQUE
|
||||
, value number NOT NULL
|
||||
, gmt_create timestamp DEFAULT current_timestamp NOT NULL
|
||||
, gmt_modified timestamp DEFAULT current_timestamp NOT NULL
|
||||
) PARTITION BY HASH(id) PARTITIONS 16;
|
||||
Query OK, 0 rows affected (0.16 sec)
|
||||
|
||||
obclient> CREATE TABLE account2(
|
||||
id bigint NOT NULL PRIMARY KEY
|
||||
, name varchar(50) NOT NULL
|
||||
, value number NOT NULL
|
||||
, gmt_create timestamp DEFAULT current_timestamp NOT NULL
|
||||
, gmt_modified timestamp DEFAULT current_timestamp NOT NULL
|
||||
) PARTITION BY HASH(id) PARTITIONS 16;
|
||||
Query OK, 0 rows affected (0.11 sec)
|
||||
|
||||
obclient> CREATE UNIQUE INDEX account2_uk ON account2(name, id) LOCAL ;
|
||||
Query OK, 0 rows affected (0.73 sec)
|
||||
|
||||
obclient> show indexes from account;
|
||||
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+-----------+---------------+---------+
|
||||
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible |
|
||||
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+-----------+---------------+---------+
|
||||
| account | 0 | PRIMARY | 1 | id | A | NULL | NULL | NULL | | BTREE | available | | YES |
|
||||
| account | 0 | name | 1 | name | A | NULL | NULL | NULL | | BTREE | available | | YES |
|
||||
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+-----------+---------------+---------+
|
||||
2 rows in set (0.01 sec)
|
||||
|
||||
obclient> show indexes from account2;
|
||||
+----------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+-----------+---------------+---------+
|
||||
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible |
|
||||
+----------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+-----------+---------------+---------+
|
||||
| account2 | 0 | PRIMARY | 1 | id | A | NULL | NULL | NULL | | BTREE | available | | YES |
|
||||
| account2 | 0 | account2_uk | 1 | name | A | NULL | NULL | NULL | | BTREE | available | | YES |
|
||||
| account2 | 0 | account2_uk | 2 | id | A | NULL | NULL | NULL | | BTREE | available | | YES |
|
||||
+----------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+-----------+---------------+---------+
|
||||
3 rows in set (0.00 sec)
|
||||
|
||||
obclient> SELECT * FROM information_schema.`TABLE_CONSTRAINTS` WHERE table_schema='TPCCDB' AND table_name LIKE 'ACCOUNT%';
|
||||
+--------------------+-------------------+-----------------+--------------+------------+-----------------+
|
||||
| CONSTRAINT_CATALOG | CONSTRAINT_SCHEMA | CONSTRAINT_NAME | TABLE_SCHEMA | TABLE_NAME | CONSTRAINT_TYPE |
|
||||
+--------------------+-------------------+-----------------+--------------+------------+-----------------+
|
||||
| def | tpccdb | PRIMARY | tpccdb | account | PRIMARY KEY |
|
||||
| def | tpccdb | name | tpccdb | account | UNIQUE |
|
||||
| def | tpccdb | PRIMARY | tpccdb | account2 | PRIMARY KEY |
|
||||
| def | tpccdb | account2_uk | tpccdb | account2 | UNIQUE |
|
||||
+--------------------+-------------------+-----------------+--------------+------------+-----------------+
|
||||
4 rows in set (0.02 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
关于表组
|
||||
=========================
|
||||
|
||||
|
||||
|
||||
表组是表的属性,会影响多个表的分区在 OceanBase 机器上的分布特征。
|
||||
|
||||
不同表的分区有可能分布在不同的节点上,当两个表做表连接查询时,OceanBase 会跨节点请求数据,执行时间就跟节点间请求延时有关。在 SQL 调优时,OceanBase 建议对业务上关系密切的表,设置相同的表组。OceanBase 对于同一个表组中的表的同号分区会管理为一个分区组。同一个分区组中的分区,OceanBase 会尽可能的分配到同一个节点内部,这样就可以规避跨节点的请求。
|
||||
|
||||
创建表组时,首先要规划好表组的用途。如果是用于普通表的属性,表组就不用分区;如果是用于分区表的属性,表组就要指定分区策略,并且要跟分区表的分区策略保持一致。
|
||||
|
||||
示例:创建表组
|
||||
|
||||
```javascript
|
||||
obclient> create tablegroup tpcc_group partition by hash partitions 6 ;
|
||||
Query OK, 0 rows affected (0.03 sec)
|
||||
|
||||
obclient>
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
创建表时指定表组
|
||||
=============================
|
||||
|
||||
|
||||
|
||||
表组可以在创建表的时候指定,SQL 查询语法格式如下:
|
||||
|
||||
```javascript
|
||||
CREATE TABLE table_name (
|
||||
column_name data_type [, column_name data_type]
|
||||
) TABLEGROUP tablegroup_name ;
|
||||
```
|
||||
|
||||
|
||||
|
||||
**示例:创建表时指定表组。**
|
||||
|
||||
如下创建订单表和订单明细表,业务上这两个表经常要关联查询,所以建议放到同一个表组中。
|
||||
|
||||
```javascript
|
||||
create table ordr (
|
||||
o_w_id int
|
||||
, o_d_id int
|
||||
, o_id int
|
||||
, o_c_id int
|
||||
, o_carrier_id int
|
||||
, o_ol_cnt int
|
||||
, o_all_local int
|
||||
, o_entry_d date
|
||||
, index iordr(o_w_id, o_d_id, o_c_id, o_id) local
|
||||
, primary key ( o_w_id, o_d_id, o_id )
|
||||
)tablegroup tpcc_group partition by hash(o_w_id) partitions 6;
|
||||
create table ordl (
|
||||
ol_w_id int
|
||||
, ol_d_id int
|
||||
, ol_o_id int
|
||||
, ol_number int
|
||||
, ol_delivery_d date
|
||||
, ol_amount decimal(6, 2)
|
||||
, ol_i_id int
|
||||
, ol_supply_w_id int
|
||||
, ol_quantity int
|
||||
, ol_dist_info char(24)
|
||||
, primary key (ol_w_id, ol_d_id, ol_o_id, ol_number )
|
||||
)tablegroup tpcc_group partition by hash(ol_w_id) partitions 6;
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
查看表组信息
|
||||
===========================
|
||||
|
||||
|
||||
|
||||
使用命令 SHOW TABLEGROUPS 和 SHOW CREATE TABLE 来查看表组信息。
|
||||
|
||||
示例:查看表组信息和定义
|
||||
|
||||
```javascript
|
||||
obclient> show tablegroups;
|
||||
+-----------------+------------+---------------+
|
||||
| Tablegroup_name | Table_name | Database_name |
|
||||
+-----------------+------------+---------------+
|
||||
| oceanbase | NULL | NULL |
|
||||
| tpcc_group | cust | tpccdb |
|
||||
| tpcc_group | dist | tpccdb |
|
||||
| tpcc_group | hist | tpccdb |
|
||||
| tpcc_group | nord | tpccdb |
|
||||
| tpcc_group | ordl | tpccdb |
|
||||
| tpcc_group | ordr | tpccdb |
|
||||
| tpcc_group | stok | tpccdb |
|
||||
| tpcc_group | ware | tpccdb |
|
||||
+-----------------+------------+---------------+
|
||||
9 rows in set (0.01 sec)
|
||||
|
||||
obclient> show create tablegroup tpcc_group\G
|
||||
*************************** 1. row ***************************
|
||||
Tablegroup: tpcc_group
|
||||
Create Tablegroup: CREATE TABLEGROUP IF NOT EXISTS `tpcc_group` BINDING = FALSE
|
||||
partition by hash partitions 6
|
||||
|
||||
1 row in set (0.00 sec)
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
向表组中增加表
|
||||
============================
|
||||
|
||||
|
||||
|
||||
如果表创建的时候没有指定表组,也可以事后加入到表组中。有两种方法:一是修改表的表组属性,二是使用 ALTER TABLEGROUP ADD 语法。
|
||||
|
||||
* 示例:修改表的表组属性
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
**说明**
|
||||
|
||||
修改表的表组属性时名字不用单引号,否则会区分大小写。
|
||||
|
||||
```javascript
|
||||
obclient> alter table ordr tablegroup=tpcc_group;
|
||||
Query OK, 0 rows affected (0.04 sec)
|
||||
|
||||
obclient> alter table ordl tablegroup=tpcc_group;
|
||||
Query OK, 0 rows affected (0.04 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
* 示例:向表组中加入表
|
||||
|
||||
|
||||
|
||||
|
||||
向表组中加入表的前提是表的分区策略跟表组的分区策略保持一致。
|
||||
|
||||
```javascript
|
||||
obclient> alter table ordr tablegroup='';
|
||||
Query OK, 0 rows affected (0.04 sec)
|
||||
|
||||
obclient> alter table ordl tablegroup='';
|
||||
Query OK, 0 rows affected (0.03 sec)
|
||||
|
||||
obclient> alter tablegroup tpcc_group add ordr, ordl ;
|
||||
Query OK, 0 rows affected (0.03 sec)
|
||||
|
||||
obclient>
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
删除表组
|
||||
=========================
|
||||
|
||||
|
||||
|
||||
如果要删除表组,得先确认表组中没有表。具体方法就是从表组中删除表,或者将相关表的表组属性清空。
|
||||
|
||||
示例如下:
|
||||
|
||||
```javascript
|
||||
obclient> show tablegroups;
|
||||
+-----------------+------------+---------------+
|
||||
| Tablegroup_name | Table_name | Database_name |
|
||||
+-----------------+------------+---------------+
|
||||
| oceanbase | NULL | NULL |
|
||||
| tpcc_group | cust | tpccdb |
|
||||
| tpcc_group | dist | tpccdb |
|
||||
| tpcc_group | hist | tpccdb |
|
||||
| tpcc_group | nord | tpccdb |
|
||||
| tpcc_group | ordl | tpccdb |
|
||||
| tpcc_group | ordr | tpccdb |
|
||||
| tpcc_group | stok | tpccdb |
|
||||
| tpcc_group | ware | tpccdb |
|
||||
+-----------------+------------+---------------+
|
||||
9 rows in set (0.01 sec)
|
||||
|
||||
obclient> drop tablegroup tpcc_group;
|
||||
ERROR 4615 (HY000): tablegroup is not empty
|
||||
obclient>
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
创建视图
|
||||
=========================
|
||||
|
||||
|
||||
|
||||
可以使用 CREATE VIEW 语句来创建视图。
|
||||
|
||||
示例:创建视图 stock_item,该视图内容取自 stock 和 item 两张表,两张表做表连接就得到这个视图。
|
||||
|
||||
```javascript
|
||||
obclient> CREATE VIEW stock_item
|
||||
AS
|
||||
SELECT /*+ leading(s) use_merge(i) */
|
||||
i_price, i_name, i_data, s_i_id, s_w_id, s_order_cnt, s_ytd, s_remote_cnt, s_quantity, s_data, s_dist_01, s_dist_02, s_dist_03, s_dist_04, s_dist_05, s_dist_06, s_dist_07, s_dist_08, s_dist_09, s_dist_10
|
||||
FROM stok s, item i
|
||||
WHERE s.s_i_id = i.i_id;
|
||||
Query OK, 0 rows affected (0.01 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
查看更多创建视图的选项,请参见《SQL参考(MySQL模式)》。
|
||||
@ -0,0 +1,21 @@
|
||||
修改视图
|
||||
=========================
|
||||
|
||||
|
||||
|
||||
可以使用 CREATE OR REPLACE VIEW 语句来对视图进行修改。
|
||||
|
||||
示例:修改视图 stock_item。
|
||||
|
||||
```javascript
|
||||
obclient> CREATE OR REPLACE VIEW stock_item
|
||||
AS
|
||||
SELECT /*+ leading(s) use_merge(i) */
|
||||
i_price, i_name, i_data, s_i_id, s_w_id, s_order_cnt, s_ytd, s_remote_cnt, s_quantity, s_data, s_dist_01, s_dist_02, s_dist_03, s_dist_04, s_dist_05, s_dist_06, s_dist_07, s_dist_08, s_dist_09, s_dist_10
|
||||
FROM stok s, item i
|
||||
WHERE s.s_i_id = i.i_id;
|
||||
Query OK, 0 rows affected (0.02 sec)
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
删除视图
|
||||
=========================
|
||||
|
||||
|
||||
|
||||
删除视图使用 DROP VIEW 语句。
|
||||
|
||||
删除视图并不会删除视图引用的表。如果视图被其他视图所引用,视图删除后将会导致依赖当前视图的其他视图查询失败。
|
||||
@ -0,0 +1,7 @@
|
||||
关于数据迁移和同步
|
||||
==============================
|
||||
|
||||
|
||||
|
||||
数据从传统数据库迁移到 OceanBase 数据库上,可以选择将数据导出为 CSV 文件、SQL 文件,然后再导入到 OceanBase 中。
|
||||
|
||||
@ -0,0 +1,103 @@
|
||||
通用数据同步框架 DataX
|
||||
===================================
|
||||
|
||||
|
||||
|
||||
DataX 简介
|
||||
-----------------
|
||||
|
||||
DataX 是阿里巴巴集团内被广泛使用的离线数据同步工具/平台,实现包括 MySQL、Oracle、SqlServer、Postgre、HDFS、Hive、ADS、HBase、TableStore(OTS)、MaxCompute(ODPS)、DRDS 和 OceanBase 等各种异构数据源之间高效的数据同步功能。
|
||||
|
||||
DataX 本身作为数据同步框架,将不同数据源的同步抽象为从源头数据源读取数据的 Reader 插件,以及向目标端写入数据的 Writer 插件,理论上 DataX 框架可以支持任意数据源类型的数据同步工作。同时 DataX 插件体系作为一套生态系统,每接入一套新数据源该新加入的数据源即可实现和现有的数据源互通。
|
||||
|
||||
DataX 已经在 github 开源,开源地址是:[github.com/Alibaba/datax](http://github.com/Alibaba/datax),开源的产品不支持 OceanBase 和 DB2 ,OceanBase 产品团队提供针对 OceanBase 和 DB2 的读写插件。
|
||||
|
||||
DataX 的使用示例
|
||||
--------------------
|
||||
|
||||
DataX 安装后,默认目录在 /home/admin/datax3 。目录下有个文件夹 job ,默认存放数据迁移任务的配置文件,当然也可以自定义目录。
|
||||
|
||||
每个任务的参数文件是一个 json 格式,主要由一个 reader 和一个 writer 组成。job 文件夹下有个默认的示例任务配置文件 job.json:
|
||||
|
||||
```javascript
|
||||
[admin /home/admin/datax3/job]
|
||||
$cat job.json
|
||||
{
|
||||
"job": {
|
||||
"setting": {
|
||||
"speed": {
|
||||
"byte":10485760
|
||||
},
|
||||
"errorLimit": {
|
||||
"record": 0,
|
||||
"percentage": 0.02
|
||||
}
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"reader": {
|
||||
"name": "streamreader",
|
||||
"parameter": {
|
||||
"column" : [
|
||||
{
|
||||
"value": "DataX",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"value": 19890604,
|
||||
"type": "long"
|
||||
},
|
||||
{
|
||||
"value": "1989-06-04 00:00:00",
|
||||
"type": "date"
|
||||
},
|
||||
{
|
||||
"value": true,
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"value": "test",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"sliceRecordCount": 100000
|
||||
}
|
||||
},
|
||||
"writer": {
|
||||
"name": "streamwriter",
|
||||
"parameter": {
|
||||
"print": false,
|
||||
"encoding": "UTF-8"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
这个任务的配置文件的 reader 和 writer 类型是一个 stream。这个任务会检测 DataX 是否正确安装,运行之前确保安装 JDK 运行环境。
|
||||
|
||||
```javascript
|
||||
[admin@h07g12092.sqa.eu95 /home/admin/datax3/job]
|
||||
$cd ../
|
||||
|
||||
[admin@h07g12092.sqa.eu95 /home/admin/datax3]
|
||||
$bin/datax.py job/job.json
|
||||
```
|
||||
|
||||
|
||||
|
||||
运行结果如下:
|
||||
|
||||

|
||||
|
||||
|
||||
**说明**
|
||||
|
||||
|
||||
|
||||
这个默认的任务参数关闭了数据流输出,所以看不到输出结果。
|
||||
|
||||
@ -0,0 +1,422 @@
|
||||
不同数据源的 DataX 读写插件示例
|
||||
========================================
|
||||
|
||||
|
||||
|
||||
DataX 官网支持绝大部分主流数据源的读写插件,并且有详细的使用文档。
|
||||
|
||||
CSV 文件的读写插件
|
||||
--------------------
|
||||
|
||||
csv 文件就是文本文件,用 txtreader 和 txtwriter 读写。配置文件详细语法请参见 [DataX官网说明](https://github.com/alibaba/DataX)。
|
||||
|
||||
**txtreader配置示例:**
|
||||
|
||||
```javascript
|
||||
"reader":{
|
||||
"name":"txtfilereader",
|
||||
"parameter":{
|
||||
"path":["文件全路径"],
|
||||
"encoding":"UTF-8",
|
||||
"column":[
|
||||
{ "index":0, "type":"long" }
|
||||
,{ "index":1, "type":"long" }
|
||||
,{ "index":2, "type":"string" }
|
||||
,{ "index":3, "type":"double" }
|
||||
,{ "index":4, "type":"string" }
|
||||
],
|
||||
"fieldDelimiter":"||",
|
||||
"fileFormat":"text"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
**txtwriter** **配置示例:**
|
||||
|
||||
```javascript
|
||||
"writer":{
|
||||
"name":"txtfilewriter",
|
||||
"parameter":{
|
||||
"path":"文件全路径",
|
||||
"fileName":"文件名",
|
||||
"writeMode":"truncate",
|
||||
"dateFormat":"yyyy-MM-dd",
|
||||
"charset":"UTF-8",
|
||||
"nullFormat":"",
|
||||
"fileDelimiter":"||"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
MySQL 数据库的读写插件
|
||||
-----------------------
|
||||
|
||||
针对 MySQL 数据库,用 mysqlreader 和 mysqlwriter 插件读写。
|
||||
|
||||
**mysqlreader** **配置示例:**
|
||||
|
||||
```javascript
|
||||
"reader": {
|
||||
"name": "mysqlreader",
|
||||
"parameter": {
|
||||
"username": "root",
|
||||
"password": "root",
|
||||
"column": [
|
||||
"id",
|
||||
"name"
|
||||
],
|
||||
"splitPk": "db_id",
|
||||
"connection": [
|
||||
{
|
||||
"table": [
|
||||
"table"
|
||||
],
|
||||
"jdbcUrl": [
|
||||
"jdbc:mysql://127.0.0.1:3306/database"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
**mysqlwriter** **配置示例:**
|
||||
|
||||
```javascript
|
||||
"writer": {
|
||||
"name": "mysqlwriter",
|
||||
"parameter": {
|
||||
"writeMode": "insert",
|
||||
"username": "root",
|
||||
"password": "root",
|
||||
"column": [
|
||||
"id",
|
||||
"name"
|
||||
],
|
||||
"session": [
|
||||
"set session sql_mode='ANSI'"
|
||||
],
|
||||
"preSql": [
|
||||
"delete from test"
|
||||
],
|
||||
"connection": [
|
||||
{
|
||||
"jdbcUrl": "jdbc:mysql://127.0.0.1:3306/datax?useUnicode=true&characterEncoding=gbk",
|
||||
"table": [
|
||||
"test"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
Oracle 数据库的读写插件
|
||||
------------------------
|
||||
|
||||
针对 Oracle 数据库,用 oraclereader 和 oraclewriter 插件来读写。
|
||||
|
||||
**oraclereader** **配置示例:**
|
||||
|
||||
```javascript
|
||||
"reader": {
|
||||
"name": "oraclereader",
|
||||
"parameter": {
|
||||
// 数据库连接用户名
|
||||
"username": "root",
|
||||
// 数据库连接密码
|
||||
"password": "root",
|
||||
"column": [
|
||||
"id","name"
|
||||
],
|
||||
//切分主键
|
||||
"splitPk": "db_id",
|
||||
"connection": [
|
||||
{
|
||||
"table": [
|
||||
"table"
|
||||
],
|
||||
"jdbcUrl": [
|
||||
"jdbc:oracle:thin:@[HOST_NAME]:PORT:[DATABASE_NAME]"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
**oraclewriter** **配置示例:**
|
||||
|
||||
```javascript
|
||||
"writer": {
|
||||
"name": "oraclewriter",
|
||||
"parameter": {
|
||||
"username": "root",
|
||||
"password": "root",
|
||||
"column": [
|
||||
"id",
|
||||
"name"
|
||||
],
|
||||
"preSql": [
|
||||
"delete from test"
|
||||
],
|
||||
"connection": [
|
||||
{
|
||||
"jdbcUrl": "jdbc:oracle:thin:@[HOST_NAME]:PORT:[DATABASE_NAME]",
|
||||
"table": [
|
||||
"test"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
DB2 数据库的读写插件
|
||||
---------------------
|
||||
|
||||
**db2reader** **配置示例:**
|
||||
|
||||
```javascript
|
||||
"reader":{
|
||||
"name":"db2reader",
|
||||
"parameter":{
|
||||
"username":"SRC_DB_UESRNAME",
|
||||
"password":"SRC_DB_PASSWORD",
|
||||
"column":[
|
||||
"SRC_COLUMN_LIST"
|
||||
],
|
||||
"connection":[
|
||||
{
|
||||
"table":[
|
||||
"SRC_TABLE_NAME"
|
||||
],
|
||||
"jdbcUrl":[
|
||||
"jdbc:db2://SRC_DB_IP:SRC_DB_PORT/SRC_DB_NAME"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
**db2writer** **配置示例:**
|
||||
|
||||
OceanBase 数据库的读写插件
|
||||
---------------------------
|
||||
|
||||
OceanBase 数据库使用插件 oceanbasev10reader 和 oceanbasev10writer 来读写。该插件由 OceanBase 产品团队单独提供。
|
||||
|
||||
* **oceanbasev10reader** **配置示例**
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
"reader":{
|
||||
"name":"oceanbasev10reader",
|
||||
"parameter":{
|
||||
"where":"",
|
||||
"timeout":10000,
|
||||
"readBatchSize":100000,
|
||||
"readByPartition":"true",
|
||||
"column": [
|
||||
"列名1","列名2"
|
||||
],
|
||||
"connection":[
|
||||
{
|
||||
"jdbcUrl":["||_dsc_ob10_dsc_||集群名:租户名||_dsc_ob10_dsc_||jdbc:oceanbase://连接IP:连接端口/模式名或数据库名"],
|
||||
"table":["表名"]
|
||||
}
|
||||
],
|
||||
"username":"租户内用户名",
|
||||
"password":"密码"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
示例:OceanBase 表 ware 导出到 csv 文件
|
||||
|
||||
```javascript
|
||||
[admin@*** /home/admin/datax3]
|
||||
$cat job/ob_tpcc_ware_2_csv.json
|
||||
{
|
||||
"job":{
|
||||
"setting":{
|
||||
"speed":{
|
||||
"channel":10
|
||||
},
|
||||
"errorLimit":{
|
||||
"record":0, "percentage": 0.02
|
||||
}
|
||||
},
|
||||
"content":[
|
||||
{
|
||||
"reader":{
|
||||
"name":"oceanbasev10reader",
|
||||
"parameter":{
|
||||
"where":"",
|
||||
"timeout":10000,
|
||||
"readBatchSize":100000,
|
||||
"readByPartition":"true",
|
||||
"column": [
|
||||
"W_ID","W_YTD","W_TAX","W_NAME","W_STREET_1","W_STREET_2","W_CITY","W_STATE","W_ZIP"
|
||||
],
|
||||
"connection":[
|
||||
{
|
||||
"jdbcUrl":["||_dsc_ob10_dsc_||obdemo:obbmsql||_dsc_ob10_dsc_||jdbc:oceanbase://127.1:2883/tpcc"],
|
||||
"table":["ware"]
|
||||
}
|
||||
],
|
||||
"username":"tpcc",
|
||||
"password":"123456"
|
||||
}
|
||||
},
|
||||
"writer":{
|
||||
"name":"txtfilewriter",
|
||||
"parameter":{
|
||||
"path":"/home/admin/csvdata/",
|
||||
"fileName":"ware",
|
||||
"writeMode":"truncate",
|
||||
"dateFormat":"yyyy-MM-dd",
|
||||
"charset":"UTF-8",
|
||||
"nullFormat":"",
|
||||
"fileDelimiter":"||"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
[admin@*** /home/admin/datax3]
|
||||
$bin/datax.py job/ob_tpcc_ware_2_csv.json
|
||||
```
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
* **oceanbasev10writer** **配置示例**
|
||||
|
||||
|
||||
|
||||
|
||||
使用 DataX 向 OceanBase 里写入时,要避免写入速度过快导致 OceanBase 的增量内存耗尽。通常建议 DataX 配置文件里针对写入做一个写入限速设置。关键字是 memstoreThreshold :
|
||||
|
||||
```javascript
|
||||
"writer": {
|
||||
"name": "oceanbasev10writer",
|
||||
"parameter": {
|
||||
"username": "租户内的用户名",
|
||||
"password": "密码",
|
||||
"writeMode": "insert",
|
||||
"column": [
|
||||
"列名1","列名2"
|
||||
],
|
||||
"preSql": [
|
||||
""
|
||||
],
|
||||
"connection": [
|
||||
{
|
||||
"jdbcUrl": "||_dsc_ob10_dsc_||集群名:租户名||_dsc_ob10_dsc_||jdbc:oceanbase://连接IP:连接端口(默认2883)/模式名或数据库名",
|
||||
"table": [
|
||||
"表名"
|
||||
]
|
||||
}
|
||||
],
|
||||
"batchSize": 1024,
|
||||
"memstoreThreshold": "90"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
示例:从 csv 文件导入到 OceanBase 表中
|
||||
|
||||
```javascript
|
||||
[admin@*** /home/admin/datax3]
|
||||
$cat job/csv_2_ob_tpcc_ware2.json
|
||||
{
|
||||
"job":{
|
||||
"setting":{
|
||||
"speed":{
|
||||
"channel":32
|
||||
},
|
||||
"errorLimit":{
|
||||
"record":0, "percentage": 0.02
|
||||
}
|
||||
},
|
||||
"content":[
|
||||
{
|
||||
"reader":{
|
||||
"name":"txtfilereader",
|
||||
"parameter":{
|
||||
"path":["/home/admin/csvdata/ware*"],
|
||||
"encoding":"UTF-8",
|
||||
"column":[
|
||||
{ "index":0, "type":"long" }
|
||||
,{ "index":1, "type":"long" }
|
||||
,{ "index":2, "type":"long" }
|
||||
,{ "index":3, "type":"string" }
|
||||
,{ "index":4, "type":"string" }
|
||||
,{ "index":5, "type":"string" }
|
||||
,{ "index":6, "type":"string" }
|
||||
,{ "index":7, "type":"string" }
|
||||
,{ "index":8, "type":"string" }
|
||||
],
|
||||
"fieldDelimiter":",",
|
||||
"fileFormat":"text"
|
||||
}
|
||||
},
|
||||
"writer":{
|
||||
"name":"oceanbasev10writer",
|
||||
"parameter":{
|
||||
"writeMode":"insert",
|
||||
"column":[
|
||||
"W_ID","W_YTD","W_TAX","W_NAME","W_STREET_1","W_STREET_2","W_CITY","W_STATE","W_ZIP"
|
||||
],
|
||||
"connection":[
|
||||
{
|
||||
"jdbcUrl":"||_dsc_ob10_dsc_||obdemo:obbmsql||_dsc_ob10_dsc_||jdbc:oceanbase://127.1:2883/tpcc",
|
||||
"table":["WARE2"]
|
||||
}
|
||||
],
|
||||
"username":"tpcc",
|
||||
"password":"123456",
|
||||
"batchSize":256,
|
||||
" memstoreThreshold":"90"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[admin@*** /home/admin/datax3]
|
||||
$bin/datax.py job/csv_2_ob_tpcc_ware2.json
|
||||
```
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
Binary file not shown.
@ -0,0 +1,170 @@
|
||||
使用 Canal 向 OceanBase 数据库增量迁移数据
|
||||
===================================================
|
||||
|
||||
|
||||
|
||||
您可以使用 Canal 向 OceanBase 数据库增量迁移数据。Canal 是一个开源的 MySQL 数据库 Binlog 的增量订阅和消费组件,基于 MySQL 数据库的增量日志解析,可以用于数据同步。关于 Canal 的详细信息,参考 Canal 的 [GitHub 仓库](https://github.com/alibaba/canal)。
|
||||
|
||||
Canal 的工作流程如下:
|
||||
|
||||

|
||||
|
||||
其中 Canal Client Adapter 支持 Logger、Rdb、HBase、Kudu 和 Elasticsearch。本文档使用 Rdb。其他可选的配置如下:
|
||||
|
||||
* Canal Server 支持单机模式和 HA 模式。
|
||||
|
||||
|
||||
|
||||
* Canal Connector 支持 TCP 和 MQ 两种类型。MQ 目前支持 Kafka、RabbitMQ 和 RocketMQ。
|
||||
|
||||
|
||||
|
||||
* Rdb 版的 Client Adapter 支持按表同步和整库同步。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
操作步骤
|
||||
-------------------------
|
||||
|
||||
按照以下步骤向 OceanBase 数据库增量迁移数据:
|
||||
|
||||
1. 部署 Canal。
|
||||
|
||||
部署 Canal Server 参考 [Canal 快速上手](https://github.com/alibaba/canal/wiki/QuickStart) 文档。
|
||||
|
||||
|
||||
2. 同步 Canal Client Adapter Rdb。
|
||||
|
||||
同步 Canal Client Adapter Rdb 参考 [同步 Rdb](https://github.com/alibaba/canal/wiki/Sync-RDB) 文档。
|
||||
**说明**
|
||||
|
||||
|
||||
|
||||
OceanBase 数据库兼容 MySQL 协议,您可以直接按照 MySQL 配置 Adapter。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
示例
|
||||
-----------------------
|
||||
|
||||
以 TCP 类型的 Connetors 为例,按照以下步骤向 OceanBase 数据库增量迁移数据:
|
||||
|
||||
1. 将 `plugin` 目录下的 RDB 和 TCP Connector 对应的 JAR 包添加到 `lib` 目录下。
|
||||
|
||||
|
||||
|
||||
2. 修改启动配置文件` conf/application.yml`。
|
||||
|
||||
```yaml
|
||||
# spring 服务相关配置
|
||||
server:
|
||||
port: 8081
|
||||
spring:
|
||||
jackson:
|
||||
date-format: 'yyyy-MM-dd HH:mm:ss'
|
||||
time-zone: GMT+8
|
||||
default-property-inclusion: non_null
|
||||
|
||||
# canal 相关配置
|
||||
canal.conf:
|
||||
# 指定 Canal Connector 类型
|
||||
mode: tcp
|
||||
flatMessage: true
|
||||
# 集群模式下的 zookeeper 地址,如果配置了单机地址则以单机为准
|
||||
zookeeperHosts:
|
||||
syncBatchSize: 1000
|
||||
retries: 0
|
||||
timeout:
|
||||
accessKey:
|
||||
secretKey:
|
||||
# Canal Connector 数据消费配置
|
||||
consumerProperties:
|
||||
canal.tcp.server.host: 127.0.0.1:11111
|
||||
canal.tcp.zookeeper.hosts:
|
||||
canal.tcp.batch.size: 500
|
||||
canal.tcp.username:
|
||||
canal.tcp.password:
|
||||
# 适配器列表
|
||||
canalAdapters:
|
||||
- instance: example # canal instance 名
|
||||
groups:
|
||||
- groupId: g1 # 适配器分组 ID
|
||||
outerAdapters: # 分组内适配器列表
|
||||
- name: rdb # 指定适配器为 RDB 类型
|
||||
key: mysql1 # 适配器标识 key,与表映射配置中 outerAdapterKey 对应
|
||||
properties:
|
||||
jdbc.driverClassName: com.mysql.jdbc.Driver
|
||||
jdbc.url: jdbc:mysql://127.0.0.1:2883/ob_db?useUnicode=true
|
||||
jdbc.username: username@tenant_name#deploy_name
|
||||
jdbc.password: password
|
||||
```
|
||||
|
||||
|
||||
|
||||
其中,`outerAdapters` 中的 `rdb` 数据源表示目标数据库。OceanBase 数据库用户名的格式详情,参考 [通过 MySQL 客户端连接 OceanBase 租户](../../7.developer-guide-1/2.connect-to-the-oceanbase-database/1.connect-to-an-oceanbase-tenant-by-using-a-mysql-client.md)。
|
||||
|
||||
|
||||
3. 配置表映射。
|
||||
|
||||
Adapter 服务在启动时,会加载对应类型 Adapter 的配置目录(本例的配置目录为 `conf/rdb/`)下的所有配置文件。
|
||||
|
||||
表映射有两种配置形式:按表映射同步和整库同步。
|
||||
|
||||
**按表映射同步:**
|
||||
|
||||
```yaml
|
||||
dataSourceKey: defaultDS # 源数据源的 Key, 对应上面配置的 srcDataSources 中的值
|
||||
destination: example # Cannal Server 的 Instance 名
|
||||
outerAdapterKey: mysql1 # Adapter Key, 对应上面配置 outAdapters 中的 Key
|
||||
concurrent: true # 是否按主键 Hash 并行同步, 并行同步的表必须保证主键不会更改及主键不能为其他同步表的外键
|
||||
dbMapping:
|
||||
database: mysql_db # 源数据源的 Database/Schema
|
||||
table: mysql_tbl # 源数据源表名
|
||||
targetTable: ob_db.ob_tbl # 目标数据源的库名.表名
|
||||
targetPk: # 主键映射
|
||||
id: id # 如果是复合主键可以换行映射多个
|
||||
mapAll: true # 是否整表映射, 要求源表和目标表字段名一模一样 (如果 targetColumns 也配置了映射,则以 targetColumns 配置为准)
|
||||
# targetColumns: # 字段映射, 格式: 目标表字段: 源表字段, 如果字段名一样源表字段名可不填
|
||||
# id:
|
||||
# name:
|
||||
# role_id:
|
||||
# c_time:
|
||||
# test1:
|
||||
```
|
||||
|
||||
|
||||
|
||||
其中,`targetTable` 格式为"库名.表名",实际测试中只能直接使用表名,此处需要进一步确认。
|
||||
|
||||
**按整库同步:**
|
||||
|
||||
```yaml
|
||||
dataSourceKey: defaultDS
|
||||
destination: example
|
||||
outerAdapterKey: mysql1
|
||||
concurrent: true
|
||||
dbMapping:
|
||||
mirrorDb: true
|
||||
database: mysql_db
|
||||
```
|
||||
|
||||
|
||||
|
||||
4. 执行以下命令,启动 RDB。
|
||||
|
||||
```bash
|
||||
bin/startup.sh
|
||||
```
|
||||
|
||||
|
||||
|
||||
服务日志位于 `logs` 目录。
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,193 @@
|
||||
OceanBase 数据加载技术
|
||||
=====================================
|
||||
|
||||
|
||||
|
||||
OceanBase 支持通过 LOAD DATA 命令加载外部文本文件的内容到数据库表中。
|
||||
|
||||
LOAD DATA语法格式如下:
|
||||
|
||||
```javascript
|
||||
LOAD DATA
|
||||
[/*+ parallel(N)*/]
|
||||
INFILE 'file_name'
|
||||
[REPLACE | IGNORE]
|
||||
INTO TABLE tbl_name
|
||||
[{FIELDS | COLUMNS}
|
||||
[TERMINATED BY 'string']
|
||||
[[OPTIONALLY] ENCLOSED BY 'char']
|
||||
[ESCAPED BY 'char']
|
||||
]
|
||||
[LINES
|
||||
[STARTING BY 'string']
|
||||
[TERMINATED BY 'string']
|
||||
]
|
||||
[IGNORE number {LINES | ROWS}]
|
||||
[(col_name_var
|
||||
[, col_name_var] ...)]
|
||||
```
|
||||
|
||||
|
||||
|
||||
其中,REPLACE 选项只适用于 MySQL 租户。
|
||||
**注意**
|
||||
|
||||
|
||||
|
||||
要加载的文件必须在该表的主副本所在的 OBServer 上,当前版本不支持从远程客户端加载数据。
|
||||
|
||||
Load Data 目前可以对CSV格式的文本文件进行导入,整个导入的过程如下:
|
||||
|
||||
1. 解析文件:OceanBase 会根据用户输入的文件名,读取文件中的数据,并且根据用户输入的并行度来决定并行或者串行解析输入文件中的数据。
|
||||
|
||||
2. 分发数据:由于 OceanBase 是分布式数据库系统,各个分区的数据可能分布在各个不同的 OBServer 上,Load Data 会对解析出来的数据进行计算,决定数据需要被发送到哪个 OBServer。
|
||||
|
||||
3. 插入数据:当目标 OBServer 收到了发送过来的数据之后,在本地执行 INSERT 操作把数据插入到对应的分区当中。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Load Data 提供了很多选项支持用户不同的需求,目前支持的选项有:
|
||||
|
||||
* **并行度:** /\*+ parallel(N)\*/选项指定加载数据的并行度,建议使用的值范围是\[0 - 租户的最大CPU数\]。例如:
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
load data /*+ parallel(4) */infile '/home/admin/a.csv' into table t
|
||||
```
|
||||
|
||||
|
||||
|
||||
* **输入文件:** INFILE 'file_name' 关键字指定输入文件的路径和文件名,目前 Load Data 只支持加载 OBServer 本地的输入文件。所以,用户需要在导入之前把文件拷贝到某一个 OBServer 上,并连接文件所在的 OBServer 运行Load Data 语句。
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- -->
|
||||
|
||||
* **执行权限:** 用户需要授予权限才能访问机器上的文件,有两步:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
* **重复数据处理:** 这部分指定如何处理重复的数据。Replace 表示将表中原有的数据替换成为输入文件中的数据; Ignore表示忽略掉重复的数据。Load Data 通过表的主键来判断数据是否重复,如果表不存在主键,那么 Load Data 语句就无法判断数据是否重复,Replace 和 Ignore 选项没有区别。如果用户不指定这个选项,那么遇到重复数据的时候,Load Data 语句会将出现把错误的数据记录到日志文件中。
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- -->
|
||||
|
||||
* **目标表选项:** INTO TABLE tbl_name 关键字用于指定目标表名称。Load Data 支持分区表和非分区表。
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- -->
|
||||
|
||||
* **字段格式:** 这部分指定输入文件的各个字段的分隔符选项,通过 Fields \| Columns 子句来指定,其中:Terminated By关键字用来指定字段的分隔符;Enclosed By 关键字指定每个字段的开始和结束是否包含了特定的字符;Escaped By 关键字用来指定字段中的通配符。
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- -->
|
||||
|
||||
* **行格式:** 这部分指定输入文件中每一行的开始和结束字符,通过 Lines 子句设置。 其中 Starting By 用于指定每一行开始的字符;Terminated By 用户指定每一行的结束字符。IGNORE number {LINES \| ROWS} 子句指定忽略掉输入文件的前 number 行数据。
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
load data /*+ parallel(4) */infile '/home/admin/a.csv' into table t fields terminated by ',' lines terminated by '\n';
|
||||
```
|
||||
|
||||
|
||||
|
||||
* **列对应关系选项:** 这部分用于指定目标表的各个列与输入文件的字段之间的关系,通过 (col_name_var \[, col_name_var\] ...) 关键字指定。如果用户没有指定,默认会将输入文件中的字段逐个与表中的列进行对应。如果用户通过col_name_or_user_var 关键字指定输入文件中的字段与表中列的对应关系,Load Data 会根据指定的列名与表中的列进行对应,没有被指定的列会取空值,下面是一个基本的示例:
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
load data infile '/home/admin/a.csv' into table t (id, names) set dates=CURRENT_TIMESTAMP;
|
||||
```
|
||||
|
||||
|
||||
|
||||
如果输入文件中并没有包含所有的列,那么缺少的列按照以下的规则会被默认填充:
|
||||
|
||||
|
||||
|
||||
如果用户需要添加空值,请在输入文件中使用'\\N'。
|
||||
|
||||
* **日志文件:** 如果导入的过程中出现了错误,出现错误的 INSERT 语句会被回滚,并且 Load Data 语句会在 OBServer 安装路径的 log 子目录下产生名称为 obloaddata.log.\<XXXXXX\> 的日志文件,以下是一个日志文件的示例:
|
||||
|
||||
|
||||
|
||||
|
||||
```javascript
|
||||
Tenant name: mysql
|
||||
File name: /home/admin/a.csv
|
||||
Into table: `test`.`t`
|
||||
Parallel: 1
|
||||
Batch size: 1000
|
||||
SQL trace: YD7A20BA65670-0005AADAAA3CAB52
|
||||
Start time: 2020-07-29 21:08:13.073741
|
||||
Load query:
|
||||
load data infile '/home/admin/test.csv' into table t fields terminated by ',' lines terminated by '\n'
|
||||
Row ErrCode ErrMsg
|
||||
1 1062 Duplicated primary key
|
||||
2 1062 Duplicated primary key
|
||||
```
|
||||
|
||||
|
||||
|
||||
日志中会包含 Load Data 产生的任务的基本信息,包含了:租户名,输入文件名,目标表名,并行度,使用的 Load Data 命令,并且以行为单位给出具体错误的信息。
|
||||
|
||||
|
||||
|
||||
**示例:通过** **Load Data** **导入 csv 文件到表** **ware2** **中**
|
||||
|
||||
```javascript
|
||||
[admin@h07g12092.sqa.eu95 /home/admin/csvdata]
|
||||
$more ware__df8f30ac_64e0_474c_9cc4_9919d64c5e4c
|
||||
2,1200,.0862,L6xwRsbDk,xEdT1jkENtbLwoI1Zb0,NT0j4RCQ4OqrS,vlwzndw2FPrO,XR,063311111
|
||||
1,1200,.1868,n1P4zYo8OH,jTNkXKWXOdh,lf9QXTXXGoF04IZBkCP7,srRq15uvxe5,GQ,506811111
|
||||
|
||||
$obclient -h192.168.1.101 -utpcc@obbmsql -P2881 -p123456
|
||||
|
||||
obclient> load data infile '/home/admin/csvdata/ware__df8f30ac_64e0_474c_9cc4_9919d64c5e4c' into table ware2 fields terminated by ',' lines terminated by '\n';
|
||||
Query OK, 2 rows affected (0.02 sec)
|
||||
Records: 2 Deleted: 0 Skipped: 0 Warnings: 0
|
||||
|
||||
obclient> select * from ware2\G
|
||||
*************************** 1. row ***************************
|
||||
W_ID: 2
|
||||
W_YTD: 1200
|
||||
W_TAX: .0862
|
||||
W_NAME: L6xwRsbDk
|
||||
W_STREET_1: xEdT1jkENtbLwoI1Zb0
|
||||
W_STREET_2: NT0j4RCQ4OqrS
|
||||
W_CITY: vlwzndw2FPrO
|
||||
W_STATE: XR
|
||||
W_ZIP: 063311111
|
||||
*************************** 2. row ***************************
|
||||
W_ID: 1
|
||||
W_YTD: 1200
|
||||
W_TAX: .1868
|
||||
W_NAME: n1P4zYo8OH
|
||||
W_STREET_1: jTNkXKWXOdh
|
||||
W_STREET_2: lf9QXTXXGoF04IZBkCP7
|
||||
W_CITY: srRq15uvxe5
|
||||
W_STATE: GQ
|
||||
W_ZIP: 506811111
|
||||
2 rows in set (0.00 sec)
|
||||
obclient>
|
||||
```
|
||||
|
||||
|
||||
@ -0,0 +1,78 @@
|
||||
Java 连接 OceanBase 示例
|
||||
=========================================
|
||||
|
||||
|
||||
|
||||
1. 添加 Maven 依赖
|
||||
|
||||
```javascript
|
||||
<dependency>
|
||||
<groupId>com.alipay.oceanbase</groupId>
|
||||
<artifactId>oceanbase-client</artifactId>
|
||||
<version>1.1.5</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
**说明**
|
||||
|
||||
|
||||
|
||||
如果需要下载 oceanbase-client,请点击:[oceanbase-client](https://oceanbase-aliyun-docs.oss-cn-hangzhou.aliyuncs.com/downloads/obclient/oceanbase-client-1.1.5.jar)。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- -->
|
||||
|
||||
2. 修改连接字符串
|
||||
|
||||
连接串的前缀需要设置为 jdbc:oceanbase ,其他部分的使用方式与原生的 MySQL 使用方式保持一致。
|
||||
|
||||
```javascript
|
||||
String url = "jdbc:oceanbase://192.168.1.101/TPCC?useUnicode=true&characterEncoding=utf-8";
|
||||
String username = "TPCC@obbmsql#obdemo";
|
||||
String password = "123456";
|
||||
Connection conn = null;
|
||||
try {
|
||||
Class.forName("com.alipay.oceanbase.obproxy.mysql.jdbc.Driver");
|
||||
conn = DriverManager.getConnection(url, username, password);
|
||||
PreparedStatement ps = conn.prepareStatement("select to_char(sysdate,'yyyy-MM-dd HH24:mi:ss') from dual;");
|
||||
ResultSet rs = ps.executeQuery();
|
||||
rs.next();
|
||||
System.out.println("sysdate is:" + rs.getString(1));
|
||||
rs.close();
|
||||
ps.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (null != conn) {
|
||||
conn.close();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
**注意**
|
||||
|
||||
|
||||
* 目前驱动与服务端交互使用的是文本写协议部分,为了兼容 Oracle 文本协议的 SQL 语法,驱动在PreparedStatement 的 setTimestamp() 类型中都会在前面增加 timestamp 的字面量,因此针对 PreparedStatement 模式下,timestamp 参数不支持字面量。
|
||||
|
||||
|
||||
|
||||
* ServerPreparedStatement 支持还不完善,因此请不要设置 useServerPrepStmts 和 cachePrepStmts 参数。
|
||||
|
||||
|
||||
|
||||
* 对于 Druid 框架,推荐使用 Druid 最新版 1.2.3:https://github.com/alibaba/druid/releases。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,64 @@
|
||||
OceanBase 常用参数和变量
|
||||
======================================
|
||||
|
||||
|
||||
|
||||
* 下表所示是常用的跟开发有关的参数(parameters)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| **参数名** | **参数值** | **参数含义** | **范围** |
|
||||
|---------------------------------------|---------|-----------------------------------------|--------|
|
||||
| minor_freeze_times | 0 | 指定多少次minor freeze后会触发major freeze。 | 集群 |
|
||||
| enable_major_freeze | True | 指定集群是否开启自动major freeze。 | 集群 |
|
||||
| major_freeze_duty_time | 02:00 | 指定集群自动 major freeze的开始时间。 | 集群 |
|
||||
| enable_rebalance | True | 指定是否开启分区负载均衡。True开启,False关闭。 | 集群 |
|
||||
| enable_auto_leader_switch | True | 指定是否开启分区自动切主。 | 集群 |
|
||||
| large_query_threshold | 100ms | 指定一个查询执行时间被判定为大查询的阈值。 | 集群 |
|
||||
| large_query_worker_percentage | 30 | 指定大查询要使用的CPU工作线程比例。 | 集群 |
|
||||
| data_copy_concurrency | 20 | 指定分区迁移时集群最大的迁移任务数。 | 集群 |
|
||||
| server_data_copy_out_concurrency | 2 | 指定分区迁移时每个节点最大的迁出任务数。 | 集群 |
|
||||
| server_data_copy_in_concurrency | 2 | 指定分区迁移时每个节点最大的迁入任务数。 | 集群 |
|
||||
| freeze_trigger_percentage | 70 | 指定增量内存使用比例的一个阈值,达到这个值会触发 minor freeze 。 | 集群 |
|
||||
| enable_perf_event | True | 指定是否开启性能事件信息收集,默认是False. | 集群 |
|
||||
| enable_sql_audit | True | 指定是否开启SQL日志,默认是True。 | 集群 |
|
||||
| sql_work_area | 1G | 指定租户的SQL工作内存。 | 租户 |
|
||||
| writing_throttling_maximum_duration | 1h | 指定触发租户限流时增量内存预估最大使用时间。 | 租户 |
|
||||
| writing_throttling_trigger_percentage | 100 | 指定触发租户限流时增量内存的使用比例阈值。 | 租户 |
|
||||
| max_stale_time_for_weak_consistency | 5s | 指定弱一致读能接受备副本的最大时延。 | 租户 |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
* 下表所示是常用的租户变量(variables)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| **变量名** | **变量值** | **变量含义** |
|
||||
|-----------------------------|---------------------------------------------------------------|------------------------------------------------------------------|
|
||||
| auto_increment_increment | 1 | 指定MySQL租户的自增列单次自增大小。 |
|
||||
| auto_increment_offset | 1 | 指定MySQL租户的自增列自增起始值。 |
|
||||
| auto_increment_cache_size | 1000000 | 指定MySQL租户的自增列内部缓存大小。 |
|
||||
| autocommit | ON | 指定租户是否开启事务自动提交。 |
|
||||
| ob_compatibility_mode | ORACLE/MYSQL | 显示当前租户的兼容类型。ORACLE/MYSQL。只读变量。 |
|
||||
| ob_tcp_invited_nodes | % | 指定租户访问的IP白名单,逗号分隔。如:127.1,192.168.0.0/16 |
|
||||
| ob_timestamp_service | GTS | 指定租户时间服务是用GTS还是LTS。 |
|
||||
| ob_query_timeout | 10000000 | 指定SQL执行默认超时时间,单位微秒(us)。 |
|
||||
| ob_trx_idle_timeout | 120000000 | 指定租户里事务最大空闲时间,单位微秒(us)。 |
|
||||
| ob_trx_timeout | 100000000 | 指定租户里事务最大持续时间,单位微秒(us)。 |
|
||||
| ob_read_consistency | STRONG | 指定租户里读SQL的默认一致性级别。STRONG是强一致读,WEAK是弱一致性读,FORZEN是读上次major freeze的 |
|
||||
| ob_sql_audit_percentage | 3 | 指定租户SQL日志占用最大内存百分比。 |
|
||||
| ob_sql_work_area_percentage | 5 | 指定租户SQL工作内存占内存最大百分比。 |
|
||||
| recyclebin | ON | 指定是否开启回收站。ON开启,OFF关闭。 |
|
||||
| sql_mode | STRICT_TRANS_TABLES,STRICT_ALL_TABLES,PAD_CHAR_TO_FULL_LENGTH | 指定SQL遵守的模式。 |
|
||||
| tx_isolation | READ-COMMITTED | 指定租户事务默认隔离级别。 |
|
||||
| time_zone | +8:00 | 指定租户默认时区。 |
|
||||
| system_time_zone | +08:00 | 显示系统默认时区。 |
|
||||
| version_comment | | 显示OceanBase版本,只读变量。 |
|
||||
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
OceanBase 常用 SQL Hints
|
||||
===========================================
|
||||
|
||||
|
||||
|
||||
SQL Hint的语法格式如下:
|
||||
|
||||
```javascript
|
||||
/*+ HINT_NAME */
|
||||
```
|
||||
|
||||
|
||||
|
||||
或
|
||||
|
||||
```javascript
|
||||
/*+ HINT_NAME ( HINT_PARA )
|
||||
```
|
||||
|
||||
|
||||
|
||||
如果是在命令行客户端 obclient 或 mysql 下连接 OceanBase,注意需要指定参数 -c ,这样 Hint 文本才会发送到 OBServer 端生效。
|
||||
|
||||
|
||||
|
||||
OceanBase 常用 SQL Hint 如下表所示:
|
||||
|
||||
|
||||
| **Hint** **名称** | **Hint** **参数** | **Hint** **语义** |
|
||||
|-------------------------|-------------------------------------------------------|----------------------------------------------------------------------------|
|
||||
| NO_REWRITE | | 不改写SQL。 |
|
||||
| READ_CONSISTENCY | weak\|strong\|frozen | weak:弱一致性读 strong:强一致性读 frozen:读最近一次冻结点的数据 |
|
||||
| INDEX_HINT | \[ *qb_name* \] *table_name* *index_name* | 指定查询表时选择的索引。 |
|
||||
| QUERY_TIMEOUT | *int64* | 指定语句执行的超时时间,单位是微秒(us)。 |
|
||||
| LEADING | \[ *qb_name* \] *table_name* \[, *table* _ *name* \] | 指定多表连接时的顺序。 |
|
||||
| ORDERED | | 指定多表连接顺序按SQL中表出现的顺序。 |
|
||||
| FULL | \[qb *_name* \] *table_name* | 指定表的访问方式为全表扫描(有主键时会读主键)。 |
|
||||
| USE_MERGE | \[qb *_name* \] *table_name* \[, *table_name* \] | 指定多表连接时使用MERGE算法。 |
|
||||
| USE_NL | \[ *qb_name* \] *table_name* \[, *table_name* \] | 指定多表连接时使用NEST LOOP算法。 |
|
||||
| USE_BNL | \[qb_name\] table_name \[,table_name\] | 指定多表连接时适用BLOCK NEST LOOP算法。 |
|
||||
| USE_HASH_AGGREGATION | \[ *qb_name* \] | 指定 aggregate 方法使用HASH AGGREGATE,例如HASH GROUP BY,HASH DISTINCT。 |
|
||||
| NO_USE_HASH_AGGREGATION | \[ *qb_name* \] | 指定 aggregate 方法不使用HASH AGGREGATE,使用MERGE GROUP BY,MERGE DISTINCT 。 |
|
||||
| QB_NAME | *qb_name* | 指定 query block的名称。 |
|
||||
| PARALLEL | *int64* | 指定分布式执行的并行度。 |
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user