2018-03-06 15:34:44 +08:00
# Cetus sharding版使用指南
## 简介
Cetus sharding版支持对后端的数据库进行分库, 可以根据hash/range方式对大表进行分布式部署, 提升系统整体的响应和容量。
## 安装部署
### 准备
**1.MySQL**
- 5.7.16以上版本(分布式事务功能需要)
- 数据库设计(即分库,根据业务将数据对象分成若干组)
- 创建用户和密码
- 确认Cetus可以远程登录MySQL
**2.Cetus**
2018-03-29 15:35:59 +08:00
- 根据MySQL后端信息配置users.json、sharding.json和shard.conf( variables.json可选配) , 具体配置说明详见[Cetus 分库(sharding)配置文件说明](https://github.com/Lede-Inc/cetus/blob/master/doc/cetus-shard-profile.md)
2018-03-06 15:34:44 +08:00
**3.LVS & keepalived**
- 确定LVS的监听ip和端口
- 根据实际情况和需求确定多个Cetus的LVS分发权重
- 配置keepalived.conf
### 安装
2018-03-06 18:04:10 +08:00
Cetus只支持linux系统, 安装过程详见[Cetus 安装说明](https://github.com/Lede-Inc/cetus/blob/master/doc/cetus-install.md)
2018-03-06 15:34:44 +08:00
### 部署
Cetus 在部署时架构图如下图所示。
2018-03-06 18:04:10 +08:00
![Cetus sharding版本部署时架构图 ](https://github.com/Lede-Inc/cetus/blob/master/doc/picture/Cetus_deployment_sharding.png )
2018-03-06 15:34:44 +08:00
Cetus位于应用程序与MySQL数据库之间, 作为前端应用与数据库的通讯。其中, 前端应用连接LVS节点, LVS节点映射端口到多个Cetus服务, 后者通过自身的连接池连接到后端的数据库。
MySQL和Cetus可以部署在同一台服务器上, 也可以部署在各自独立的服务器上, 但一般LVS & keepalived与MySQL、Cetus分布在不同的服务器上。
### 启动
Cetus可以利用bin/cetus启动
```
bin/cetus --defaults-file=conf/shard.conf [--conf-dir= /home/user/cetus_install/conf/]
```
其中Cetus启动时可以添加命令行选项, --defaults-file选项用来加载启动配置文件, 且在启动前保证启动配置文件的权限为660; --conf-dir是可选项, 用来加载其他配置文件(.json文件), 默认为当前目录下conf文件夹。
Cetus可起动守护进程后台运行, 也可在进程意外终止自动启动一个新进程, 可通过启动配置选项进行设置。
## 数据库设计
### 1.专用术语
**1.数据分片**
按一定的规则,将数据表横向切分成若干部分并将其部署在一个或多个数据节点上。
**2.分片表**
即Sharding表, 将属于同一张表中的数据根据规则拆分成多个分片, 分别存入底层不同的存储节点。
**3.分片键**
即Sharding Key, Sharding表将根据此分片键再加上拆分规则, 从而成为sharding表。
**4.拆分方法**
目前支持的拆分规则有hash、range, 其中hash支持数字类型、字符串类型, range支持数字类型、字符串类型、时间类型( date/datetime) 。
Hash分片即数据库中的Hash分区。首先, 将Sharding表中Sharding Key的数据进行hash, 然后根据制定的分片规则, 进行数据存储。Sharding Key常选用数字类型。
Range分片类似数据库中的Range分区。将Sharding表中Sharding Key的数据进行范围分片, 根据规则, 进行数据存储。Sharding Key常选用数字类型、时间类型( DATE/DATETIME) 。
**5.全局表**
Public表, 即全局表, 具有相同VDB的存储节点公共表数据是一致的, 即都是全量数据, 但不同VDB的存储节点公共表是不同的, 如果想具有相同公共表, 需要前端再处理, 例如配置表conf, VDB1和VDB2都需要, 需要前端应用分别写入VDB1、VDB2。
**6.VDB**
VDB( Virtual DataBase) 非物理DB, 即逻辑DB, 主要对应分片表, 表现在业务数据层, 即代表此VDB内的数据有相同属性值, 可以根据此属性值, 进行数据的进一步拆分, 从而构成分片表, 例如订单数据和用户数据属于同一个VDB, 可以根据用户ID进行分片, 而仓储和商品可以放入另一个VDB。不同VDB之间的数据不能进行关联查询, 只有在同一个VDB内才支持。
### 2.设计原则
按业务数据的内在联系(例如,支持同一业务模块,或经常需要一起存取或修改等),将数据对象分成若干个组,使同一分组的数据表高度“内聚”,不同分组之间的表高度“独立”;找出每个组中各表共同的“根元”,以此作为分片键,对数据进行分片。
VDB要在业务设计之初确定, 后续的底层数据存储以及上层数据操作都会与此息息相关, 所以需要开发、DBA一同来设计。
以下是具体样例:
**用户VDB( VDB_ACCOUNT) **
描述:与用户订单相关联的业务
分片键: ACCOUNT_ID
TB_ORDER( 订单表)
TB_ACCOUNT( 用户信息表)
TB_PAY_ORDER( 支付订单表)
TB_CHARGE_ORDER( 充值订单表)
TB_TRANSFER_RECORD( 转账记录表)
TB_ACTIVITY_RECORD( 活动记录表)
TB_REFUND_ORDER( 退款记录表)
**产品VDB( VDB_PRODUCT) **
描述:与产品相关联的业务
分片键: PERIOD_ID
TB_PRODUCT( 商品信息表)
TB_PRODUCT_PERIOD( 商品期次表)
## 主要功能概述
### 1.连接池功能
Cetus内置了连接池功能。该连接池会在Cetus启动时, 使用默认用户自动创建到后端的连接。创建的连接使用的database是配置的默认database, 这些选项是由DBA根据后端数据库的实际情况进行的配置。以确保主要的业务请求过来不需要临时建立连接并切换数据库。
### 2.数据分片功能
Cetus支持对后端的数据库进行分库, 可以根据hash/range方式对大表进行分布式部署, 提升系统整体的响应和容量。由于分片处理后, 数据的整体性被破坏, 为了保证查询的结果符合预期, 我们需要对SQL进行分析, 并改写后发送到不同的后端。
### 3.分布式事务处理
如果前端执行SQL时, 开启了事务( start transaction) , 则统一采用分布式事务处理( 除非开启了单点事务的注释功能) , 如果未开启事务, 直接发送SQL指令, Cetus 在处理时会判断是否开启分布式事务。
### 4.结果集压缩
由于当连接距离较远网络延迟较大时, 结果集较大会很大幅度地增加数据传输时长, 降低性能, 因此针对高延迟场合, Cetus支持对结果集的压缩来提高性能。
网络延迟越大, 开启结果集压缩功能的优势越大, 当连接距离较近几乎没有网络延迟时, Cetus无形中增加了压缩和解压步骤, 反而降低了性能, 因此在延迟较小时不建议使用结果集压缩。
### 5.安全性管理
安全性管理功能包括后端管理、基本配置管理、查看连接信息、用户密码管理、IP许可管理、远程配置中心管理和整体信息查询。
2018-03-06 18:04:10 +08:00
用户可以通过登录管理端口, 查看后端状态, 增删改指定后端, 查看和修改基本配置( 包括连接池配置和从库延迟检测配置) , 查看当前连接的详细信息, 用户连接Cetus的密码查询和增删改, 查看以及增删改IP许可(可以为每个用户以及管理员用户指定允许访问的来源ip地址, 但是无法为来自不同ip的同一用户提供不同的权限设置), 重载远程配置和查看Cetus总体状态等, 具体管理操作相见[Cetus 分库(sharding)版管理手册](https://github.com/Lede-Inc/cetus/blob/master/doc/cetus-shard-admin.md)。
2018-03-06 15:34:44 +08:00
### 6.状态监控
Cetus内置监控功能, 可通过配置选择开启或关闭。开启后Cetus会定期和后端进行通信, 检测后端数据库的存活状态和主从延迟时间等。可通过登录管理端口SELECT * FROM backends查看。
后端状态包括unknown( 后端初始状态, 还未建立连接) 、up( 能与后端正常建立连接, 可以正常提供服务) 、down( 与后端无法联通, 无法正常提供服务) 、maintaining( 后端正在维护, 无法建立连接) 和delete( 后端已被删除) 。
主从延迟检测检测设有阈值slave-delay-down和slave-delay-recover, 检测到的从库延迟超过slave-delay-down阈值后端状态将被设置为DOWN, 从库延迟少于slave-delay-recover阈值后端状态将恢复为UP。
### 7.TCP流式
针对结果集过大情况, Cetus 采用了tcp stream流式, 不需要先缓存完整结果集才转发给客户端, 以避免内存炸裂问题, 降低内存消耗, 提高性能。
### 8.域名连接后端
支持利用域名连接数据库后端, Cetus设有相关的启动配置选项disable-dns-cache, 选择是否开启解析连接到后端的域名功能, 开启后可以通过设置域名并利用域名访问后端。
### 9.insert批量操作
2018-03-22 10:48:44 +08:00
支持在insert语句中写多个value, value之间用","隔开,例如:
2018-03-06 15:34:44 +08:00
INSERT INTO table (field1,field2,field3) VALUES ('a',"b","c"), ('a',"b","c"),('a',"b","c");
## 注意事项
### 1.连接池使用注意事项
所有的后端MySQL节点, 都需要有相同的用户名及口令, 在conf/users.json中指定, 不在选项中指定的用户, 不能用来登录Cetus, 不能为每个不同的后端实例单独指定登录信息。用户连接Cetus时的用户名和密码不一定与后端一致, 可在conf/users.json中查看。
### 2.set命令的支持说明
我们支持使用以下session级别的set命令: Set names/autocommit。global级别的set命令统一不支持, 其他未列出的set命令不在支持范围内, 不建议使用。如果业务确实需要, 建议联系DBA将行为配置为默认开启。
支持CLIENT_FOUND_ROWS 全局参数属性的统一设置, 同一个Cetus只能选择打开或者关闭, 不支持对每个连接单独设置这项属性, 可以通过设置启动配置选项来打开, 默认关闭。不支持CLIENT_LOCAL_FILES。其他未列出的需要额外设置的特性请测试后再确认。
### 3.环境变量修改建议
虽然我们支持客户端对连接环境变量进行修改, 但是, 我们不建议在程序中进行修改。因为一旦变量有改动。我们需要在执行SQL前对连接状态进行复位, 会产生额外的请求到服务端, 客户端响应的延迟也会增加。
### 4.代码处理
能用程序代码实现的函数尽量不用 SQL 函数,最好先在程序中计算出结果,再把值传入 SQL, 如时间类函数 curdate(),字符串处理类函数 trim()。因为函数用于分库键将无法路由,导致查询语句需要发送到所有后端,单事务可以搞定的事情就变成了分布式事务,性能变差。
程序做分页任务时,尽量自己记录页偏移,因为 Cetus 做偏移时offset 会重写, SQL 把 offset 置为零,数据全部取回后再截取该页需要的数据,这样一来数据量无形中就变大很多倍,且不能有效利用数据库索引,性能比较差,而且一旦数据超出内存阈值前端将接收到错误。
### 5.不支持 Kill query
不支持在 Sql 执行过程中 kill query操作, 一旦 Sql 语句开始执行就不能通过这种方式来终止, 此时可以连接Cetus 管理后端,通过执行 show connectionlist 命令查看正在执行的 Sql, 从而找到正在执行的后端信息, 通过数据库中 kill query的命令进行终止操作。
### 6.不支持TLS
不支持TLS协议, 目前不能在某种程度上使主从架构应用程序通讯本身预防窃听、干扰和消息伪造。
### 7.不支持多租户
目前分片版本还不支持。
### 8.不支持动态扩容
目前分库版暂不支持动态扩容,需要手工迁移数据,多数情况下需要“停机”扩容。
### 9.分区限制
目前只支持一级分区, 且最多支持64个分库( 全局表的更新数量太大, 会导致分布式事务性能急剧恶化) , 建议4, 8, 16个分库, 暂不支持二级分区; 分区的自增主键最好用第三方, 比如redis。
### 10.SQL书写规范
由于SQL需要进行完整解析器, 建议大家在书写涉及分片的SQL时, 要按照标准书写, 在测试环境下测试通过后再上线, 请遵循以下规范:
1. 在Select语句中尽量为表达式列或函数计算列添加别名, 比如“select count(\*) rowcnt from ...”, 以利于提高SQL解释器的分析水平。
2. Sql文本要简洁, 针对sharding表, 如果有条件, 请务必加sharding key做为过滤条件。
2018-03-22 10:48:44 +08:00
3. 开启事务推荐使用start transaction。
2018-03-06 15:34:44 +08:00
4. 少用子查询这种写法,必须用的话,可以用关联查询语法进行替换。
5. Update/delete操作要根据sharding key进行过滤后操作( 仅针对分片表) 。
6. For update语句不建议使用, 锁开销严重, 建议在应用端处理该业务逻辑, 比如引入分布式锁或者先分配给redis等等。
### 11.SQL支持
Cetus sharding版能支持大多数的SQL语句, 目前限制支持的功能有以下几种:
**不支持项:**
**1.不支持COUNT(DISTINCT)/SUM(DISTINCT)/AVG(DISTINCT)**
全局表没有限制;针对分片表建议分开操作,即先用 distinct 获取所有后端节点的值,类似 select distinct val from xxx order by val, 然后将数据整合到一起做去重计数/ 去重求和/ 去重求平均值的工作。
**2.不支持LAST_INSERT_ID**
目前线上没有发现该用法,如希望获取全局唯一值建议使用 redis 获取, 另外Cetus本身也提供了一种方法, select cetus_sequence(),即可返回一个 64 位递增不连续随机数字。
**3.不支持存储过程和视图**
**4.不支持批量sql语句的执行**
**5.不支持客户端的change user命令**
**6.不支持having多个条件**
**7.不支持含有any/all/some的子查询语句**
不支持含有any/all/some的子查询语句, 例如: select dept_no,emp_no from dept_emp where emp_no > any (select emp_no from dept_emp where dept_no='d001');若需要可转成关联查询语句。
**8.不支持load data infile**
**9.不支持handler语法**
**10.不支持lock tables语法**
**11.多个聚合函数情况下, 不支持having条件**
**限制支持项:**
**1.ORDER BY的限制**
2018-03-19 14:47:46 +08:00
针对全局表没限制; 针对分片表, 排序字段不超过8个列, ORDER BY需要使用列名或者别名, 目前暂且不支持使用数字, ORDER BY目前不支持字段为枚举类型的排序。
2018-03-06 15:34:44 +08:00
**2.DISTINCT的限制**
针对全局表没有限制; 针对分片表, 仅支持DISCTINCT字段同时也是ORDER BY字段, 例如: select distinct col1 from tab1 order by col1, 另外为了在使用上更加友好, 对于order by未写全的, Cetus会进行补充, 例如: selectdistinct col1,col2 from tab1 order by col1, Cetus会改写为 select distinct col1,col2 fromtab1 order by col1,col2, 但如果写成select distinct * from tab1, Cetus则会返回错误, 为了效率, 提倡使用标准写法, 以免造成不必要的资源开销。
**3.CASE WHEN/IF 的限制**
全局表没有限制; 针对分片表, 不能用于DML语句中, 也不能用在GROUP BY后, 可以用于SELECT 后,也可以作为过滤条件。
**4.分页查询的限制**
由于对分片的支持,我们带来的新限制。在结果集大于特定值时分页时,由于性能开销较大,可能无法返回准确值。
**5.JOIN的使用限制**
不支持跨库的JOIN, 非分片表可以在每个分片中都保存一份, 以提高join的使用成功率。
**6.Where条件的限制**
当Where条件中有分区列时, 值不能有函数转换, 也不能有算术表达式, 必须是原子值, 否则处理结果会不准确或者强制走全库查询, 增加后端数据库的负担。
目前支持有限的子查询类型以及有限的操作类型:支持子查询作为查询条件使用;支持子查询作为数据源使用。
**7.查询业务的限制**
在做SQL查询时, 应注意以下约束: 只支持同一个 VDB 内的关联查询;针对 sharding 表,在查询条件中可以使用 sharding key 的要求加上该过滤条件,
另外,使用 sharding key 时,不建议使用带有函数转换、算术表达式等逻辑处理, 会严重影响效率。
**8.PREPARE的限制**
不支持服务器端 PREPARE,可以用客户端的 PREPARE 代替。
**9.中文列名的限制**
对表列的中文列名或别名的使用有限制,使用中文列名或中文别名时必须加引号``。
### 12.事务处理限制
2018-03-23 08:15:27 +08:00
跨库事务的有限支持,针对同一分区键分布的事务,我们默认通过分布式事务方式执行,如果需要考虑性能,可以考虑在所有数据操作都在同一分区时,手动通过注释走单机事务提交。
2018-03-06 15:34:44 +08:00
不跨分区的事务需要在第一条语句中引用分区键, 方便Cetus进行SQL转发和路由, 并使用单机事务提升效率。
在分布式事务里面,要求用户尽量不要嵌入 select, 因为 select 是会加锁的,会导致性能非常差。
能用单事务就不要走分布式事务,所谓单事务就是此事务确定只会根据分片键定位到一个后端节点。
DML语句的限制: Update/Delete 支持子查询,但子查询中不要有嵌套(仅针对分片表);不支持对 sharding key 列进行 update( 仅针对分片表) ; Insert 使用时要写全列名, 例如: insert into a(col1,col2) values(xx,xx); Insert 不支持子查询,如有特殊业务需要用到,可以使用注释( 仅针对分片表, 详见注释功能) ; Insert 支持多 value 语句, 例如: insert into a(col1,col2) values(x,x),(xx,xx);支持 replace into/insert on duplicate key 语法。
2018-03-23 08:15:27 +08:00
### 13.分区键的类型
2018-03-06 15:34:44 +08:00
用于分区的列, 可以是“int”或“char”类型, “int”对应到MySQL中各种整数类型, “char”对应到各种定长和变长字符串类型, 日期类型在SQL中按字符串处理的话可以支持。 后续会支持特定格式的字符型表示的时间类型,如“YYYY- MM- DD” 和 “YYYY- MM- DD HH24:MI:SS” 格式,时间格式不支持针对时区进行转换,统一使用本地时间。
### 14.分区相关注释
Cetus提供注释功能, 用以解决日常维护时的需求( DBA同学经常使用) 和一些针对前端业务的特殊需求( 例如, 强制该SQL只通过主库或者从库进行操作) 。
注释书写样式 /\*# key=value \*/。
其中,以“/\*#”号开头(“/\*” 与“#”之间不允许有空格),“\*/”结尾, 中间以键值对形式书写, 如果value包含[a-zA-Z0-9_-.]以外的其它特殊字符,需加双引号 。Key/value的值大小写均可, 建议统一小写。
Sharding版支持的key类型: table|group|mode|transaction, 支持的value包括all/readwrite/readonly/single_node。
使用示例如下:
**1.Key类型为table的用法**
用法:/\*#table =employee\*/
SQL: select /\*# table=employee key=123\*/emp_no,emp_name from employee;
说明: 查询表employee中分区键的值是123的记录。
**2.Key类型为group的用法**
用法:/\*# group=dataA\*/
SQL: select /\*# group=dataA \*/ count(\*) from employee;
说明: 查询后端节点dataA中, 表employee的记录数。
用法:/\*# group=all \*/
SQL: create /\*# group=all \*/ table employee xxxx;
说明:在后端所有节点均创建此表。
**3.Key类型为mode的用法**
用法:/\*# mode=readwrite \*/
SQL: select /\*# mode=readwrite \*/ count(\*) fromemployee;
说明:此查询语句强制选择主库执行,查询操作默认选择从库执行。
**4.Key类型为transaction的用法**
用法:/\*# transaction=single_node \*/
SQL: update /\*# transaction=single_node \*/ departmentsset dept_name='ecbj' where dept_no='d010';
说明: 此dml语句将强制采用非分布式事务, 一旦Cetus在执行时判断应该采用分布式事务, 会返回错误。
**5.复合用法**
用法:/\*# table=employee key=123\*/ /\*#mode =readwrite\*/
SQL: select /\*# table=employee key=123\*/ /\*#mode =readwrite\*/ emp_no,emp_name from employee;
说明: 查询表employee中分区键的值是123的记录, 且强制从主库读取。
注意: table、group这两个key是互斥的, 即table或者group分别可以和mode/transaction共用, 但它俩不能同时出现, 否则会返回错误。另外, 请将注释部分写到第一个关键字之后。注释一旦使用, 其优先级高于后续where条件( 如果有的话) 中的分区路由信息。
## Cetus应用示例
### 1.连接Cetus
```
$ mysql --prompt="proxy> " --comments -h**.**.**.** -P**** -u**** -p***
proxy>
```
在连接Cetus时, 使用在配置文件中确认好的用户名和密码登陆, 登陆的ip和端口为Cetus监听的ip和端口。
可同时启动监听同一个ip不同端口的Cetus。
### 2.管理Cetus
```
$ mysql --prompt="admin> " --comments -h**.**.**.** -P**** -u**** -p***
admin> show connectionist;
```
可以使用在配置文件中的admin用户名和密码, 登陆地址为admin-address的MySQL对Cetus进行管理, 例如在查询Cetus的连接详细信息时, 可以登录后通过命令 show connectionlist, 显示从前端到后端端口映射关系, 以及涉及到的 xa 信息。
2018-03-06 18:04:10 +08:00
具体使用说明详见[Cetus 分库(sharding)版管理手册](https://github.com/Lede-Inc/cetus/blob/master/doc/cetus-shard-admin.md)
2018-03-06 15:34:44 +08:00
### 3.查看Cetus参数
```
$ cd /home/user/cetus_install/
bin/cetus --help | -h
```
2018-03-06 18:04:10 +08:00
可以查看帮助, 获取Cetus启动配置选项的参数及含义。