TiDB技术分享
点击上方“IT那活儿”,关注后了解更多精彩内容!!
1. 目的
本文档旨在为使用 TiDB 数据库的应用和系统提供统一规范参考,标准化 TiDB 数据库 的开发使用及SQL优化流程,提高业务开发系统的规范性和代码的可读性,减轻维护工作量, 提高工作效率。2. 适用范围
3. 注意事项
用于规范数据库对象的名命,如数据库(DATABASE)、表(TABLE)、索引(INDEX)、用户(USER)等的命名约定。1. 原则
1)命名建议使用具有意义的英文词汇,词汇中间以下划线分隔;3)避免用 TiDB 的保留字如:group,order 等作为单个字段名;2. 数据库命名规范
建议按照业务、产品线或者其它指标进行区分,一般不要超过 20 个字符。如:临时库( db_tmp)、测试库(db_test)。3. 表
3.1. 表命名规范
1)同一业务或者模块的表尽可能使用相同的前缀,表名称尽可能表达含义。3)建议对表的用途进行注释说明,以便于统一认识。如:● 临时表(tbl_tmp_crm_relation_0425);● 备份表(tbl_bak_crm_relation_20170425)。4)不同业务模块的表多带带建立 DATABASE,并增加相应注释。5)目前 TiDB 只支持将 lower-case-table-names 值设为 2,即按照大小写来保存表名,按照小 写来比较(不区分大小写)。3.2. 表的设计
1)在进行 TiDB 表设计过程中,有以下几点需要注意:● 为了保障主从集群复制以及增量备份的幂等性,表需要有主键。建议采用自然主键,而非业务字段作为主键。TIDB为避免热点问题,可以用AutoRandom替换AUTOINCREMENT,随机分配,采用unsigned bigint 作为该列的列属性。建议采用聚簇索引,非聚簇索引主键实际为唯一索引,存储仍以隐式的rowid构成。聚簇索引不支持增删改操作。● 出于为性能考虑,尽量避免存储超宽表,表字段数不建议超过 60 个,建议单行的总数 据大小不要超过 64K,数据长度过大字段最好拆到另外的表。● 表及字段要加commet属性,方便业务的理解。● 单表数据规模建议在1000万以内。对于大型业务表,建议建表初期设计分区以及清理备份策略。● 需要 join 的字段,数据类型保障绝对一致,避免隐式转换。● 字段属性尽量加上NOT NULL约束以及默认值,使用NULL值会导致如下副作用:----含义不明,对很多运算符不管用,增加复杂度。2)TiDB 字符集默认就是 UTF8 ,而且目前只支持 UTF8:● 使用 utf8mb4 编码,它是 utf8 的超集,除了将编码改为 utf8mb4 外不需要做其他转换。● TiDB 中,utf8mb4 的默认排序规则为 utf8mb4_bin(区分大小写)。● 4.0 支持 utf8mb4_general_ci(不区分大小写) ,需要初始化集群前配置 new_collations_enabled_on_first_bootstrap = true 。4. 字段
4.1. 字段命名规范
3)字段需要添加注释,枚举型需指明主要值的含义,如”0 - 离线,1 - 在线”;4)布尔值列命名为 [is_描述]。如 member 表上表示为 enabled 的会员的列命名为 is_enabled ;5)字段名不建议超过 30 个字符,字段个数不建议大于 60;6)尽量避免使用保留字,如 order、from、desc 等,请参考官方保留字。4.2. 字段的设计
● TiDB 支持 MySQL 所有的整数类型,包括 INTEGER/INT、TINYINT、SMALLIN、T MEDIUMINT 以及 BIGINT;● INT:所有整数类型的字段推荐只使用 INT 或者 BIGINT;● 推荐使用 INT(10) UNSIGNED 存储 IPv4 格式 IP 地址;● TINYINT(1)、TINYINT(4) 都是存储一个字节,并不会因为括号里的数字改变。例如 TINYINT(4) 存储 22 则会显示 0022,因为最大宽度为4,达不到的情况下用0来补充。● TiDB 支持 MySQL 所有的浮点类型,包括 FLOAT、DOUBLE,DECIMAL、NUMERIC 等。● DECIMAL(M,D):推荐使用 DECIMAL 类型。float 和 double 在存储的时候,存在精度 损失的问题,很可能在值的比较时,得到不正确的结果。DECIMAL 在与 VARCHAR、 CHAR 类型比较时会转换为 DOUBLE 类型进行比较,也存在精度损失问题,建议在进 行比较时使用显示类型转换,如 CAST 避免精度损失。● TiDB 支持 MySQL 所有的日期时间类型,包括 DATE、DATETIME、TIMESTAM、P TIME 以及 YEAR;● DATE:所有只需要精确到天的字段全部使用 DATE 类型,而不应该使用 TIMESTAMP 或者DATETIME 类型;● DATETIME:所有需要精确到时间(时分秒)的字段均使用 DATETIME,不要使用 TIMESTAMP 类型;● 时间字段使用时间日期类型,不要使用字符串类型存储。否则无法利用日期函数对日 期字段进行操作,而需要使用字符串函数进行复杂的操作。● 尽管 TiDB 尝试解释不同的格式,日期部分必须是按 年-月-日 的顺序(比如,’ 98-09-04’),而不是 月-日-年 或者 日-月-年 的顺序;● 要求年份必须是四位数字。日期值中包含两位数字的年份是有歧义的,TiDB 按下面规则解释:范围在 70-99 之间的被转换成 1970-1999 范围在 00-69 之间的被转换成 2000-2069 。● TiDB 支持 MySQL 所有的字符串类型,包括 CHAR、VARCHAR、BINARY、 VARBINARY、BLOB、TEXT、ENUM 以及 SET;● VARCHAR(N):所有动态长度字符串全部使用 VARCHAR 类型,N 表示的是字符数不 是字节数,比如 VARCHAR(256),需要根据实际的宽度来选择 N,N 尽可能小;● CHAR:仅仅只有单个字符的字段使用 CHAR(1) 类型,例如性别字段;● TEXT:仅仅当字符数量可能超过 20000 个的时候,才建议使用TEXT类型来存放字符 类数据,因为所有 TiDB 数据库都会使用 UTF8 字符集。所有使用 TEXT 类型的字段 建议和原表进行分拆,与原表主键多带带组成另外一个表进行存放;● 不建议使用 ENUM、SET 类型,尽量使用 TINYINT 来代替。5. 索引
5.1. 索引命名规范
1)主键索引:
2)唯一索引:
3)普通索引:
idx_[表名称简写]_[字段名简写] 4)多单词组成的 column_name,取尽可能代表意义的缩写。5.2. 索引设计
1)选择区分度大的列建立索引,不在低基数列上建立索引,例如:“性别”,“是否是 XXX”;2)单张表的索引数量控制在 5 个以内,避免冗余索引;6)对于确定需要组成组合索引的多个字段,建议将选择性高的字段靠前放;7)最左前缀原则,使用联合索引时,从左向右匹配,比如索引 idx_c1_c2_c3 (c1,c2,c3,)相当 于创建了 (c1)、(c1,c2)、(c1,c2,c3) 三个索引,where 条件包含上面三种情况的字段比较则可 以用到索引,但像 where c1=a and c3=c 只能用到 c1 列的索引,像 c2=b and c3=c 等情况就 完全用不到这个索引;8)很长的 VARCHAR 字段建立索引时,指定索引长度,没必要对全字段建立索引,根据 实际 文本区分度决定索引长度即可。idx_table_name (name(10));10)ORDER BY,GROUP BY,DISTINCT 的字段需要添加在索引的后面,形成覆盖索引;11)不建议在 where 条件索引列上使用函数,会导致索引失效,如 lower(email);12)使用 like 模糊匹配,% 不要放首位,会导致索引失效。5.3. TiDB 中的索引
索引也是数据,也要占用存储空间。和表中的数据一样,TiDB 中表的索引在存储引擎 中也被作为 kv 来存储,一行索引是一个 kv 对。例如一张有 10 个索引的表,每插入一行数据 的时候,会写入 11 个 kv 对。TiDB 支持主键索引,唯一索引,也支持二级索引,构成以上索引的可以是单一列,也 可以是多个列(复合索引)。TiDB 目前(v5.0)还不支持反向/双向索引,全文索引,分区表的全局索引。TiDB 中在查询的谓词是 =,>,=,<=,like ‘...%’,not like ‘.,..i%n,’ not in,<>,!=, is null,is not null,between…and … 时能够使用索引,使用与否由优化器来决策。TiDB 中在查询的谓词是 like ‘%...’,like ‘%...%’,not like ‘%..,.’not like ‘%...%’,<=> 时 ,都无法使用索引。目前版本(v5.0)还没有对 <=> 做处理,无法像 is null 一样使用索引。1. 账号权限
TiDB 在数据库初始化时会生成一个 root@% 的默认账户,生产环境建议调整 root 用户为强密码,且不对外开放。线上所需用户建议按照用户或者业务场景划分,根据实际情况对每个用户授予相应权限,例如:1
| root | 超级用户 | 全局管理,禁止对外开放 |
2
| dba | 数据库管理员 | 数据库 DBA |
3
| app | 应用开发 | 应用开发 |
4
| tempuser | 临时统计 | 线上业务临时统计,只读用户 |
5
| other | 其他用户 | 第三方人员访问 |
2. 角色
角色是一系列权限的集合。用户可以创建角色、删除角色、将权限赋予角色;也可以将角色授予给其他用户,被授予的用户在启用角色后,可以得到角色所包含的权限。2.1. 创建角色
CREATE ROLE app_developer, app_read, app_write;
角色会被保存在 mysql.user 表中,角色名称的主机名部分(如果省略)默认为 %。如果表中有同名角色或用户,角色会创建失败并报错。创建角色的用户需要拥有 CREATE ROLE 或 CREATE USER 权限。2.2. 角色授权
为 app_read 角色授予数据库 app_db 的读权限:GRANT SELECT ON app_db.* TO app_read@%;
2.3. 角色赋给用户
GRANT app_read TO dev1@localhost;
2.4. 默认角色
角色在授予给用户之后,并不会生效;只有在用户启用了某些角色之后,才可以使用角色拥有的权限。可以对用户设置默认启用的角色;用户在登录时,默认启用的角色会被自动启用。比如将 app_read 和 app_wirte 设置为 rw_user1@localhost 的默认启用角色:SET DEFAULT ROLE app_read, app_write TO rw_user1@localhost;
将 dev1@localhost 的所有角色,设为其默认启用角色:SET DEFAULT ROLE ALL TO dev1@localhost;
2.5. 启用角色
为当前用户启用角色 app_read 和 app_write ,仅在当前 session 有效:SET ROLE app_read, app_write;
SET ROLE ALL EXCEPT app_read
1. 建表删表规范
1.1. 基本原则:表的建立在遵循表命名规范前提下,建议业务应用内部封装建表删表语句增加判断逻辑,防止业务流程异常中断。1.2. 详细说明:create table if not exists table_name 或 者 drop table if exists table_name 语句建议增加 if 判 断,避免应用侧由于表的改动造成的异常中断。1.3. 不支持 create table as select 语法,需要改写为表结构复制 create table like … 和将数据写入的 insert into select … 的组合语句。2. select * 使用规范
2.1. 基本原则:禁止使用 select * 进行查询。2.2. 详细说明:按需求选择合适的字段列,杜绝直接 SELECT * 读取全部字段,减少网络带宽消耗,有效利用 覆盖索引。3. Region 热点
● 这条 SQL 涉及到的 region 的 leader 全部在这个 TiKV 上;● 这条 SQL 只涉及到一个 region,并且有大量的请求使用同样或者类似的 SQL。3.1. 基本原则:如果表的数据量比较小,数据存储大概率只涉及到一个 region,大量请求对该表进行写入或者读取都会造成该 region 热点,可以通过手工拆分 region 方式进行调整,也可以在建表时预先进行 split region。●Handle ID 设计 避免连续自增主键的设计,建议采用AutoRandom替换AUTOINCREMENT,随机分配;● TiDB Partition 通过设置表分区方式来避免热点,支持按照 Hash 以及按照 Range 分区。4. 字段上使用函数规范
4.1. 基本原则:在取出字段上可以使用相关函数,但是在 Where 条件中的过滤条件字段上严禁使用任何函数, 包括数据类型转换函数。``` select gmt_create from ... where
date_format(gmt_create,%Y%m%d %H:%i:%s) = 20090101 00:00:0 ```
``` select date_format(gmt_create,%Y%m%d %H:%i:%s) from .. .
where gmt_create = str_to_date(20090101 00:00:00,%Y%m%d %H:%i:s);```
5. 数据删除规范
删除表中全部的数据时,使用 TRUNCATE 或者 DROP 后重建方式,不要使用 DELETE;DELETE,TRUNCATE 和 DROP 都不会立即释放空间,对于 TRUNCATE 和 DROP操作,在 达到 TiDB 的 GC (garbage collection) 时间后(默认 10 分钟),TiDB的 GC 机制会删除数据 并释放空间。对于 DELETE 操作 TiDB 的 GC 机制会删除数据,但不会释放空间,而是当后 续数据写入 RocksDB 且进行 compact 时对空间重新利用。TiDB 支持的隔离级别是 Snapshot Isolation(SI),和 RepeatabRleead(RR) 隔离级别 基本等价。基于日志的数据库在面对大事务时,需要手动调大可用日志的容量,以避免日志被单 一事务占满。TiDB 处理大事务的性能相较于处理并发的小事务的性能要差,因此 TiDB 中对于事务 量设定了一些限制:● 最大单行记录容量为 120MB(v5.0 及更高的版本可通过 tidb-server 配置项 performance.txn-entry-size-limit 调整,低于 v5.0 的版本支持的单行容量为 6MB);● 支持的最大单个事务容量为 10GB(v4.0 及更高版本可通过 tidb-server 配置项 performance.txn-total-size-limit 调整,低于 v4.0 的版本支持的最大单个事务容量为 100MB)。1. group by
出于便捷的考量,MySQL “扩展” 了 group by 语法,使 select 子句可以引用未在 group by 子句中声明的非聚集字段,也就是 non-full group by 语法,在其他数据库中,这被认为是一种语法错误,因为这会导致结果集不稳定。想保障 group by 语句结果集的稳定,请使用 full group by 语法。2. order by
在 SQL 的语义中,只有使用了 order by 语法才会保障结果集的顺序输出。而单机数据 库由于数据都存储在一台服务器上,在不进行数据重组时,多次执行的结果往往是稳定的,有些数据库(尤其是 MySQL InnoDB 存储引擎)还会按照主键或索引的顺序进行结果集的输出。TiDB 是分布式数据库,数据被存储在多台服务器上,另外 TiDB 层不缓存数据页,因此不含 order by 的 SQL 语句的结果集展现顺序容易被感知到不稳定。想要按顺序输出的结果集,需 明确的把要排序的字段添加到 order by 子句中,这符合 SQL 的语义。1. GC 超时
TiDB的事务的实现采用了 MVCC(多版本并发控制)机制,当新写入的数据覆盖旧的数据时,旧的数据不会被替换掉,而是与新写入的数据同时保留,并以时间戳来区分版本。TiDB 通过定期 GC 的机制来清理不再需要的旧数据。默认配置下 TiDB 可以保障每个 MVCC 版本(一致性快照)保存 10 分钟,读取时间超 过 10 分钟的事务,会收到报错GC life time is shorter than transaction duration 当用户确信自己需要更长的读取时间时,比如在使用了 Mydumper 做全量备份的场景 中(Mydumper 备份的是一致性的快照),可以通过调整 TiDB 中 mysql.tidb 表中的 tikv_gc_life_time 的值来调大 MVCC 版本保留时间,需要注意的是 tikv_gc_life_time 的配置是立刻影响全局的,调大它会为当前所有存在的快照增加生命时长,调小它会立即缩短所有快照的生命时长。过多的 MVCC 版本会拖慢 TiKV 的处理效率,在使用 Mydumper 做完全量备份 后需要及时把 tikv_gc_life_time 调整回之前的设置。2. 事务超时
含 DML 语句的事务,除了受tikv_gc_life_time 限制之外,还受到另外一个参数 max-txn-time-use 的影响,这个参数位于 tidb-server 的配置文件 tidb.toml 中,用于控制单个事 务允许的最大执行时间。该参数的默认值为 590(秒),需要注意必须控制该参数的值小于 tikv_gc_life_time 的值。形如 insert into t10 select * from t1 的 SQL 语句,即使执行时间没有达到 tikv_gc_life_time 限制,但超过了 max-txn-time-use 的限制,会由于超时而回滚。3. SQL 超时
TiDB 还提供了一个系统变量来限制单条 SQL 语句的执行时间:max_execution_time, 它的默认值为 0,表示无限制。max_execution_time 目前对所有类型的 statement 生效,并非只 对 SELECT 语句生效。其单位为 ms,但实际精度在 100ms 级别,而非更准确的毫秒级别。1. TiDB 服务端兼容
TiDB 服务端兼容 MySQL 5.7,客户端推荐使用 5.1.36 或更高版本 的 5.1.x jdbc 驱动。2. JDBC 参数设置
Java 应用常用的数据库连接池包括 weblogic、c3p0、Druid等。使用连接池配置数据源 时,需要配置一系列参数,其中比较重要的包括 jdbc 的 url 配置,超时探活机制等。充分认识并理解各项参数有助于让 TiDB 发挥出更高的性能。spring.datasource.url=JDBC:mysql://{TiDBIP}:
{TiDBPort}/{DBName}?characterEncoding=
utf8&useSSL=false&useServerPrepStmts=true&prepStmtCacheSqlLi
mit=10000000000&useCon
figs=maxPerformance&rewriteBatchedStatements=true&defaultfet
chsize=-214783648
序列是一种数据库对象,应用程序通过调用某个序列可以产生递增的序列值,应用程序可以灵活的使用这个序列值为一张表或多张表赋值,也可以使用序列值进行更复杂的加工,来实现文本和数字的组合,来赋予代理键以一定的跟踪和分类的意义。TiDB 从 v4.0 版本开 始提供序列功能,详情请参考官方文档。CREATE [TEMPORARY] SEQUENCE [IF NOT EXISTS] sequence_name
[ INCREMENT [ BY | = ] increment ]
[ MINVALUE [=] minvalue | NO MINVALUE | NOMINVALUE ]
[ MAXVALUE [=] maxvalue | NO MAXVALUE | NOMAXVALUE ]
[ START [ WITH | = ] start ]
[ CACHE [=] cache | NOCACHE | NO CACHE]
[ CYCLE | NOCYCLE | NO CYCLE]
[ ORDER | NOORDER | NO ORDER]
[table_options]
参数 | 默认值
| 描述
|
TEMPORARY | FALSE | TiDB 暂时不支持 TEMPORARY 选项,仅在语法上做兼容。 |
INCREMENT | 1 | 指定序列的步长。其正负值可以控制序列的增长方向。 |
MINVALUE | 1 或 -9223372036854775807 | 指定序列的最小值。当 INCREMENT > 0 时,默认值为 1;当 INCREMENT < 0 时,默认值为 -9223372036854775807。 |
MAXVALUE | 9223372036854775806 或 -1 | 指定序列的最大值。当 INCREMENT > 0 时,默认值为 9223372036854775806;当 INCREMENT < 0 时,默认值为 -1。 |
START | MINVALUE 或 MAXVALUE | 指定序列的初始值。当 INCREMENT > 0 时,默认值为 MINVALUE; 当 INCREMENT < 0 时,默认值为 MAXVALUE。 |
CACHE | 1000 | 指定每个 TiDB 本地缓存序列的大小。 |
CYCLE | NO CYCLE | 指定序列用完之后是否要循环使用。在 CYCLE 的情况下,当 INCREMENT > 0 时,序列用完后的后续起始值为 MINVALUE;当 INCREMENT < 0 时,序列用完后的后续起始值为 MAXVALUE。 |
ORDER | NO ORDER | TiDB 暂时不支持 ORDER 选项,仅在语法上做兼容。 |
1. SEQUENCE函数
● NEXTVAL 或 NEXT VALUE FOR
本质上都是 nextval() 函数,获取序列对象的下一个有效值,其参数为序列的 identifier。● LASTVAL
lastval() 函数,用于获取本会话上一个使用过的值。如果没有值,则为 NULL,其参数为序列的 identifier。● SETVAL
setval() 函数,用于设置序列的增长。其第一参数为序列的 identifier,第二个参数为 num。2. 示例
2.1. 创建一个默认参数的序列对象
CREATE SEQUENCE seq;
Query OK, 0 rows affected (0.06 sec)
2.2. 使用 nextval() 函数获取序列对象的下一个值
SELECT nextval(seq);
+--------------+
| nextval(seq) |
+--------------+
| 1 |
+--------------+
1 row in set (0.02 sec)
2.3. 使用 lastval() 函数获取本会话上一次调用序列对象所产生的值
SELECT lastval(seq);
+--------------+
| lastval(seq) |
+--------------+
| 1 |
+--------------+
row in set (0.02 sec)
2.4. 使用 setval() 函数设置序列对象当前值的位置
SELECT setval(seq, 10);
+-----------------+
| setval(seq, 10) |
+-----------------+
| 10 |
+-----------------+
1 row in set (0.01 sec)
2.5. 使用 next value for 语法获取序列的下一个值
SELECT next value for seq;
+--------------------+
| next value for seq |
+--------------------+
| 11 |
+--------------------+
1 row in set (0.00 sec)
1. ADD COLUMN
ALTER TABLE.. ADD COLUMN 语句用于在已有表中添加列。在 TiDB 中,ADD COLUMN 为在线操作,不会阻塞表中的数据读写。● 不支持将新添加的列设为 PRIMARY KEY;● 不支持将新添加的列设为 AUTO_INCREMENT。2. ADD INDEX
ALTER TABLE.. ADD INDEX 语句用于在已有表中添加一个索引。在 TiDB 中,ADD INDEX 为在线操作,不会阻塞表中的数据读写。● 不支持 FULLTEXT,HASH 和 SPATIAL 索引;● 不支持降序索引(类似于 MySQL 5.7);● 无法向表中添加 CLUSTERED 类型的 PRIMARY KEY。3. ALTER TABLE
3.1 ALTER TABLE 语句用于对已有表进行修改,以符合新表结构。ALTER TABLE 语句可用于:● ADD,DROP,MODIFY 或 CHANGE 列。3.2 TiDB 中的 ALTER TABLE 语法主要存在以下限制:● 不支持在单个 ALTER TABLE 语句中进行多个更改;● 不支持主键列上 Reorg-Data 类型的变更;● 不支持部分数据类型(例如,部分时间类型、Bit、Set、Enum、JSON 等)的变更,因为 TiDB 中的 CAST 函数与 MySQL 的行为存在兼容性问题;4. ALTER INDEX
ALTER INDEX 语句用于修改索引的可见性,可以将索引设置为 Visible 或者 Invisible。设置为 Invisible 的索引即不可见索引 (Invisible Index) 由 DML 语句维护,不会被查询优化器使用。5. CHANGE COLUMN
ALTER TABLE.. CHANGE COLUMN 语句用于在已有表上更改列,包括对列进行重命名,和将数据改为兼容类型。从 v5.1.0 版本起,TiDB 开始支持 Reorg 数据的类型变更,包括但不限于:● 从 varchar(10) 到 varchar(5) 的长度压缩。6. MySQL 兼容性
● 不支持在单个 ALTER TABLE 语句中进行多个更改;●不支持主键列上 [Reorg-Data](/sql-statements/sql-statement-modify-column.md#Reorg-Data Change) 类型的变更;● 不支持部分数据类型(例如,部分时间类型、Bit、Set、Enum、JSON 等)的变更,因为TiDB 中 CAST 函数与 MySQL 的行为存在兼容性问题。
本 文 原 创 来 源:IT那活儿微信公众号(上海新炬王翦团队)
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/129680.html