【优化】Mysql字段尽可能用NOT NULL

下面咱们要聊的是 MySQL 里的 null,在大量的 MySQL 优化文章和书籍里都提到了字段尽可能用NOT NULL,而不是NULL,除非特殊情况。但却都只给结论不说明原因,犹如鸡汤不给勺子一样,让不少初学者对这个结论半信半疑或者云里雾里。本文今天就详细的剖析下使用 Null 的原因,并给出一些不用 Null 的理由。

1、NULL 为什么这么多人用?

  • NULL是创建数据表时默认的,初级或不知情的或怕麻烦的程序员不会注意这点。
  • 很多人员都以为not null 需要更多空间,其实这不是重点。
  • 重点是很多程序员觉得NULL在开发中不用去判断插入数据,写sql语句的时候更方便快捷。

2、是不是以讹传讹?

MySQL 官网文档:

NULL columns require additional space in the rowto record whether their values are NULL. For MyISAM tables, each NULL columntakes one bit extra, rounded up to the nearest byte.

Mysql难以优化引用可空列查询,它会使索引、索引统计和值更加复杂。可空列需要更多的存储空间,还需要mysql内部进行特殊处理。可空列被索引后,每条记录都需要一个额外的字节,还能导致MYisam 中固定大小的索引变成可变大小的索引。

—— 出自《高性能mysql第二版》

照此分析,还真不是以讹传讹,这是有理论依据和出处的。

3、给我一个不用 Null 的理由?

  • (1)所有使用NULL值的情况,都可以通过一个有意义的值的表示,这样有利于代码的可读性和可维护性,并能从约束上增强业务数据的规范性。
  • (2)NULL值到非NULL的更新无法做到原地更新,更容易发生索引分裂,从而影响性能。

注意:但把NULL列改为NOT NULL带来的性能提示很小,除非确定它带来了问题,否则不要把它当成优先的优化措施,最重要的是使用的列的类型的适当性。

  • (3)NULL值在timestamp类型下容易出问题,特别是没有启用参数explicit_defaults_for_timestamp
  • (4)NOT IN、!= 等负向条件查询在有 NULL 值的情况下返回永远为空结果,查询容易出错

create table table_2 (
	 `id` INT (11) NOT NULL,
	user_name varchar(20) NOT NULL
)

create table table_3 (
	 `id` INT (11) NOT NULL,
	user_name varchar(20)
)

insert into table_2 values (4,"zhaoliu_2_1"),(2,"lisi_2_1"),(3,"wangmazi_2_1"),(1,"zhangsan_2"),(2,"lisi_2_2"),(4,"zhaoliu_2_2"),(3,"wangmazi_2_2")

insert into table_3 values (1,"zhaoliu_2_1"),(2, null)

-- 1、NOT IN子查询在有NULL值的情况下返回永远为空结果,查询容易出错
select user_name from table_2 where user_name not in (select user_name from table_3 where id!=1)

mysql [email protected]10.48.186.32:t_test_zz5431> select user_name from table_2 where user_name not
                                    -> in (select user_name from table_3 where id!=1);
+-------------+
| user_name   |
|-------------|
+-------------+
0 rows in set
Time: 0.008s
mysql [email protected]10.48.186.32:t_test_zz5431>

-- 2、单列索引不存null值,复合索引不存全为null的值,如果列允许为null,可能会得到“不符合预期”的结果集
-- 如果name允许为null,索引不存储null值,结果集中不会包含这些记录。所以,请使用not null约束以及默认值。
select * from table_3 where name != ‘zhaoliu_2_1‘

-- 3、如果在两个字段进行拼接:比如题号+分数,首先要各字段进行非null判断,否则只要任意一个字段为空都会造成拼接的结果为null。
select CONCAT("1",null) from dual; -- 执行结果为null。

-- 4、如果有 Null column 存在的情况下,count(Null column)需要格外注意,null 值不会参与统计。
mysql [email protected]:t_test_zz5431> select * from table_3;
+------+-------------+
|   id | user_name   |
|------+-------------|
|    1 | zhaoliu_2_1 |
|    2 | <null>      |
|   21 | zhaoliu_2_1 |
|   22 | <null>      |
+------+-------------+
4 rows in set
Time: 0.007s
mysql [email protected]10.48.186.32:t_test_zz5431> select count(user_name) from table_3;
+--------------------+
|   count(user_name) |
|--------------------|
|                  2 |
+--------------------+
1 row in set
Time: 0.007s

-- 5、注意 Null 字段的判断方式, = null 将会得到错误的结果。
mysql [email protected]:cygwin> create index IDX_test on table_3 (user_name);
Query OK, 0 rows affected
Time: 0.040s
mysql [email protected]:cygwin>  select * from table_3 where user_name is null\G
***************************[ 1. row ]***************************
id        | 2
user_name | None

1 row in set
Time: 0.002s
mysql [email protected]:cygwin> select * from table_3 where user_name = null\G

0 rows in set
Time: 0.002s
mysql [email protected]:cygwin> desc select * from table_3 where user_name = ‘zhaoliu_2_1‘\G
***************************[ 1. row ]***************************
id            | 1
select_type   | SIMPLE
table         | table_3
type          | ref
possible_keys | IDX_test
key           | IDX_test
key_len       | 23
ref           | const
rows          | 1
Extra         | Using where

1 row in set
Time: 0.006s
mysql [email protected]:cygwin> desc select * from table_3 where user_name = null\G
***************************[ 1. row ]***************************
id            | 1
select_type   | SIMPLE
table         | None
type          | None
possible_keys | None
key           | None
key_len       | None
ref           | None
rows          | None
Extra         | Impossible WHERE noticed after reading const tables

1 row in set
Time: 0.002s
mysql [email protected]:cygwin> desc select * from table_3 where user_name is null\G
***************************[ 1. row ]***************************
id            | 1
select_type   | SIMPLE
table         | table_3
type          | ref
possible_keys | IDX_test
key           | IDX_test
key_len       | 23
ref           | const
rows          | 1
Extra         | Using where

1 row in set
Time: 0.002s
mysql [email protected]:cygwin>

  • (5)Null 列需要更多的存储空间:需要一个额外字节作为判断是否为 NULL 的标志位

alter table table_3 add index idx_user_name (user_name);
alter table table_2 add index idx_user_name (user_name);
explain select * from table_2 where user_name=‘zhaoliu_2_1‘;
explain select * from table_3 where user_name=‘zhaoliu_2_1‘;

可以看到同样的 varchar(20) 长度,table_2 要比 table_3 索引长度大,这是因为:

两张表的字符集不一样,且字段一个为 NULL 一个非 NULL。

key_len 的计算规则和三个因素有关:数据类型、字符编码、是否为 NULL

key_len 62 == 20*3(utf8 3字节) + 2 (存储 varchar 变长字符长度 2字节,定长字段无需额外的字节)

key_len 83 == 20*4(utf8mb4 4字节) +  1 (是否为 Null 的标识) + 2 (存储 varchar 变长字符长度 2字节,定长字段无需额外的字节)

所以说索引字段最好不要为NULL,因为NULL会使索引、索引统计和值更加复杂,并且需要额外一个字节的存储空间。基于以上这些理由和原因,我想咱们不用 Null 的理由应该是够了 :)

转自:https://my.oschina.net/leejun2005/blog/1342985

原文地址:https://www.cnblogs.com/itplay/p/11401497.html

时间: 2024-11-08 09:03:06

【优化】Mysql字段尽可能用NOT NULL的相关文章

mysql TIMESTAMP 设置为可NULL字段

今天遇到问题是mysql新建表的时候TIMESTAMP 类型的字段 默认是NOT NULL 然后上网查了一下 发现 很多都说 就是不能为NULL的 这都什么心态 其实设置为空很简单 只要在字段后面加上 NULL 就行了 eg. CREATE TABLE `TestTable` ( Column1 INT NOT NULL COMMENT 'Column1' , Column2 TIMESTAMP NULL COMMENT 'Column2' , PRIMARY KEY (Column1) ) C

mysql oder by排序把null的字段放在最后面

默认情况下,MySQL将null算作最小值. 使用:if(isnull(字段名),1,0) 说明:它的意思是将该字段根据是否为null值分成两部分,0相当于null值部分放在最后面,1相当于非null值部分放在前面,在排序的时候,先根据这一属性进行升序排列 select city_code, ins_code, organ_type, organ_name, organ_address, organ_access_url, state, longitude, latitude, area, or

优化MYSQL数据库的方法

1.选取最适用的字段属性 尽可能减少定义字段长度,尽量把字段设置NOT NULL,例如'省份,性别',最好设置为ENUM 2.使用连接(JOIN)来代替子查询:  a.删除没有任何订单客户 ELETE FROM customerinfo WHERE customerid NOT in(SELECT customerid FROM orderinfo) b.提取所有没有订单客户 SELECT FROM customerinfo WHERE customerid NOT in(SELECT cust

MYSQL之性能优化 ----MySQL性能优化必备25条

今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数据库的性能,这并不只是DBA才需要担心的事,而这更是我 们程序员需要去关注的事情.当我们去设计数据库表结构,对操作数据库时(尤其是查表时的SQL语句),我们都需要注意数据操作的性能.这里,我们不会讲过 多的SQL语句的优化,而只是针对MySQL这一Web应用最多的数据库.希望下面的这些优化技巧对你有用. 1. 为查询缓存优化你的查询 大多数的MySQL服务器都开启了查询缓存.这是提高性最有效的方法之一,而且这是被

mysql的MyISAM 和 InnoDB 的区别?优化MYSQL数据库的方法?

MyISAM 和 InnoDB 的基本区别 1.InnoDB不支持FULLTEXT类型的索引. 2.InnoDB 中不保存表的具体行数,也就是说,执行select count(*) from table时,InnoDB要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可.注意的是,当count(*)语句包含 where条件时,两种表的操作是一样的. 3.对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他

优化MySQL数据库性能的八大方法

本文探讨了提高MySQL 数据库性能的思路,并从8个方面给出了具体的解决方法. 1.选取最适用的字段属性 MySQL可以很好的支持大数据量的存取,但是一般说来,数据库中的表越小,在它上面执行的查询也就会越快.因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小.例如,在定义邮政编码这个字段时,如果将其设置为CHAR(255),显然给数据库增加了不必要的空间,甚至使用VARCHAR这种类型也是多余的,因为CHAR(6)就可以很好的完成任务了.同样的,如果可以的话,我们应该

2-16 MySQL字段约束-索引-外键

一:字段修饰符 1:null和not null修饰符 我们通过这个例子来看看 mysql> create table worker(id int not null,name varchar(8) not null,pass varchar(20) not null); mysql> insert into worker values(1,'HA','123456'); mysql> insert into worker values(1,'LB',null); ERROR 1048 (2

如何优化Mysql数据库

1.添加主键ID 2.尽量避免使用select * form table 3.创建索引     对于查询占主要的应用来说,索引显得尤为重要.很多时候性能问题很简单的就是因为我们忘了添加索引而造成的,或者说没有添加更为有效的索引导致.如果不加索引的话,那么查找任何哪怕只是 一条特定的数据都会进行一次全表扫描,如果一张表的数据量很大而符合条件的结果又很少,那么不加索引会引起致命的性能下降.但是也不是什么情况都非得建索引不可,比如性别可能就只有两个 值,建索引不仅没什么优势,还会影响到更新速度,这被称

MySQL优化-MySQL体系结构

MySQL优化-MySQL体系结构 三层体系结构: 连接层 SQL层 存储层 关于timeout 通过jdbc等程序连接的是非交互会话. 通过mysql cli客户端连接的是交互会话. wait_timeout,关闭非交互连接(程序端)之前等待的秒数.默认8h. interactive_timeout,关闭交互式连接(客户端)前等待的秒数.默认8h. 本节小结: 5.7开始,支持密码过期机制. 8.0开始,支持更多密码安全机制,更安全的MySQL 8.0之全新密码策略. 一些LB应用会疯狂探测数