百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

第03期列非空与自增

toyiye 2024-06-21 12:34 10 浏览 0 评论

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


一、NULL / NOT NULL

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

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

关于 NULL 的特性如下:

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

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

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: 0mysql> select * from t3;+------+| r1   |+------+| NULL || NULL |+------+2 rows in set (0.00 sec)

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

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

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: 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 过程中持有一个轻量级的互斥锁(线程级别,而不是事务级别),而不是直到语句结束才释放的表锁。

-- 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 默认就是交错模式。

-- 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。

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

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. 自增列也可以显式插入有符号的值

mysql> insert into f1 values (-10),(-20),(-30);Query OK, 3 rows affected (0.01 sec)Records: 3  Duplicates: 0  Warnings: 0mysql> select * from f1;+-----+| c1  |+-----+| -30 || -20 || -10 || 127 |+-----+4 rows in set (0.00 sec)

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


总结

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


关于 MySQL 的技术内容,你们还有什么想知道的吗?赶紧留言告诉小编吧!

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码