第03期:列非空与自增

第03期:列非空与自增MySQL 里字段的属性很多,对性能来说,影响也是可大可小,所以针对其属性这一块有必要进行一次探究。 一、NULL / NOT NULL NULL 对外部程序来说,具体为不知道、不确切的、无法表述的…

第03期:列非空与自增

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

一、NULL / NOT NULL

NULL 对外部程序来说,具体为不知道、不确切的、无法表述的值。所以在很多家公司的开发规范里都明确规定了,必须为 NOT NULL。 其实用到 NULL 的场景都可以转换为有意义的字符或者数值,一是有利用数据的易读性以及后期的易维护性;二是降低 SQL 语句的编写难度。

关于 NULL 的特性如下:

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

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

mysql> show create table t1G
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`r1` varchar(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

mysql> show create table t2G
*************************** 1. row ***************************
Table: t2
Create Table: CREATE TABLE `t2` (
`r1` varchar(10) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 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)

代码100分

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

代码100分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,这导致结果忽略了这些值。

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 相关函数处理,

代码100分mysql> select count(ifnull(r1,"")) as rc from t1;
+-------+
| rc |
+-------+
| 32768 |
+-------+
1 row in set (0.03 sec)

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

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 的详细描述。

mysql> pager grep -i "key_len"
PAGER set to "grep -i "key_len""

mysql> explain select * from t1 where r1 = ""G
key_len: 43
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from t2 where r1 = ""G
key_len: 42
1 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 过程中持有一个轻量级的互斥锁(线程级别,而不是事务级别),而不是直到语句结束才释放的表锁。

-- SQL 1
mysql> 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 2
mysql> insert into f1(c2) select "database";
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

innodb_autoinc_lock_mode=2

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

-- SQL 1
mysql> 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 2
mysql> 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。

-- 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 4
mysql> 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 就报错了。所以这点上建议提前规划好自增列的字段类型,提前了解上限值。

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 1
mysql> insert into f1 values (127);
Query OK, 1 row affected (0.01 sec)

-- SQL 2
mysql> select * from f1;
+-----+
| c1 |
+-----+
| 127 |
+-----+
1 row in set (0.00 sec)

-- SQL 3
mysql> insert into f1 select null;
ERROR 1062 (23000): Duplicate entry "127" for key "PRIMARY"

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

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;另外一个是字段的自增属性,其中关于性能与用法的相关示例。希望对大家有帮助。


关于 MySQL 的技术内容,你们还有什么想知道的吗?赶紧留言告诉小编吧! 第03期:列非空与自增

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/8726.html

(0)
上一篇 2023-02-22 20:30
下一篇 2023-02-23

相关推荐

  • 数据库-数据库系统原理[通俗易懂]

    数据库-数据库系统原理[通俗易懂]数据库-数据库系统原理 落花人独立,微雨燕双飞。 简介:数据库-数据库系统原理。 一、事务 概念 事务指的是满足 ACID 特性的一组操作,可以通过 Commit 提交一个事务,也可以使用 Rollb

    2023-04-19
    166
  • Python中timedelta函数的详细解析

    Python中timedelta函数的详细解析时间是人类社会发展的必然产物,而对于任何研究、计算与管理时间的系统,其中最基础的概念就是时间差,也被称为时间间隔、时间距离等等。这个概念在计算机语言中得到了广泛应用,比如Python中的timedelta函数。timedelta函数是Python里处理时间差的一个库,它提供了一些方法来处理日期、时间、日期时间、时间间隔、时间段等数据,可以方便地对时间进行加减、日期计算、时间差的计算等操作。

    2024-05-27
    70
  • Redis 7.x 入门和开发实战「建议收藏」

    Redis 7.x 入门和开发实战「建议收藏」《Redis 7.x 入门和开发实战》技术专栏通过基础知识介绍入门-环境搭建-项目开发实践,让初学者快速掌握Redis。内部包括分布式缓存组件Redis 7.x的安装配置部署、基本数据类型、常用命令、

    2023-06-05
    154
  • mysqlsql查询语句大全及实例_数据库的语句

    mysqlsql查询语句大全及实例_数据库的语句Python基础之MySQL数据库 一、SQL语句常用查询方法 前期数据准备 ​ 为了更加直观的展示、演示SQL语句查询关键字,需导入下列表格与记录(数据) ​ 模拟公司,导入创建公司员工表,表内包含

    2023-06-17
    136
  • redis专项进阶课_redis项目

    redis专项进阶课_redis项目通过简单的KV数据库理解Redis 分为访问模块,操作模块,索引模块,存储模块 底层数据结构 除了String类型,其他类型都是一个键对应一个集合,键值对的存储结构采用哈希表 哈希表由多个哈希桶组成,

    2023-05-30
    148
  • python大数据分析包(数据分析常用的python包)

    python大数据分析包(数据分析常用的python包) Python在大数据行业非常火爆近两年,as a pythonic,所以也得涉足下大数据分析,下面就聊聊它们。

    2023-11-02
    181
  • Oracle学习笔记二十二:存储过程「终于解决」

    Oracle学习笔记二十二:存储过程「终于解决」一、存储过程简介 • 存储过程(Stored Procedure)是一组为了完成特定功能的PL/SQL语句块,经编译后存储在数据库中。 • 存储过程经编译和SQL优化后存储在数据库服务器中,使用时只要

    2023-05-21
    156
  • 深入学习Python API

    深入学习Python APIPython是一种高级编程语言,它被广泛应用于Web开发、数据分析、人工智能、自然语言处理等领域。Python语言特别适用于函数式编程,代码简洁易读,非常适合快速开发和原型设计。Python的API库也非常丰富,开发者可以使用这些API快速构建自己的应用程序。Python API是Python语言的精髓之一,是许多Python程序员和开发人员使用Python编写应用程序时必须了解的重要知识。

    2024-07-01
    40

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注