NULL影响mysql性能是‘优化军规’还是‘中医理论’?用测试数据说话,顺便玩玩golang

wind5o · · 3429 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

最近公司规范数据库方面的一些东西,老程序员告诉我mysql里不准使用null,说是含null值的列没法索引,对性能影响很大。我第一次听说这个,对此将信将疑,之前用null并索引并没有感觉到不妥,可能是数据量小吧。于是我去google了一顿,但是并没有找到权威的说法。

V2EX——关于null的索引不生效的传说是真的么?

by shiny:
由于数据库的复杂性,以讹传讹的空间非常大,快赶上中医养生了。避免使用 NULL 的理由,在高性能MySQL里有提到一段。建议大家多读些书,少看网上的奇技淫巧。特意把书翻出来摘录了下以供参考:
要尽量避免 NULL
要尽可能地把字段定义为 NOT NULL。即使应用程序无须保存 NULL(没有值),也有许多表包含了可空列(Nullable Column),这仅仅是因为它为默认选项。除非真的要保存 NULL,否则就把列定义为 NOT NULL。
MySQL难以优化引用了可空列的查询,它会使索引、索引统计和值更加复杂。可空列需要更多的储存空间,还需要在MySQL内部进行特殊处理。当可空列被索引的时候,每条记录都需要一个额外的字节,还可能导致 MyISAM 中固定大小的索引(例如一个整数列上的索引)变成可变大小的索引。
即使要在表中储存「没有值」的字段,还是有可能不使用 NULL 的。考虑使用 0、特殊值或空字符串来代替它。
把 NULL 列改为 NOT NULL 带来的性能提升很小,所以除非确定它引入了问题,否则就不要把它当作优先的优化措施。然后,如果计划对列进行索引,就要尽量避免把它设置为可空。

stackoverflow——NULL in MySQL (Performance & Storage)

by Arian Acosta:
Advantage of using NULLS over Empty Strings or Zeros:
1 NULL requires 1 byte
1 Empty String requires 1 byte (assuming VARCHAR)
1 Zero requires 4 bytes (assuming INT)
You start to see the savings here:
8 NULLs require 1 byte
8 Empty Strings require 8 bytes
8 Zeros require 32 bytes
On the other hand, I suggest using NULLs over empty strings or zeros, because they're more organized, portable, and require less space. To improve performance and save space, focus on using the proper data types, indexes, and queries instead of weird tricks.

中英文用户们对此都是争论不休,在群里问了一下老前辈,前辈表示mysql这个东西很玄乎,还是自己写性能测是最靠谱,说的好那么开始写吧。

测试环境

  • mysql 5.7
  • golang 1.9.2
  • mac os 10.13.1

测试思路

  • 建两张类似的表,有id,name,number三列,给name建索引,两张表区别是一个允许null一个not_null
  • 随机生成若干条数据,包含一些name为空的数据
  • 将相同的数据插入两个表中,区别空值是一个用null一个用‘’
  • 测试SELECT * FROM table WHERE NAME = ? 的性能

测试代码

https://github.com/win5do/pla...

进入文件所在目录运行:go test -v -run=none -bench=. -benchmem

测试结果

mock100w条数据的结果,null完全没影响

储存空间占用也是相同的,null占空间 ‘’同样占空间

写测试代码碰到的坑

为什么要用golang,因为最近在学习golang,通过写代码来加深对语言的理解。而且golang自带测试工具,做性能测试非常方便。

第一个坑,以前没用过golang连接数据库,写create语句的时候一直提示syntax error,仔细检查了好几遍,没有错误啊。后来翻了翻文档发现连接是加上multiStatements=true参数才能使用多行sql。

第二个坑就是insert语句拼接的太长会导致write pipe broken,mock50000行时没问题,但100000行就报错了,解决办法就是分块插入,10000行插一下。

第三个坑,就是query之后是要close的

结论

当前稳定版本mysql5.7之中,null对索引没有影响(比较懒,5.5没测试,不想去安装卸载mysql,有空补上)。不要一棒子把null打死,该用就用,null的语义化很好,配合orm使用基本无痛。比如时间列,不用null用个0000-00-00不要太蛋痛。

那句老话:

过早的优化是万恶之源

仅凭中医理论优化更是无可救药

更重要的,用golang写代码真的很cool


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

3429 次点击  
加入收藏 微博
4 回复  |  直到 2018-01-08 14:26:39
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传