【MySQL】Schema与数据类型优化
目录
选择优化的数据类型
整数类型
实数类型
字符串类型
日期和时间类型
位置据类型
选择标识符
特殊类型数据
MySQL schema 设计中的陷阱
范式和反范式
缓存表和汇总表
加快 ALTER TABLE 操作的速度
快速创建 MyISAM 索引
选择优化的数据类型
不管存储哪种数据类型,下面几个简单的原则都有助于做出更好的选择。
更好的通常更小
尽量使用可以正确存储数据的最小数据类型,但是要确保没有低估需要存储的值的范围,因为在schema中的多个地方增加数据类型的范围是一个非常耗时和痛苦的操作
简单就好
简单数据类型的操作通常需要更少的CPU周期
尽量避免NULL
列最好指定为 NOT NULL,除非确实可以是 NULL。如果查询中包含可为 NULL 的列,会使索引、索引统计和值比较都更复杂。当可为NULL的列被索引时,每个索引记录需要一个额外的字节,在MyISAM里甚至还可能导致固定大小的索引变成可变大小的索引。
把 NULL 列改为 NOT NULL 列带来的性能提升比较小,但是用于索引的列最好是 NOT NULL。另外,InnoDB 使用单独的位(bit)存储 NULL 值,包含很多 NULL 值的稀疏数据的空间效率比较高。
整数类型
可用的整型数据有:
TINYINT:8 bit
SMALLINT:16 bit
MEDIUMINT:24 bit
INT:32 bit
BIGINT:64 bit
所有的整数类型都支持 UNSIGNED 属性,表示无符号数据,例如 TINYINT UNSIGNED。有符号和无符号整型数据存储空间一样,性能一样。
MySQL 中可以为整数类型指定宽度,例如 INT(11),这个宽度只是规定了 MySQL 交互工具用来显示字符的个数,不影响存储和计算。
实数类型
可用的实数数据有:
浮点类型:
FLOAT:32 bit。不精确类型,支持浮点运算。
DOUBLE:64 bit。不精确类型,支持浮点运算。
DECIMAL:最多允许 65 个数字,例如 DECIMAL(33,32),DECIMAL(65,0)。可以存储精确的小数,也可以存储比 BIGINT 还大的整数,支持精确计算。因为 CPU 不支持对 DECIMAL 的直接计算,所以 MySQL 服务器自身实现了 DECIMAL 的高精度计算。
MySQL 支持精确类型(DECIMAL),也支持不精确类型(浮点类型,即 FLOAT 和 DOUBLE)。
FLOAT、DOUBLE 和 DECIMAL 类型都可以指定精度。例如 DOUBLE(12,3) 表示最多 9 位整数、3 位小数。DECIMAL(18,9) 表示小数点两边各存储 9 个数字,一共使用 9 个字节(小数点占一个字节)。
DECIMAL 需要的存储空间大,计算开销高,如果数据量很大,可以考虑用 BIGINT 替代 DECIMAL。例如金额需要保证 0.001 的精度,则可以将所有金额乘以 1000 后取整存入 BIGINT 类型的字段,从而避免浮点数计算不准确和 DECIMAL 精确计算代价高的问题。
字符串类型
VARCHAR 和 CHAR 类型
存储引擎存储CHAR或者VARCHAR值的方式在内存中和磁盘上可能不一样,所以MySQL服务器从存储引擎读出的值可能需要转换为另一种存储格式。
VARCHAR
VARCHAR类型用于存储可变长字符串,比定长类型更省空间,因为它仅使用必要的空间,有一种情况例外,如果MySQL表使用ROW_FORMAT=FIXED创建,每一行都会使用定长存储,这会浪费空间。
VARCHAR需要使用1或者2个额外字节记录字符串的长度,如果列的最大长度小于或等于255字节,则只使用一个字节表示,否则使用两个字节。
在执行 UPDATA 操作时,如果需要增大行的长度,需要额外工作。如果磁盘当前页的空间不够,不同存储引擎的处理方式不同。MyISAM 会将行拆成不同的片段存储,InnoDB 则需要分裂页来使行可以放进页内。InnoDB 会把过长的 VARCHAR 存储为 BLOB。
CHAR
CHAR是定长的,MySQL总是根据定义的字符串长度分配足够的空间。当存储CHAR值时,MySQL会删除所有的末尾空格。(VARCHAR不会)
VARBINARY
二进制变长字符串,存储字节码而不是字符。
BINARY
二进制定长字符串,存储字节码而不是字符,使用 \0(零字节)填充而不是空格,且检索时不会去掉填充值。
适合使用 VARCHAR 的场景:
字符串列的最大长度比平均长度大得多
列很少更新,所以碎片不是问题
使用 UTF-8 字符集,每个字符用不同的字节数进行存储
适合使用 CHAR 的场景:
短字符串
定长字符串,例如密码的 HASH 值
经常变更的字符串,不易产生碎片
BLOB 和 TEXT 类型
BLOB:二进制方式存储大数据的字符串类型,没有字符集和排序规则。具体的类型有:TINYBLOB, SMALLBLOB, BLOB, MIDIUMBLOB, LONGBLOB
TEXT:字符方式存储大数据的字符串类型,有字符集和排序规则。具体的类型有:TINYTEXT, SMALLTEXT, TEXT, MIDIUMTEXT, LONGTEXT
在 MySQL中,每个BLOB 和TEXT类型的值都是独立对象。当值太大时,InnoDB还会使用专门的外部存储区域来存储,此时行内只需 1-4 个字节存储指针。
MySQL只对每个列的最前max_sort_length字节而不是整个字符串做排序,MySQL不能将BLOB和TEXT列全部长度的字符串进行索引,也不能使用这些索引消除排序。
ENUM 枚举类型代替字符串
有时可以使用枚举列代替常用的字符串类型,MySQL在内部会将每个值在列标中的位置保存为整数,并且在表的.frm文件中保存,”数字-字符串”映射关系的“查找表“。
例:
mysql> CREATE TABLE enum_test(
e ENUM('fish', 'apple', 'dog') NOT NULL
);
mysql>INSERT INTO enum_test(e) VALUES('fish'), ('dog'), ('apple');
MariaDB [foo]> SELECT e + 0 FROM enum_test;
+-------+
| e + 0 |
+-------+
| 1 |
| 3 |
| 2 |
MariaDB [foo]> SELECT e FROM enum_test;
+-------+
| e |
+-------+
| fish |
| apple |
| dog |
+-------+
枚举字段是根据内部存储的整数而不是定义的字符串进行排序的
可以使用 FIELD() 函数显式指定排序顺序,但会导致 MySQL 无法利用索引消除排序
枚举的缺点,字符串列标固定,添加或删除字符串必须使用ALTER TABLE,因此,对于一系列未来可能会改变的字符串。
日期和时间类型
DATETIME:时间范围 1001 - 9999 年,精度范围 1 秒。内部把日期和时间封装到格式为 YYYYMMDDHHMMSS 的整数中,与时区无关,使用 8 个字节的存储空间。MySQL 默认以可排序、无歧义的格式显示 DATETIME 的值,例如 “2018-04-26 16:51:05”。
TIMESTAMP:TIMESTAMP 显示的值依赖于时区。时间范围 1970 - 2038 年,保存从 1970 年 1 月 1 日凌时开始的秒数,跟 UNIX 的时间戳一样。只使用 4 个字节的存储空间。
如果跨时区访问或存储数据,TIMESTAMP 和 DATETIME 的行为完全不一样。
常见的特殊属性有:
- 创建表时,所有的 TIMESTAMP 字段默认为 NOT NULL,且第一个 TIMESTAMP 字段有默认值 CURRENT_TIMESTAMP。
- INSERT 插入数据时,如果没有指定第一个 TIMESTAMP 列的值,MySQL 会设置这个列的值为当前时间。
- UPDATE 更新数据时,MySQL 会默认更新第一个 TIMESTAMP 列的值为当前时间(除非明确指定值)。
MySQL 内置函数可以实现时间戳和日期的互相转换:
FROM_UNIXTIME():将UNIX 时间戳转为日期。
UNIX_TIMESTAMP():将日期转为 UNIX 时间戳。
位置据类型
BIT(不建议用)
BIT 列中可以存储一个或多个 true/false 值。BIT(n) 定义了存储 n 个位的字段,BIT 列最大支持 64 个位。
BIT 的行为因存储引擎的不同而不同。MyISAM 会打包存储所有的 BIT 列,17 个单独的 BIT 列只需要 17 个位存储(假设没有可为 NULL 的列),只需要 3 个字节。假设存储引擎是 Memory 或 InnoDB,每个 BIT 列使用一个足够存储的最小整数来存放,无法节省存储空间。
MySQL 把 BIT 当做字符串类型,而不是数字类型。当检索 BIT(1) 的值时,结果是一个包含二进制 0 或 1 的字符串,而不是 ASCII 码的 0 或 1。但是在数字上下文的场景中,会自动将位字符转为对应的 ASCII 码。例如,对于存储了 b'00111001' 的 BIT(8) 列,正常检索时得到字符码为 57 的字符‘9’,但是在数字上下文场景中,得到数字 57。
SET
如果需要保存很多 true/false 值,可以合并这些列到一个 SET 数据类型,在 MySQL 内部以一系列打包的位的集合来表示。存储空间利用率高,使用方便(可以在查询中使用 FIELD() 和 FIELD() 函数)。缺点是改变列的定义时,代价较高(需要使用 ALTER TABLE),对于大表很麻烦。
选择标识符
标识列(identifier column)选择合适的数据类型很重要。标识列可以与其他值进行比较,或通过标识列寻找其他列。标识列也可以作为其他表中的外键。
选择标识列的类型时,既要考虑存储类型,也要考虑 MySQL 对这种类型怎么执行计算和比较。例如 MySQL 内部用整数存储 ENUM 和 SET 类型,在比较操作时转为字符串。
整数类型
整数是标识列最佳选择,快,且可以使用 AUTO_INCREMENT
ENUM 和 SET 类型
标识列的糟糕选择。ENUM 和 SET 列适合存储固定信息,例如性别、产品类型。
字符串类型
尽量避免,慢,存储空间大。尤其是 MyISAM,会对字符串压缩索引。
特殊类型数据
例如IPv4,人们经常用VARCHAR(15)列来存储IP地址,然而,它们实际上是32位无符号整数,不是字符串,用小数点将地址分成四段的表示方法只是为了让人阅读容易,所以应该用无符号整数存储IP地址。MySQL 提供了 INET_ATON() 和 INET_NTOA() 函数进行转换。
MySQL schema 设计中的陷阱
太多的列
MySQL的存储引擎API工作时需要在服务器层和存储引擎层之间通过行缓冲格式拷贝数据,然后在服务器层将缓冲内容解码成各个列。从行缓冲中将编码过的列转换成行数据结构的操作代价是非常高的。
太多的关联
如果希望查询执行得快速且并发性好,单个查询最好在12个表以内做关联
全能得枚举
当需要在枚举列表中增加一个新的值时,就要做一次ALTER TABLE操作,在MySQL5.0以及更早的版本中ALTER TABLE是一种阻塞操作,在后续版本中,如果不是在列表的末尾增加值也会一样需要ALTER TABLE。
变相的枚举
枚举(ENUM)列允许在列中存储一组定义值中的单个值,集合(SET)列则允许在列中存储一组定义值中的一个或多个值,因此使用时不要混乱。
范式和反范式
范式化数据库中,每个事实数据会出现且只出现一次。反范式化数据库中,信息是冗余的,可能存储在多处。
范式化的优点:
更新操作快
较好的范式化可以减少数据冗余,修改简单
范式化好的表更新
冗余少的数据可以更少的使用 DISTINCT 或 GROUP BY 语句
范式化设计的 schema 的缺点:
是通常需要关联查询。
反范式化的优点:
所有数据都在一张表中,避免了关联。
最常见的混用范式化和反范式化的例子是复制或缓存,在不同的表中存储相同的特定列。可以通过触发器更新冗余列。
缓存表和汇总表
缓存表,表示存储那些可以比较简单从schema其他表获取数据的表
汇总表,保存的是使用GROUP BY语句聚合数据的表
完全独立的缓存表和汇总表,可以存储少量冗余数据。
缓存表是实时维护的。
汇总表是定期重建的。
可以使用物化视图(MySQL 需要借助工具 Flexviews)和计数器表。
加快 ALTER TABLE 操作的速度
MySQL 执行 ALTER TABLE 时,通常是用新的结构创建一个空表,从旧表中查出所有数据插入新表,再删除旧表。
大部分 ALTER TABLE 操作会中断 MySQL 服务。有几种方式可以避免停机:
- 主备结构时,可以在不提供服务的备用库执行操作,完成后再主备切换。
- 影子拷贝:用新的表结构创建新表,然后通过重命名和删表操作交换两张表。可以使用 Facebook 的“online schema change”工具。
改变或删除列的默认值时,可以使用 ALTER COLUMN(不一定重建表)或 MODIFY COLUMN(需要重建表)。列的默认值存储在表的 .frm 文件中。
只修改.frm文件
下面的操作是有可能不需要重建表的:
移除(不是增加)一个列的AUTO_INCREMENT属性
增加、移除或更改ENUM和SET常量。如果移除的是已经有行数据用到其值的常量,查询将会返回一个空字串值
基本技术是为想要的表创建一个新的.frm文件,然后用它替换掉已经存在的那张表的.frm文件
- 创建一张有相同结构的空表,并进行所需要的修改(例如增加ENUM常量)
- 执行FLUSH TABLES WITH READ LOCK。这将会关闭所有正在使用的表,并且禁止任何表被打开。
- 交换.frm文件
- 执行UNLOCK TABLES来释放第2步的读锁
快速创建 MyISAM 索引
ALTER TABLE 时,先禁用索引,载入数据后再启用索引,可以高效载入数据。
这个方法对唯一索引无效,因为DISABLE KEYS只对非唯一索引有效
操作步骤
- 用需要的表结构创建一张表,但不包括索引
- 载入数据到表中以创建.MYD文件
- 按照需要的结构创建另外一张空表,这次要包含索引。这会创建需要的.frm和.MYI文件。
- 获取读锁并刷新表。
- 重命名第二张表的.frm和.MYI文件,让MySQL认为是第一张表的文件。
- 释放读锁
- 使用REPAIR TABLE来重建表的索引。该操作会通过排序来构建所有索引,包括唯一索引。
【MySQL】Schema与数据类型优化相关推荐
- MySQL Schema与数据类型的优化
选择优化的数据类型: 1. 更小的通常更好: 一般情况下,应该尽量使用可以正确存储数据的最小数据类型.更小的数据类型通常更快,因为他们占用更少的磁盘,内存和cpu缓存,并且处理时需要的cpu周期也更少 ...
- mysql schema数据混乱_MySQL之Schema与数据类型优化
选择优化的数据类型 MySQL支持的数据类型非常多,选择正确的数据类型对于获得高性能至关重要.不管存储哪种类型的数据,下面几个简单的原则都有助于做出更好的选择: 更小的通常更好 一般情况下,应该尽量使 ...
- mysql eva模型_第四章 Schema 与数据类型优化
1.选择优化的数据类型 更小的通常更好 一般情况下选择可以正确存储数据的最小数据类型.更小的类型通常更快,占用更少的硬盘.内存.CPU等.但要确保没有递归需要存储的值的范围. 简单就好 简单数据类型的 ...
- Mysql高性能笔记(一):Schema与数据类型优化
1.数据类型 1.1.几个参考优化原则 a. 更小的通常更好 i.更小的数据类型,占用更少磁盘.内存和CPU缓存,需要的CPU周期更少 ii.如果无法确定哪个数据类型是最好的,就选择不会超过范围的最 ...
- 高性能MySQL(2)——Schema与数据类型的优化
良好的逻辑设计和物理设计是高性能的基石,应该根据系统将要执行的查询语句来设计 schema,这往往需要权衡各种因素. 一.选择优化的数据类型 MySQL支持的数据类型非常多,选择正确的数据类型对于获得 ...
- MySQL高性能及性能优化技巧---更适合开发人员
更新次数 更新时间 首发 2021.10.25 第一次更新 2021.10.26 1.删除了书中大量不必要的存储引擎类型 2.摘要完毕Mysql架构与历史部分 第二次更新 2021.10.29 1.摘 ...
- MySQL查询过程及Scheme设计与数据类型优化
MySQL查询过程 我们总是希望MySQL能够获得更高的查询性能,最好的办法是弄清楚MySQL是如何优化和执行查询的.一旦理解了这一点,就会发现:很多的查询优化工作实际上就是遵循一些原则让MySQL的 ...
- mysql 数值 字符 优化,教你如何进行Mysql数据类型优化
1. 版本 1)操作系统版本 cat /proc/version Linux version 3.10.0-957.5.1.el7.x86_64 (mockbuild@kbuilder.bsys.ce ...
- mysql表设计很多非常大的varchar_聊一聊数据库(MySQL)设计中的数据类型优化
良好的逻辑设计和物理设计事高性能的基石,在进行数据库设计时,我们应该要考虑到未来将会执行的查询语句,这就需要对各种因素进行权衡.本文将会聊一聊数据库(MySQL)设计中有关数据类型优化的一些内容.以下 ...
最新文章
- Linux的10个游戏
- 最新!压缩为rar格式方法,目前只能用:WinRAR压缩工具-rar压缩格式的版权所有者。
- ServerBootstrap的启动流程
- boost::hana::second用法的测试程序
- LeetCode 424. Longest Repeating Character Replacement
- Redis PK Memcached,哪个更牛叉
- Java BigDecimal stripTrailingZeros()方法与示例
- 北大保安又双叒…出“传奇”!这次是挑战“天下第一考”
- 不用数学讲清马尔可夫链蒙特卡洛方法?
- 容器编排技术 -- Kubernetes kubectl edit 命令详解
- java开发者工具开源版_6种开源工具可帮助教育工作者保持井井有条
- 怎样进行大数据的入门级学习
- c# mysql 汉字乱码_在C#和MySQL中存取中文字符时避免乱码的方法
- 重学JavaScript系列之一_引用类型
- 【论文解读】“推荐系统”加上“图神经网络”
- JFinal开发8个常见问题
- ulipad.4.1.zip linux,在ubuntu中安装ulipad
- 使用面包板的一点小注意
- 解决vue项目背景图片在IE和火狐上不显示的问题
- HCIP-Routing Switching V2.5考试大纲