mysql访问类型最好的_【干货满满】最全的MySQL性能指南(一):选择最佳的数据类型...

对于 MySQL 数据库来说,好的逻辑表和物理表的规划至关重要,我们需要根据查询语句来针对性地设计 Schema ,没有万能好用的 Schema。一个 denormalized 的 schema 可以在某些场景下加速语句查询,但是放在其他应用场景下就会适得其反。增加辅助表可以优化查询,但是表的维护成本就很高。MySQL 的实现原理决定了这一事实。

《最全的MySQL性能指南》系列文章重点讨论数据库索引和 MySQL 的 schema 设计。在读之前,读者应该熟悉数据库设计基本知识。在系列文章中,我们会讲到逻辑设计,物理级别的设计,和语句执行。

MySQL 支持多种数据类型,存储数据类型的选择对性能至关重要,下面的一些基本原则可以帮助你在不同场景下选择合适的数据类型:

字段越小越好

一般情况下,选择可以存储你的数据的最小数据类型,越小越快,因为占用更少的磁盘空间,更少的内存,和 CPU 缓存。小的数据类型也只需要较少的 CPU 周期来处理。

简单即更优

处理简单的数据类型通常耗费更少的 CPU 周期。举个例子,整数比较比字母比较更快,将字母用数字表示更好。而且我们不应该用字符串,而是应该用 MySQL 原生的日期和时间类型来存储时间数据,用整数来存储 IP 地址。

尽可能避免 NULL

很多表都支持带 null 值的字段,即便是应用软件没有 null 这么个概念,也还是会在表里加入这个一个值做为默认值。最好的方式是每一个字段都标注 NOT NULL,除非应用确实有 NULL 的概念。MySQL 对涉及 null 字段的查询无法做到最佳优化,因为 null 值本身让索引和数值比较更加复杂。带 null 值的字段消耗更多的存储空间,而且需要一些特殊的处理。当带 null 值的字段被加了索引的时候,每一个字段值都会多出一个字节,在 MyISAM 存储引擎中还有可能将固定大小的索引变成随机大小的索引。

把一个带 null 的字段修改成 NOT NULL 的字段带来的性能提升不大,所以如果不是必须的话,请不要在建表之后去修改,但是如果要在字段上加索隐,最好还是用 NOT NULL。

也有例外的情况。比如 InnoDB 用一个比特来存储 NULL,所以对于稀疏表,是很节省空间的。但是 MyISAM 就不是这样。

确定字段类型的第一步,就是了解数据的基本特征:数值型,字符串型,时间型,等等。这个很好确定,但是也有上文提到的特殊情况。

下一步就是确定具体的数据类型。很多 MySQL 的数据类型可以存储相同的数据,但是对数据的范围要求、精度、存储格式却不一样。某些数据格式有特殊的性质。

比如说,DATETIME 和 TIMESTAMP 字段可以存储相同的数据 - 日期和时间,精确到秒。但是 TIMESTAMP 只需要 DATETIME 一般的存储空间,能够存储时区信息,并且还支持自动数据生成。但是,TIMESTAMP 能表示的时间范围就小很多,而且其自动数值生成功能有时候会产生不要的效果

整数

数字有两种类型:整数和实数(带小数点位的数字)。如果是存储整数,我们可以可以使用TINYINT (8 bits,能存储

equation?tex=-2%5E%7B7%7D

equation?tex=2%5E%7B7%7D+-+1 范围的整数)

SMALLINT (16 bits,能存储

equation?tex=-2%5E%7B15%7D

equation?tex=2%5E%7B15%7D+-+1 范围的整数)

MEDIUMINT (24 bits,能存储

equation?tex=-2%5E%7B23%7D

equation?tex=2%5E%7B23%7D+-+1 范围的整数)

INT (32 bits,能存储

equation?tex=-2%5E%7B31%7D

equation?tex=2%5E%7B31%7D+-+1 范围的整数)

BITINT (64 bits,能存储

equation?tex=-2%5E%7B63%7D

equation?tex=2%5E%7B63%7D+-+1 范围的整数)

整数类型的可以自带 UNSIGNED 属性,该属性去除了所有负值,将能表示的最大值翻两倍。例如,TINYINT UNSIGNED 可以存储 0 到 255 的数值,而 TINYINT 只能存储 -128 到 127。

Signed 和 unsigned 数据类型占用相同的存储空间,性能效果相同,所以根据你自己的需求随意设置就好。

我们对数据类型的选择决定了 MySQL 存储数据的方式。但是整数相关的计算通常用的是 64位 BITINT 的方式进行,就算在32位处理器中也是如此。

MySQL 可以指定整数的上限位数,比如 INT(11),这其实是一个没啥卵用的功能:这并不会改变整数的范围,仅仅只是制定 MySQL 在 command line 下显示整数所用的占位符的个数。从存储和计算角度上来说,INT(1) 和 INT(20) 是一样的

实数

实数是带有小数点位的数字,但它们不仅仅存储分数性质的数字,你也可以用 DECIMAL 来存储达到连 BIGINT 都存不下的数值。MySQL 支持完全准确和近似值类型的实数

FLOAT 和 DOUBLE 支持用浮点方式的近似计算,计算方式因处理器的不同而稍有差异。

DECIMAL 是用来存储完全准确的实数的。在 MySQL 5.0 以及以后的版本中,DECIMAL 支持完全精确计算。而 MySQL 4.1 和更早的版本则是使用浮点方式来进行 DECIMAL 的运算,这在某些情况下会出现很诡异的计算结果,所以在老版本中,DECIMAL 只是一个 “存储类型”。

在 MySQL 5.0 以及以后的版本中,MySQL server 负责 DECIMAL 的所有计算,因为 CPU 本身不支持 DECIMAL 运算。浮点运算(Floating-point )会快很多,因为 CPU 支持浮点运算。

浮点(floating-point)和 DECIMAL 类型都支持指定精度。对于 DECIMAL 字段,可以指定小数点之前和之后的最大位数,注意,这会影响字段的存储。

MySQL 5.0 以及以后的版本中的 DECIMAL 支持最高带有 65 个数字的数值。

相比于 DECIMAL 浮点类型通常要求较少的存储空间来存储相同的数据。一个 FLOAT 字段占用 4 个字节,DOUBLE 占用 8 个字节并且精度更高。和整数一样,浮点和 DECIMAL 只是存储方式上的区别,计算都是通过 DOUBLE 的计算方式进行的。

因为占用更多的存储空间和计算资源,应该只有在需要精确小数计算的时候你才应该使用 DECIMAL。一个可用的场景是存储金融相关的数据。但是对于大体量数据,更好的方法是用 BIGINT,将货币数据转化成可复原的整数形式。比如你需要存储小数点后两位的货币数据,你可以将原数值乘以一百万,存成 BITINT,这样可以防止因为浮点数据存储和 DECIMAL 运算带来的精度误差。

字符串类型

MySQL 支持相当多的字符串类型,这些类型在 4.1 版本和 5.0 版本中有了很大的变化,也因此变得更加复杂。

MySQL 4.1 开始,每个字符串字段能有不同的字符集和对应的字符集排序规则。

VARCHAR 和 CHAR 类型

两个主要的字符串类型是 VARCHAR 和 CHAR,每个存储引擎对这两者的存储方式都很不一样。我们这里仅讨论 InnoDB 和 MyISAM 两种存储,如果你关注的是其它存储引擎,请参阅对应的文档。

我们来看一下 VARCHAR 和 CHAR 是如何在磁盘上存储的。存储引擎在内存和磁盘上存储 VARCHAR 和 CHAR 的方式可能不一样,不仅如此,当 MySQL 把字符串从存储引擎中读出来的时候还会进行二次转化。以下是两种类型的一般比较:

VARCHAR

VARCHAR 存储的是变量长度的字符串,也是最常见的数据类型。VARCHAR 比定长的字符串可以占用更少的存储空间,因为 VARCHAR 存储短的字符串就只需要更少的空间。但是在 MyISAM 中用 ROW_FORMAT=FIXED 创建出来的表,用的就是占用固定的磁盘空间,这可能会带来存储资源的浪费。

VARCHAR 借助 1 或者 2 个额外的字节来记录字符串的长度。如果字段的最大长度小于等于 255 个字节,就用一个字节,如果大于 255 就用 两个字节。在拉丁语字符集情况下,VARCHAR(10) 最高占用 11 个字节的存储空间,VARCHAR(1000) 占用 1002 个字节,因为额外需要两个字节来存储。

VARCHAR 对性能提升有帮助,因为占用更少的空间。但是,因为是长度可变的,这可能会带来额外的开销。如果长度超过的了最大值,不同的存储引擎会有不同的策略。例如,MyISAM 会将一行数据切分,而 InnoDB 可能会将表分成多个 page,其他引擎可能根本就不会在原数据上做修改。

当最长字符串长度远大于平均字符串长度,字符串更新很少见(因为上文提到的切分就不会出现),并且使用 UTF-8 这样的复杂字符集(每一个字都占用不同字节数量的存储空间)的时候,VARCHAR 是一个不错的选择,。

MySQL 5.0 以及以后的版本中,会保留字符串后面的空格,而在 4.1 以及以前的版本中,MySQL 会将尾部空格剔除。

CHAR

CHAR 是定长的:MySQL 总是按照一定数量划分出足够的空间,存储 CHAR 的时候,MySQL 总是删除尾随空格。CHAR 在相互比较的时候为了长度相同,会在尾部添加空位。

如果存储很短的字符串,或者所有字符串都基本一样长,CHAR 是一个更好的选择。比如说,CHAR 特别适合存储密码的 MD5 值,MD5 值的长度都是相同固定的。对于经常变化的数据,CHAR 也是一个比 VARCHAR 更好的选择,因为定长字符串不会出现分片现象。对于很短的字符串字段,CHAR 有更好的性能;CHAR(1) 用来存储 Y 和 N 在单字节字符集中只需要一个字节,而 VARCHAR(1) 则需要两个字节,其中一个用来记录长度。

CHAR 和 VARCHAR 的姊妹类型是 BINARY 和 VARBINARY,用来存储二进制字符串。二进制字符串和普通字符串类似,只是存的是字节,不是字母。在尾部添加空位也不一样:MySQL 给 BINARY 尾部添位用的是 \0 ,而不是空格,而且不会剔除尾部 \0

当数据是二进制的,而且需要比较的情况下,BINARY 和 VARBINARY 是比较好的选择。二进制比较的优点除了不区分大小写,MySQL 是一个一个字节地比较 BINARY 字符串,比较每个字节的数值,这样做的结果就是二进制比较会更加简单快速。

一般化的处理方式可能会适得其反

存储 “hello” 这个单词对于 VARCHAR(5) 和 VARCHAR(200) 需要的存储空间是一样的,那用 VARCHAR(5) 好在哪里呢?

确实会好很多,VARCHAR(200) 会消耗更多的内存,因为 MySQL 会分配固定的内存空间,这对排序和内存操作带来很大压力。

BLOB 和 TEXT 类型

BLOB 和 TEXT 是用来存储占体很大的数据,分别以二进制和字符的形式存储。

实际上,不止是 BLOB 和 TEXT 这两个,还有 TINYTEXT,SMALL TEXT,TEXT,MEDIUMTEXT,和 LONGTEXT,TINYBLOB,SMALLBLOB,BLOB,MEDIUMBLOB,和 LONGBLOB。 BLOB 等同于 SMALLBLOB, TEXT 等同于 SMALLTEXT。

和其它数据类型不同的是,MySQL 存储 BLOB 和 TEXT的方式有些特殊,InnoDB 会用一个单独的“外部”存储区域,表里实际存的是这个外部区域的位置信息(1 到 4 个字节)。

BLOB 和 TEXT 的唯一区别是 BLOB 类型存储的是二进制数据,没有 collation 和字符集,而 TEXT 则有字符集和 collation。

MySQL 用了和其它类型不同的方法给 BLOB and TEXT 排序:不会给整个字符串排序,而是给前 max_sort_length 数量的字节排序。如果你只想根据前面几个字符排序,要么降低 max_sort_length 要么使用 ORDER BY SUBSTRING(column, length)

MySQL 无法按照这些数据类型的原长建立索引,也不能在排序的时候用到索引。

BLOB 和 TEXT 类型 - 磁盘临时表 & 排序

因为内存存储引擎(memory storage engine)不支持 BLOB 和 TEXT 类型,查询 BLOB 和 TEXT 字段并且需要创建临时表的话的查询语句需要使用 MyISAM 的磁盘临时表(Percona 的内存存储引擎也支持),即使是访问小部分数据也是如此。

这会对性能带来很严重的影响,即便你将 MySQL 的配置设置成在内存存放临时表,也会导致很多的大开销的系统调用。

最好的方法是不到万不得已的情况下不要使用 BLOB 和 TEXT 类型。如果一定需要的话,就在需要使用 BLOB 字段的语句(包括 ORDER BY 语句)中使用 SUBSTRING(column, length),将其转换成字符形式,因为这样就可以使用内存临时表了。这里要确保使用比较短的 substring,保证临时表不会比 max_heap_table_size 或者 tmp_table_size 更大,否则 MySQL 会将表变成磁盘形式的 MyISAM 表。该方法同样适用于排序。

举个例子,假设有一个一千万行的表,占用了几个G的磁盘空间,表中包含一个 VARCHAR(1000),utf8字符集的字段,每个字母最多占用 3 个字节,所以最长的字符串值有 3000 个字节。如果在 ORDER BY 语句中涉及该字段,那么整个表的查询会产生 30 多个 GB 的临时表用来排序!

如果 EXPLAIN 中的 “Extra” 字段包含 “Using temporary”,那么代表着查询语句用到了临时表。

ENUM

有些时候可以用 ENUM 替代字符串类型,ENUM 字段存储某些预设好的字符串值。MySQL 用非常节省空间的方式存储 ENUM,根据预设值的个数,只用一个或者两个字节存储,存储方式是用一个整数当作字段值,这个整数等于所有预设值列表里某个具体字符串的下表,通过一个查找表来维护这个映射。举个例子:

mysql> CREATE TABLE enum_test(

-> e ENUM('fish', 'apple', 'dog') NOT NULL

-> );

mysql> INSERT INTO enum_test(e) VALUES('fish'), ('dog'), ('apple');

三行数据实际上存储的是数字,不是字符串:

mysql> SELECT e + 0 FROM enum_test;

+-------+

| e + 0 |

+-------+

| 1 |

| 3 |

| 2 |

+-------+

如果 ENUM 的真实值是数字,那么这会导致很大的歧义,所以Jack建议不要这么做。

另外,ENUM 是根据对应的整数值排序的,不是按照真实数据:

mysql> SELECT e FROM enum_test ORDER BY e;

+-------+

| e |

+-------+

| fish |

| apple |

| dog |

+-------+

解决办法是,可以在定义 ENUM 成员值的时候,有意按照顺序指定真实值,你也可以使用 FIELD() 来明确指出一个查询语句的相关顺序,但是这样 MySQL 就无法在排序的时候使用索引了:

mysql> SELECT e FROM enum_test ORDER BY FIELD(e, 'apple', 'dog', 'fish');

+-------+

| e |

+-------+

| apple |

| dog |

| fish |

+-------+

像上面这种情况,如果我们定义 ENUM 值的时候就按照字母排好序,我们之后就不需要用 FIELD() 了。

ENUM 一个最大的缺点就是预设值不能随意再添加,如果要添加或者删除需要用到 ALTER TABLE,所以如果预设值在将来可能会变动,ENUM 可能不是一个最佳选择,除非 ALTER TABLE 不会对应用产生大的影响,这在 MySQL 5.1 有比较好的支持。

因为 MySQL 存储的是整数,而且需要将整数映射到具体的预设值,ENUM 字段会有一些额外的开销。这可以通过 ENUM 的小范围预设值来抵消,但不是所有情况都是如此,比如说把 CHAR 或者 VARCHAR 字段和一个 ENUM 字段 join 起来,会比和另一个 CHAR 或者 VARCHAR 字段 join 起来要慢。

为了展示说明,这里通过一个举例来简单看一下 MySQL 执行 join 的速度。我们有一个 primary key 比较“宽”的表:

CREATE TABLE webservicecalls (

day date NOT NULL,

account smallint NOT NULL,

service varchar(10) NOT NULL,

method varchar(50) NOT NULL,

calls int NOT NULL,

items int NOT NULL,

time float NOT NULL,

cost decimal(9,5) NOT NULL,

updated datetime,

PRIMARY KEY (day, account, service, method)

) ENGINE=InnoDB;

这个表有大概11万行数据,大小只有 10 MB,所以整个表可以放入内存。service 字段包含 5 个预设值,平均长度是 4 个字符,method 字段包含 71 个预设值,平均长度是 20 个字符。

我们复制了一份这个表,并且将 service 和 method 字段转成了 ENUM:

CREATE TABLE webservicecalls_enum (

... 省略 ...

service ENUM(...预设值省略...) NOT NULL,

method ENUM(...预设值省略...) NOT NULL,

... 省略 ...

) ENGINE=InnoDB;

然后我们测量了一下根据 primary key 来 join 表的性能:

mysql> SELECT SQL_NO_CACHE COUNT(*)

-> FROM webservicecalls

-> JOIN webservicecalls USING(day, account, service, method);

我们将上述查询语句稍作修改,来相互 join VARCHAR 和 ENUM 字段,一下是测量结果:VARCHAR join VARCHAR:2.6 秒

VARCHAR join ENUM: 1.7 秒

ENUM join VARCHAR: 1.8 秒

ENUM join ENUM: 3.5 秒

将被 join 的字段改成 ENUM 之后,速度提升,但是 ENUM join VARCHAR 慢了一些,所以我们发现如果 VARCHAR 字段不是被 join 的情况,可以使用 ENUM。通常情况下,用 ENUM 这样的整数对照表,比 join 字符要来的高效。

然而,还有一个将 service 和 method 字段转成 ENUM 的好处:根据 SHOW TABLE STATUS 中的 Data_length 字段,转换之后的表缩小了三分之一。在某些时候,存储方面带来的提升可能大过 ENUM join VARCHAR 带来的性能耗损。而且,primary key 也小了一半。在 InnoDB 引擎中,缩小 primary key 也能让索引大大缩小

Date 和 Time 类型

MySQL 针对日期和时间有多种不同的类型。比如 YEAR 和 DATE。MySQL 能存储的最小时间单位是秒(MariaDB 是微秒),但却能在微秒级别上进行计算。

大多数时间相关的数据类型没有替代类型,所以也就不存在哪个更好的说法。唯一的问题就是,如果需要同时存储日期和时间,该怎么办。对此 MySQL 提供两个类似的数据类型:DATETIME 和 TIMESTAMP。对于大多数应用来说,两者都可以,但某些情况下,就有优劣之分了,我们具体来看一下:

DATETIME:

这个类型可以存储大范围的数据,从 1001 年到 9999 年,精度到达秒级,存储日期和时间用一个整数形式表示:YYYYMMDDHHMMSS,无时区,使用 8 个字节的存储空间。

默认情况下,MySQL 显示 DATETIME 的格式是 “2008-01-16 22:37:08”,这是 ANSI 标准下规范的日期时间表示方法

TIMESTAMP:

顾名思义,TIMESTAMP 存储的是从 格林尼治时间(GMT)1970 年 1 月 1 日凌晨过去了多少秒,和 Unix 时间戳是一样的。TIMESTAMP 只需要 4 个字节存储,所以可能表示的时间范围比 DATETIME 小很多:从 1970 年到 2039 年。MySQL 提供 FROM_UNIXTIME() 和 UNIX_TIMESTAMP() 进行 Unix 时间和日期之间的转换。

MySQL 4.1 和之后的版本用和展示 DATETIME 相同的方式展示 TIMESTAMP 的值,但是 MySQL 4.0 和之前的版本在展示的时候没有标点符号,但这仅仅只是展示形式的不同,TIMESTAMP 的存储格式在所有 MySQL 的版本中都是一样的

TIMESTAMP 的展示数值也取决于时区,MySQL,操作系统,和访问客户端都有自己的时区设置。所以,当 TIMESTAMP 存储的是 0 的时候,展示出来的是 1969-12-31 19:00:00 EST,和 GMT 有 5 个小时的时差。这个区别需要注意:如果是在不同时区存储和访问时间数据,TIMESTAMP 和 DATETIME 的应对方式是不同的。前者是带了时区,后者强调日期和时间的展示形式。

TIMESTAMP 也有 DATETIME 不具备的特殊性质。默认情况下,如果没有制定具体的值,MySQL 会把第一个 TIMESTAMP 字段的值设成当前时间

一般情况下,如果可以用 TIMESTAMP 尽量用,因为 TIMESTAMP 比 DATETIME 更节省存储空间。一些人会将 UNIX 时间戳存成整数形式,但是这样并没有什么优势,整数形式比较不和规范,所Jack我建议不要这么做。

那如果需要存储秒级一下的数据呢?比如毫秒。MySQL 目前没有特定的数据格式支持,但是你可以用现有的类型做一些设计:你可以用 BIGINT 存储微秒级的时间戳,或者你可以用 DOUBLE 来存储带小数点的秒级数据。哪种方法都合适,或者可以直接用 MariaDB

Bit-存储数据类型

MySQL 里面的少数存储类型支持用数据中的某些 bit ,用压缩的方式存储数据。所有这些格式是用字符串形式存储

BIT

在 MySQL 5.0 之前,BIT 就是 TINYINT,在是在 MySQL 5.0 和以后的版本中,就是一个完全不同的数据类型

你能用 BIT 字段存储一个或者多个 true/false 值,BIT(1) 定义了一个包含了单个 bit 的值,BIT(2) 包含两个 bit,以此类推,BIT 字段的最大程度是 64 个 bits。

BIT 的性质在不同存储引擎中是不一样的。MyISAM 将所有 bit 字段放在一起,所以 17 个 BIT 字段只需要 17 个 bit 就行(字段值不能出现 NULL), MyISAM 将其转换成 3 个字节存储。其他存储引擎中,例如内存的 InnoDB,将 bit 用最小整数表示,这样会比较消耗存储空间。

MySQL 将 BIT 当作字符串对待,而不是数值。当你读取一个 BIT(1) 字段的时候,显示出来的实际上是一个字符串,值是 0 或者 1,而不是 ASCII ”0“ 或者 ”1“。但是,如果用数值的方法读取,那结果就是数值。例如,如果存储的是 b'00111001' (相当于数字 57),将其存储在 BIT(8) 字段中,读取之后,你会得到字母编码是 57 的字符串,这也是 ”9“ 的ASCII 字符码:

mysql> CREATE TABLE bittest(a bit(8));

mysql> INSERT INTO bittest VALUES(b'00111001');

mysql> SELECT a, a + 0 FROM bittest;

+------+-------+

| a | a + 0 |

+------+-------+

| 9 | 57 |

+------+-------+

这会产生很多歧义,Jack建议慎用 BIT,在大多数场景下,最好不要使用 BIT 类型

如果你需要将 true/false 值存在一个单个 bit 的字段中,另一个方法就是用 nullable 的 CHAR(0) 字段,该字段可以存储无数据(NULL)和长度是0的数据(空字符串)

SET

如果你需要存储多个 true/false 值,可以考虑用 MySQL 自带的 SET 数据类型将其合并成一个字段,MySQL 对此的存储方式是用一个 bit set。存储起来非常高效,MySQL 还可以利用 FIND_IN_SET() 和 FIELD() 优化查询语句。最大的缺点就是如果需要修改字段的定义,那么代价就很高:需要 ALTER TABLE 语句,这在大的表中代价是很大的,而且,你不能在 SET 字段上用索引

整数字段上的位运算

另外一种和 SET 有相同效果的方案是始终整数表示一个 bit 集合,例如,你可以将 8 个 bits 放入一个 TINYINT,用位运算符来处理它们。在应用中,还可以利用代名称的常数来表示每一个 bit,简化复杂度。

这种方法的优点在于我们可以不需要 ALTER TABLE 就能改变字段含义。缺点是你的查询语句会变的比较复杂和难懂,有些人喜欢位运算,觉得逼格高,但是有些人则不喜欢,所以这主要还是看个人和团队喜好。

一个合并 bits 的应用就是 ACL,控制访问权限。每个 bit 或者 SET 元素代表了一个访问权限开关,比如 CAN_READ,CAN_READ,CAN_DELETE。如果你使用的是 SET 字段类型,那么你会依赖 MySQL 的 bit 映射表;如果使用整数字段,映射表会放在应用当时。一个 SET 字段查询语句举例:

mysql> CREATE TABLE acl (

-> perms SET('CAN_READ', 'CAN_WRITE', 'CAN_DELETE') NOT NULL

-> );

mysql> INSERT INTO acl(perms) VALUES ('CAN_READ,CAN_DELETE');

mysql> SELECT perms FROM acl WHERE FIND_IN_SET('AN_READ', perms);

+---------------------+

| perms |

+---------------------+

| CAN_READ,CAN_DELETE |

+---------------------+

如果你用的是一个整数,那么上述举例就是:

mysql> SET @CAN_READ := 1 << 0,

-> @CAN_WRITE := 1 << 1,

-> @CAN_DELETE := 1 << 2;

mysql> CREATE TABLE acl (

-> perms TINYINT UNSIGNED NOT NULL DEFAULT 0

-> );

mysql> INSERT INTO acl(perms) VALUES(@CAN_READ + @CAN_DELETE);

mysql> SELECT perms FROM acl WHERE perms & @CAN_READ;

+-------+

| perms |

+-------+

| 5 |

+-------+

我们这里使用的变量来定义,但你也可以在应用中使用自己的常数。

ID 字段的类型选择

给 ID 类型的字段选择一个合适的字段类型至关重要。这些字段经常会被拿去和其它字段比较(比如 join),或者用这点字段去查找其它字段。你也会用它当多 foreign keys,所以如果是给这些字段选择类型,还需要考虑其他相关表的相关字段类型。

在选择类型的时候,你不仅要考虑存储方面的问题,还需要结合 MySQL 是如何对这个类型进行计算和比较的。比如,MySQL 存储 ENUM 和 SET 类型的方式是用整数,在比较字符串的时候会将其转换成字符串。

当选定了一个 ID 字段的类型之后,在其它表的相关字段中也需要使用相同的类型,需要完全相同,比如是否都是 UNSIGNED

选择占用空间最小的类型来存储,适当留出额外空间来应对将来的增长需求。比如,如果你要 province_id 字段存储国内所有的省份,你不需要存储几百万个数值,所以不要用 INT,TINYINT 就足够了而且还省了 3 个字节的存储空间,如果你将这个字段当作 oreign key 和其它表相连,3 个字节能带来巨大的性能差异。这里Jack给几点建议:整数类型:整数类型是 ID 类型的最佳选择,因为处理起来很快,并且可以使用 AUTO_INCREMENT

ENUM 和 SET:ENUM 和 SET 非常不适合用作 ID 类型的字段,只是在小的静态定义类型的表中可以用作 ID 类型的字段。ENUM 和 SET 字段适合存储比如订单状态,产品类型,或者性别等数据。举个例子,如果你用 ENUM 定义产品类型,那么你需要另一个 ID 是 ENUM 字段的表,但这样的情况应该避免。

字符串类型:尽量避免使用字符串类型的 ID 字段,因为占用太多的空间而且比整数型要慢很多。而且要慎用

如果需要存储 UUID 数据,你应该将 UUID 中的中划线剔除,或者用 UNHEX() 将其转换成 16 字节的数字,然后存储在 BINARY(16) 的字段中,之后你就能用 HEX() 将它们读成16进制的数字。

特殊类型的数据

某些数据没有直接对应的存储类型,毫秒级别的时间戳就是一个例子。我们在上文当中已经阐述了如何存储这类型的数据。

另一个例子就是 IPv4 地址。人们通常使用 VARCHAR(15) 来存储,但是,IPv4 实际上是 unsigned 的 32 位整数,不是字符串。里面的点符号只是为了方便大家阅读罢了。我们应该将 IP 地址存成 unsigned 整数。MySQL 支持用 INET_ATON() 和 INET_NTOA() 将两者相互转换。

总结费脑,码字不易,如若有用,还请点赞,分享更赞~

参考^某些第三方存储引擎,例如 Infobright,在存储格式和压缩方式上和大多数 MySQL 原生支持的存储引擎很不一样

^读取 BINARY 的时候要格外注意,MySQL 会在尾部添加 \0,所以如果要比较 BINARY,一定要记住这一点!

^在不同 MySQL 版本之间,TIMESTAMP 有不同的复杂规则,所以你需要亲自验证

^如果你使用 InnoDB,如果字段类型不完全一样,是不能创建 foreign key 的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/468378.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

测试两个主机之间的连通性_借助网络测试神器极速解决网络故障

时下&#xff0c;网络使用越来越频繁&#xff0c;随之遇到网络故障的情况也多起来&#xff0c;那么诊断网络故障就成了必不可少的一环&#xff0c;投诉和求助是一种基本的方法&#xff0c;但有时太过缓慢&#xff0c;有些时候自己可以快速搞定&#xff0c;检查到故障原因所在&a…

随想,对嵌入式职场建议

guoqingjie已经过去两天了&#xff0c;因为楠哥生病&#xff0c;我们搁浅在了深圳&#xff0c;不过相比在路上的奔波&#xff0c;待在家里感觉惬意不少&#xff0c;不用在路上各种操心&#xff0c;之前上班感冒&#xff0c;身体处在亚健康状态&#xff0c;睡了两天身体也恢复一…

java可达性_java垃圾回收机制--可达性算法

先说一些题外话&#xff0c;Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区&#xff0c;这些区分为线程私有区和线程共享区1、线程私有区a、程序计数器记录正在执行的虚拟机字节码指令地址。此区域是是唯一一个在java虚拟机规范中没有规定任何Ou…

为什么要有uboot?

一、为什么要有uboot1.1、计算机系统的主要部件(1)计算机系统就是以CPU为核心来运行的系统。典型的计算机系统有&#xff1a;PC机&#xff08;台式机笔记本&#xff09;、嵌入式设备&#xff08;手机、平板电脑、游戏机&#xff09;、单片机&#xff08;家用电器像电饭锅、空调…

华为路由器上有没有mac表_MAC地址表、ARP缓存表、路由表及交换机、路由器基本原理...

MAC地址表说到MAC地址表&#xff0c;就不得不说一下交换机的工作原理了&#xff0c;因为交换机是根据MAC地址表转发数据帧的。在交换机中有一张记录着局域网主机MAC地址与交换机接口的对应关系的表&#xff0c;交换机就是根据这张表负责将数据帧传输到指定的主机上的。交换机的…

#define的高级用法

来源&#xff1a;嵌入式大杂烩地址&#xff1a;https://blog.csdn.net/xiaoxu2050/article/details/82893476一、宏的定义与撤销需要注意的是&#xff1a;&#xff08;1&#xff09;宏定义应注意添加括号&#xff0c;这样语义会比较清晰。&#xff08;2&#xff09;使用#undef可…

python读取csv文件_python3.0读取csv文件

创建CSV文件&#xff0c;可以打开excel文件&#xff0c;创建表格以后&#xff0c;另存为csv文件(以逗号分隔的文件)以文本格式打开&#xff0c;发现是用逗号分隔的用python读取csv文件&#xff0c;首先要引入一个库:csvimport csvdef getCsvFile(strpath):with open(strpath, &…

直击中关村创业大街,新街头霸王来了

中国的硅谷在北京中关村。如今&#xff0c;中关村中正在酝酿硅谷中的硅谷&#xff0c;这里将不断诞生最前卫最现代的互联网和移动互联网创业公司&#xff0c;他们当中极可能孵化出中国互联网企业未来10年的新的BAT。这个硅谷中的硅谷。就是中关村创业大街。这条街在过去15年是北…

我们应该这样理解鸿蒙

之前写过文章谈到的鸿蒙&#xff0c;很多人觉得我说的有点过分&#xff0c;但是我觉得现在出来的情况还是符合预期的&#xff0c;鸿蒙正在完成他的任务。讲个故事&#xff0c;手机还没有普及的时候&#xff0c;相机的地位非常高&#xff0c;相机里面有一个东西叫做胶卷&#xf…

人工智能在语音和数字图像处理领域有哪些具体化应用_智能呼叫中心系统有哪些优势...

近年来&#xff0c;随着科技的发展特别是人工智能技术、云计算、大数据的到来&#xff0c;新技术逐渐渗入人们日常生活中的各行各业里面&#xff0c;以机器人来代替人工处理繁重的任务变得普遍。在人工智能时代下&#xff0c;新一代智能呼叫中心系统应运而生&#xff0c;呼叫中…

centos下mysql多实例安装3306、3307实例(2014-10-15)

背景说明 mysql的安装方法有多种&#xff0c;如二进制安装、源代码编译安装、yum安装等。yum安装仅仅能安装mysql 5.1 版本号&#xff1b;源代码安装编译的过程比較长。若没有对源代码进行改动且要求使用mysql较高版本号&#xff0c;建议使用二进制安装。本文以二进制安装mysql…

如何自己实现一个栈

文章转自编程珠玑&#xff0c;作者&#xff1a;守望先生前言栈是一种应用广泛的数据结构&#xff0c;例如函数的调用就需要使用栈&#xff0c;其实我们在介绍《栈的操作栈的常见操作有出栈(POP)&#xff0c;从栈中弹出一个元素&#xff1b;入栈(PUSH)&#xff0c;将一个元素压入…

python编写ATM类_Python中编写类的各种技巧和方法

有关 Python 内编写类的各种技巧和方法(构建和初始化、重载操作符、类描述、属性访问控制、自定义序列、反射机制、可调用对象、上下文管理、构建描述符对象、Pickling)。你可以把它当作一个教程&#xff0c;进阶&#xff0c;或者使用参考&#xff1b;我希望它能够成为一份针对…

再说嵌入式入门

我之前写过几篇嵌入式入门的文章 不过我的读者还是觉得不够过瘾&#xff0c;我觉得还是要着重说一下嵌入式软件应该重点学习什么&#xff0c;guoqing收假最后一天写文。精通C语言 精通C语言 精通C语言C语言对于嵌入式软件来说就是他的命脉&#xff0c;不懂C语言的人根本就不能说…

java ios 开发工具_iOS应用开发的五个Java开源工具

随着第三方工具的不断壮大&#xff0c;开发人员逐渐摆脱政策束缚&#xff0c;对于iOS系统的封闭性为其他语言(如Java)开发者诟病得到解脱&#xff0c;开始使用自己熟悉的语言来编写iOS本地应用&#xff0c;或将其他平台上的应用移植到iOS上。本文为你介绍5款开源的开发工具&…

hive 时间转字符串_大数据面试杀招——Hive高频考点,还不会的进来挨打

一、什么是Hive&#xff0c;为什么要用Hive&#xff0c;你是如何理解Hive?面试官往往一上来就一个“灵魂三连问”&#xff0c;很多没有提前准备好的小伙伴基本回答得都磕磕绊绊&#xff0c;效果不是很好。下面贴出菌哥的回答&#xff1a;Hive是基于Hadoop的一个数据仓库工具&a…

java jpa jar_JPA 开发所需的Jar包 (基于Hibernate)

JPA 开发所需的Jar包 (基于Hibernate)(一)下载Sun 的JPA规范(即&#xff1a;Jar包)登陆JavaEE 的 Technologies 页面:http://java.sun.com/javaee/technologies/index.jsp选择 Java Persistence 2.0选择 Final Release 中的 Download Page 后进入下面的界面&#xff1a;下载如下…

C语言入坑指南-数组之谜

前言在C语言中&#xff0c;数组和指针似乎总是“暧昧不清”&#xff0c;有时候很容易把它们混淆。本文就来理一理数组和指针之间到底有哪些异同。数组回顾在分析之前&#xff0c;我们不妨回顾一下数组的知识。数组是可以存储一个固定大小的相同类型元素的顺序集合。为了便于我们…

mysql服务器查绑定的域名查_MySQL使用show status查看MySQL服务器状态信息

MySQL使用show status查看MySQL服务器状态信息这篇文章主要介绍了MySQL使用show status查看MySQL服务器状态信息,需要的朋友可以参考下在LAMP架构的网站开发过程中&#xff0c;有些时候我们需要了解MySQL的服务器状态信息&#xff0c;譬如当前MySQL启动后的运行时间&#xff0c…

Fiddler抓包使用教程-安装配置

转载请标明出处&#xff1a;http://blog.csdn.net/zhaoyanjun6/article/details/72876628 本文出自【赵彦军的博客】 Fiddler是什么&#xff1f; Fiddler是一个http调试代理&#xff0c;它能 够记录所有的你电脑和互联网之间的http通讯&#xff0c;Fiddler 可以也可以让你检查所…