Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >第03期:列非空与自增

第03期:列非空与自增

作者头像
爱可生开源社区
发布于 2020-05-04 06:59:06
发布于 2020-05-04 06:59:06
62200
代码可运行
举报
运行总次数:0
代码可运行

MySQL 里字段的属性很多,对性能来说,影响也是可大可小,所以针对其属性这一块有必要进行一次探究。

一、NULL / NOT NULL

NULL 对外部程序来说,具体为不知道、不确切的、无法表述的值。所以在很多家公司的开发规范里都明确规定了,必须为 NOT NULL

其实用到 NULL 的场景都可以转换为有意义的字符或者数值,一是有利用数据的易读性以及后期的易维护性;二是降低 SQL 语句的编写难度。

关于 NULL 的特性如下:

1. 参与 NULL 字段拼接的结果都为 NULL,预期的可能会有差异

预想把字段 r1 做个拼接,再插入到新的表 t3 里,结果 t3 表的记录全为 NULL,跟预期不符。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mysql> show create table t1\G*************************** 1. row ***************************      Table: t1Create Table: CREATE TABLE `t1` ( `r1` varchar(10) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci1 row in set (0.00 sec)
mysql> show create table t2\G*************************** 1. row ***************************      Table: t2Create Table: CREATE TABLE `t2` ( `r1` varchar(10) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci1 row in set (0.00 sec)
mysql> create table t3 like t1;Query OK, 0 rows affected (0.04 sec)
mysql> insert into t3 select concat(r1,'database') from t1 limit 2;Query OK, 2 rows affected (0.02 sec)Records: 2  Duplicates: 0  Warnings: 0
mysql> select * from t3;+------+| r1   |+------+| NULL || NULL |+------+2 rows in set (0.00 sec)

那正确的方法如下,对 NULL 用函数 IFNULL 特殊处理。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mysql> insert into t3 select concat(ifnull(r1,''),'database') from t1 limit 2;Query OK, 2 rows affected (0.01 sec)Records: 2  Duplicates: 0  Warnings: 0
mysql> select * from t3;+----------+| r1       |+----------+| database || database |+----------+2 rows in set (0.00 sec)

2. 对于包含 NULL 列的求 COUNT 值也不准确

t1 和 t2 的记录数是一样的,但是字段 r1 包含了 NULL,这导致结果忽略了这些值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mysql> select count(r1) as rc from t1;+-------+| rc    |+-------+| 16384 |+-------+1 row in set (0.01 sec)
mysql> select count(r1) as rc from t2;+-------+| rc    |+-------+| 32768 |+-------+1 row in set (0.03 sec)

这时候我们可能想到了,正确的方法是用 NULL 相关函数处理,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mysql> select count(ifnull(r1,'')) as rc from t1;+-------+| rc    |+-------+| 32768 |+-------+1 row in set (0.03 sec)

或者是直接用 COUNT(*) 包含了所有可能的值

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mysql> select count(*) as rc from t1;+-------+| rc    |+-------+| 32768 |+-------+1 row in set (0.02 sec)

当然了不仅仅是 COUNT,除了 NULL 相关的函数,大部分对 NULL 处理都不友好。

所以其实从上面两点来看,NULL 的处理都得特殊处理,额外增加了编写 SQL 的难度。

3. 包含 NULL 的索引列

对包含 NULL 列建立索引,比不包含的 NULL 的字段,要多占用一个 BIT 位来存储。

示例

key_len 分别为 43 和 42,t1 比 t2 多了一个字节,那这里可能有人要问了,不是说占了一个 BIT 位吗?那为什么多了一个字节?可以关注我上一篇文章(第02期:MySQL 数据类型的艺术 - 大对象字段)关于 BIT 的详细描述。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mysql> pager grep -i 'key_len'PAGER set to 'grep -i 'key_len''
mysql> explain select * from t1 where r1 = ''\G     key_len: 431 row in set, 1 warning (0.00 sec)
mysql> explain select * from t2 where r1 = ''\G     key_len: 421 row in set, 1 warning (0.00 sec)

4. 各存储引擎相关的对 NULL 的处理

在 MySQL 8.0 发布后,仅有 InnoDB、MyISAM 和 Memory 支持对包含 NULL 列的索引,其他引擎不支持。比如 NDB。

二、AUTO_INCREMENT

列的自增属性,一般用来设置整数列根据一定步长逐步增长的值,类似于其他数据库的序列。不过这里的“序列”是基于特定一张表的。关于自增属性的相关特性如下:

1. 控制自增属性性能的变量:innodb_autoinc_lock_mode

innodb_autoinc_lock_mode=0

代表传统模式,也就是说,在对有自增属性的字段插入记录时,会持续持有一个表级别的自增锁,直到语句执行结束为止。比如说下面两条语句,SQL 1 在执行期间,一直持有对表 f1 的表级自增锁,接下来 SQL 2 执行时锁超时。

innodb_autoinc_lock_mode=1

代表连续模式,和传统模式差不多,不同的点在于对于简单的插入语句,比如 SQL 2,只在分配新的 ID 过程中持有一个轻量级的互斥锁(线程级别,而不是事务级别),而不是直到语句结束才释放的表锁。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- SQL 1mysql> insert into f1(c2) select rpad(uuid(),100,uuid()) from t1;Query OK, 16777216 rows affected (3 min 35.92 sec)Records: 16777216  Duplicates: 0  Warnings: 0
-- SQL 2mysql> insert into f1(c2) select 'database';ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

innodb_autoinc_lock_mode=2

代表交错模式。这个模式下放弃了自增表锁,产生的值会不连续。不过这是性能最高的模式,多条插入语句可以并发执行。MySQL 8.0 默认就是交错模式。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- SQL 1mysql> insert into f1(c2) select rpad(uuid(),100,uuid()) from t1;Query OK, 16777216 rows affected (3 min 35.92 sec)Records: 16777216  Duplicates: 0  Warnings: 0
-- SQL 2mysql> insert into f1(c2) select 'sql2';
Query OK, 1 row affected (0.02 sec)Records: 1  Duplicates: 0  Warnings: 0

那针对复制安全来说,以上三种模式,0 和 1 对语句级别安全,也就是产生的二进制日志复制到任何其他机器都数据都一致;2 对于语句级别不安全;三种模式对二进制日志格式为行的都安全。

2. 控制自增属性的步长以及偏移量

一般用在主主复制架构或者多源复制架构里,主动规避主键冲突。

auto_increment_increment 控制步长 auto_increment_offset 控制偏移量

3. 对于要立刻获取插入值的需求

就是说要获取一张表任何时候的最大值,应该时刻执行以下 SQL 3 ,而不是 SQL 2。SQL 2 里的函数 last_insert_id() 只获取上一条语句最开始的 ID,只适合简单的 INSERT。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-- SQL 1  mysql> insert into f1(c2) values ('xx1'),('xx2'),('xx3');  Query OK, 3 rows affected (0.01 sec)  Records: 3  Duplicates: 0  Warnings: 0
-- SQL 2  mysql> select last_insert_id() as last_insert_id;  +----------------+  | last_insert_id |  +----------------+  |              1 |  +----------------+  1 row in set (0.00 sec)-- SQL 3  mysql> select max(c1) as last_insert_id from f1;  +----------------+  | last_insert_id |  +----------------+  |              3 |  +----------------+  1 row in set (0.00 sec)
-- SQL 4mysql> select * from f1;+----+------+| c1 | c2   |+----+------+|  1 | xx1  ||  2 | xx2  ||  3 | xx3  |+----+------+3 rows in set (0.00 sec)

4. 自增列溢出现象

自增属性的列如果到了此列数据类型的最大值,会发生值溢出。比如变更表 f1 的自增属性列为 tinyint。

SQL 2 显式插入最大值 127, SQL 3 就报错了。所以这点上建议提前规划好自增列的字段类型,提前了解上限值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mysql> drop table f1;Query OK, 0 rows affected (0.04 sec)
mysql> create table f1(c1 tinyint auto_increment primary key);Query OK, 0 rows affected (0.05 sec)
-- SQL 1mysql> insert into f1 values (127);Query OK, 1 row affected (0.01 sec)
-- SQL 2mysql> select * from f1;+-----+| c1  |+-----+| 127 |+-----+1 row in set (0.00 sec)
-- SQL 3mysql> insert into f1 select null;ERROR 1062 (23000): Duplicate entry '127' for key 'PRIMARY'

5. 自增列也可以显式插入有符号的值

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mysql> insert into f1 values (-10),(-20),(-30);Query OK, 3 rows affected (0.01 sec)Records: 3  Duplicates: 0  Warnings: 0
mysql> select * from f1;+-----+| c1  |+-----+| -30 || -20 || -10 || 127 |+-----+4 rows in set (0.00 sec)

那针对这样的,建议在请求到达数据库前规避掉。

总结

本文讲了一个是字段是否应该为 NOT NULL,这时候应该很清楚了,字段最好是 NOT NULL;另外一个是字段的自增属性,其中关于性能与用法的相关示例。希望对大家有帮助。


本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爱可生开源社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
MySQL 8.0 之Index Skip Scan
MySQL 8.0.13开始支持 index skip scan 也即索引跳跃扫描。该优化方式支持那些SQL在不符合组合索引最左前缀的原则的情况,优化器依然能组使用组合索引。
用户1278550
2020/06/10
2.6K1
第19期:索引设计(哈希索引数据分布与使用场景)
哈希索引显式应用主要存在于内存表,也就是 Memory 引擎,或者是 MySQL 8.0 的 Temptable 引擎。本篇的内容上都是基于内存表,MySQL 内存表的大小由参数 max_heap_table_size 来控制,其中包含了表数据,索引数据等。
爱可生开源社区
2021/01/13
3520
第21期:索引设计(函数索引)
通常来讲,索引都是基于字段本身或者字段前缀(第 20 篇),而函数索引是基于字段本身加上函数、操作符、表达式等计算而来。如果将表达式或者操作符也看做函数的话,简单来说,这样的索引就可以统称函数索引。
爱可生开源社区
2021/02/26
8150
第21期:索引设计(函数索引)
MySQL中的自增列
当然基于MySQL自增列的实现,确实是不够优雅,在新的版本还在持续引入新的特性。比如MGR里面,自增列的步长大了许多,默认是7了,这是在设计的时候考虑了MGR的节点数,提前做了预留,大多数情况下我们可以避免大量的预留值浪费。
jeanron100
2018/07/26
2.5K0
MySQL中的自增列
第24期:索引设计(多值索引的适用场景)
多值索引和基于多个字段的联合索引完全不同,联合索引是基于多个一维字段,比如字段 r1 int, r2 int,r3 int,这三个字段的组合是联合索引。一般用于三个字段的联合查找,比如 r1 = 1 and r2 = 2 and r3 = 2 等等。
爱可生开源社区
2021/04/07
6980
新特性解读 | 8.0 新增 DML 语句(TABLE & VALUES)
资深数据库专家,专研 MySQL 十余年。擅长 MySQL、PostgreSQL、MongoDB 等开源数据库相关的备份恢复、SQL 调优、监控运维、高可用架构设计等。目前任职于爱可生,为各大运营商及银行金融企业提供 MySQL 相关技术支持、MySQL 相关课程培训等工作。
爱可生开源社区
2020/03/26
7070
第20期:索引设计(前缀索引)
这里主要介绍 MySQL 的前缀索引。从名字上来看,前缀索引就是指索引的前缀,当然这个索引的存储结构不能是 HASH,HASH 不支持前缀索引。
爱可生开源社区
2021/02/01
6560
第22期:索引设计(组合索引适用场景)
建立在多个列上的索引即组合索引(联合索引),适用在多个列必须一起使用或者是从左到右方向部分连续列一起使用的业务场景。
爱可生开源社区
2021/03/16
3180
第22期:索引设计(组合索引适用场景)
第23期:索引设计(组合索引不适用场景改造)
上篇文章已经详细介绍 MySQL 组合索引的概念以及其适用场景,这篇主要介绍 MySQL 组合索引的不适用场景以及改造方案。
爱可生开源社区
2021/03/16
2610
第23期:索引设计(组合索引不适用场景改造)
MySQL重大Bug!自增主键竟然不是连续递增
AUTO_INCREMENT=2,表示下一次插入数据时,若需要自动生成自增值,会生成id=2。
JavaEdge
2021/12/07
3.6K0
MySQL重大Bug!自增主键竟然不是连续递增
MySQL8索引篇:性能提升了100%!!
今天我们一起来聊聊MySQL 8.x版本中新增的三大索引。MySQL 8.x中新增了三种索引方式,这三种索引方式直接让MySQL原地起飞了,如下所示。
冰河
2022/06/15
2.7K0
第08期:有关 MySQL 字符集的注意事项
创建数据库时,显式指定字符集和排序规则,同时,当切换到当前数据库后,参数 character_set_database,collation_database 分别被覆盖为当前显式指定的字符集和排序规则。举个简单例子,创建数据库 ytt_new2,显式指定字符集为 latin1,同时排序规则为 latin1_bin。之后在切换到数据库 ytt_new2 后,对应的系统参数也被修改。
爱可生开源社区
2020/07/13
7140
浅析MySQL存储引擎序列属性
墨墨导读:为了达到标识的目的,许多应用程序需要生成唯一编号,比如:商品编号、交易流水号等。MySQL数据库同样能够支持这样的需求场景,AUTO_INCREMENT就是为MySQL实现序列的方式,它会自动生成序列编号。
数据和云
2020/09/14
1.5K0
第 49 期:根据 EXPLAIN EXTRA 栏提示进行优化(一)
经过前面篇幅的持续阅读,相信大家对 MySQL 的执行计划已经有了一个较为深入的理解。本篇将对传统执行计划输出结果里的 Extra 栏进行逐步解析,从而使得大家更进一步的了解 MySQL 的 SQL 优化过程。
爱可生开源社区
2025/01/15
380
第 49 期:根据 EXPLAIN EXTRA 栏提示进行优化(一)
MySQL 8.0 之不可见列
可以看到,我们的SQL里面创建了一个表t2的字段有id、name和age,其中,age字段设置了不可见属性。
AsiaYe
2021/06/09
1.5K0
MySQL 8.0 之不可见列
第09期:有关 MySQL 字符集的乱码问题
相信大家通过前几篇文章,已经了解了 MySQL 字符集使用相关注意事项。那么数据乱码问题在这儿显得就非常简单了,或许说可能不会出现这样的问题。
爱可生开源社区
2020/07/24
1.4K0
mysql8.0性能_oracle scan ip
MySQL从8.0.13版本开始支持一种新的range scan方式,称为Loose Skip Scan。该特性由Facebook贡献。我们知道在之前的版本中,如果要使用到索引进行扫描,条件必须满足索引前缀列,比如索引idx(col1,col2), 如果where条件只包含col2的话,是无法有效的使用idx的, 它需要扫描索引上所有的行,然后再根据col2上的条件过滤。
全栈程序员站长
2022/08/04
5390
innodb锁机制再探
当我们使用如上所述的语法的时候,这两种方式在事务(Transaction) 进行当中SELECT 到同一个数据表时,都必须等待其它事务数据被提交(Commit)后才会执行。
AsiaYe
2019/11/06
4220
innodb锁机制再探
第 50 期:根据 EXPLAIN EXTRA 栏提示进行优化(二)
接着上一篇继续,依然是举例说明 EXPLAIN EXTRA 提示栏里的相关释义,这些提示将有助于我们更好的理解 SQL 语句在 MySQL 里的具体执行计划。
爱可生开源社区
2025/01/22
740
第 50 期:根据 EXPLAIN EXTRA 栏提示进行优化(二)
mysql轻快入门(3)
有一种情况,你忘记数据库密码啦,你怎么办,砸电脑吗?no..... 请见下: select host,user,password from mysql.user; update mysql.user set password=password('123') where name='root';
py3study
2020/01/09
3250
相关推荐
MySQL 8.0 之Index Skip Scan
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验