尽量选择范围小的数值类型
1、整型数值的选择
tinyint、smallint、mediumint、int、bigint
tinyint 存储所占一个字节,-128~127
smallint 存储所占两个字节,有符号则是 -32768~32767 (3万)
mediumint 存储所占三个字节,有符号可以存储 -8388608~8388607(800万)
int 存储所占四个字节,有符号则是 -2147483648~2147483647 (21亿)
bigint 存储所占8个字节,有符号则是 -9223372036854775808 到9223372036854775807 (907亿零六十万)
可以定义无符号,让它们的范围扩大一倍;有符号和无符号使用相同的空间存储。并拥有相同的性能。
可以为整数类型指定宽度,这对大多数应用是没有意义的。只是在mysql客户端显示的字符个数不同(填充0)对于存储和计算来说INT(1)和INT(20)是相同的
尽量选择范围小的数值类型,比如要定义一个开关。只有0和1,那么肯定使用tinyint(1) 了。最好是估算需要多大的范围,然后根据范围选择吧
有符号值:-9223372036854775808 到9223373036854775807(- 2 ^ 63 到 2 ^ 63-1)
无符号值:0到18446744073709551615(0到2^64 – 1)
bigint会不会用完?看下这图就知道了。。对比下,淘宝双十一tps50万,天天双十一都要用 2*584942年
2、浮点选择原则:定长和非定长数据类型的选择
浮点数float在储存空间及运行效率上要优于精度数值类型decimal,但float与double会有舍入错误而decimal则可以提供更加准确的小数级精确运算不会有错误产生计算更精确,适用于金融类型数据的存储。
- 金额请使用 decimal类型
在char 和 varchar、TEXT 中做出抉择
原则:尽可能选择小的数据类型和指定短的长度;
如果超过指定长度,mysql可能报错 data too long;前提是指定了sql_mode为 STRICT_TRANS_TABLES,否则并不会报错,只是自动截取只能存储的部分。比如我设置长度为5,存储一个'yinkai' 字符串,那么只有'yinka'会被存储没有超过指定长度,char和varchar表现是不同的。varchar需要多余的空间去记录字符串长度
1、使用varchar:
varchar类型用于存储可变长度的字符串。它比char这种定长类型更节省空间,因为它仅仅使用必要的空间(例如,越短的字符串使用越少的空间);varchar需要使用1或2个额外字节记录字符串的长度,若列的最大长度<=255字节,则只使用1个字节。否则使用2个字节存储长度;
varchar类型节省了存储空间,所以对性能也有帮助。但是,由于行是变长的,在UPDATE时可能使行变得比原来更长,这就导致需要额外的存储。
适用于 字符串列的最大长度比平均长度大的多;列的更新很少(varchar频繁更新将产生碎片)
过度慷慨的设置字符长度是不明智的,使用varchar(5) 和 varchar(200)存储'hello'在空间上开销是一样的。那么使用更短的列有什么优势吗?事实证明有很大的优势。更长的列会消耗更多的内存,因为mysql通常会分配固定大小的内存来保持内部值。尤其是使用内存临时表进行排序或操作时特别糟糕。在利用磁盘临时表进行排序时也同样糟糕!
所以最好的策略时是只分配真正需要的空间!!
2、使用char:
char类型是定长的,mysql总是根据定义的字符串长度分配足够的空间。若定义的字符长度大于存储的长度则剩余的空间并没有被使用而浪费。剩余的空间会被填充半角空格
字符
char适合存储很短的字符串,或者所有值都接近同一个长度。对于经常变更的数据,char比varchar更好,因为定长的char类型不容易产生碎片。对于非常短的列,char比varchar在存储空间上也更有有效率。
- MD5摘要
- uuid
电话号码、手机号等
结论:相比之下varchar更灵活,更节省空间。而char适合规定的一些字符串,如电话号码,身份证号码,当出现错误时更易发现。
3.其次BLOB或TEXT
当存储文本较多的时候,如文章、产品描述、逻辑评论,blob一般存储二进制,如音频,图片
text存储文章,文字较多时使用
4、使用text:
像商品详情、文章详情这样的大文本对象可以使用text类型。text类型也分为几种:tinytext、text、mediumtext、longtext
尽量使用整型表示字符串
请使用mysql内建类型来存储时间等,而不是使用字符串!我待过的小公司全部使用字符串来存储所有的数据类型。这对我造成了很大的影响,之前我一直以为使用字符串存储比较好。
整型比字符串操作的代价更低,因为字符集和较对规则(排序规则)使得字符比较比整型比较更复杂。
1、11位手机号可以使用bigint存储
为什么不使用 char(11) ?
Latin1的char(11)占用11字节而bigint占用8字节
为什么不使用int?
int范围 -2147483648 --- 2147483647 十位数字而已,就算使用无符号 unsigned 也容纳不了一个11位的手机号。因此需要使用 bigint
2、使用unsigned int 存储 ip
我们可以使用下面的函数实现 ip转为数值存储到mysql,可以把IP与最大长度为10位
的数值类型互转
INET_ATON(str)
,address to number
INET_NTOA(number)
,number to address
需要使用 unsigned 的 11位 int,若不加无符号则会溢出
3、添加一个冗余列来优化索引查询,使用数值类型存储url字符串
为什么要使用数值类型存储url?
在字符串类型的url字段上加索引效率并不高!前几位的http是重复的。为此我们可以这样做:考虑在表中新增一个冗余字段来存储url的伪hash值。在mysql中我们可以使用crc32('a') 函数来得到url对应的数值。然后在数值上建立索引,在插入数据时将url对应的数值插入到库中。
创建表,创建索引,插入数据
CREATE TABLE `t2` (
`id` int(11) NOT NULL,
`url` varchar(5000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`crcurl` bigint(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `crcurl`(`crcurl`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `t2` VALUES (1, 'https://www.baidu.com', CRC32('https://www.baidu.com'));
查询
EXPLAIN SELECT * FROM `t2` WHERE crcurl = CRC32('https://www.baidu.com')
也有另一种算法来生成url的标识
SELECT CONV(RIGHT(MD5('http://www.mysql.com/'), 16), 16, 10)
4、使用mysql内建类型来存储时间而不是字符串
使用DATETIME或TIMESAMP而不是直接使用字符串存储时间!很多人图省事就直接使用字符串类型存储时间了,因为可以避免很多时间转换操作,但是这对mysql存储和查询都没好处。
合理使用日期、时间类型
CREATE TABLE `test`.`Untitled` (
`year` year NULL DEFAULT NULL,
`date` date NULL DEFAULT NULL,
`time` time(0) NULL DEFAULT NULL,
`datetime` datetime(0) NULL DEFAULT NULL,
`timestamp` timestamp(0) NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
mysql给我们提供了5中和日期、时间有关的类型:
year 年份 2020
date 日期 2020
time 时间 17:23:25
datetime 2020-04-27 17:23:30
timestamp 2020-04-27 17:23:35
那么,datetime 和 timestamp 有什么区别呢?
1、两者的存储方式不一样
对于TIMESTAMP,它把客户端插入的时间从当前时区转化为UTC(世界标准时间)进行存储。查询时,将其又转化为客户端当前时区进行返回。TIMESTAMP的显示依赖于服务器当地时区。
也就是说。在mysql在中国服务器中插入的数据放到美国服务器去显示的时间就不同了。
而对于DATETIME,不做任何改变,基本上是原样输入和输出。
2、两者所能存储的时间范围不一样
timestamp所能存储的时间范围为:'1970-01-01 00:00:01.000000' 到 '2038-01-19 03:14:07.999999'。
datetime所能存储的时间范围为:'1000-01-01 00:00:00.000000' 到 '9999-12-31 23:59:59.999999'。
时间超过2038范围,或者服务器时区不一样的就建议选择 datetime。如果是想要使用自动插入时间或者自动更新时间功能的,可以使用timestamp。timestamp性能比较好
字符集选择
1、单字节字符集 Latin1
uuid、md5、cm3、base64 都可使用这个Latin1字符集,一个字符占用一个字节。空间非常小了。
像是base64就可以使用Latin1;纯数字也可使用Latin1
2、中文字符集 utf8mb4
不需要中文选择latin1
需要支持中文;小字段能用utf8mb4就用utf8mb4,避免存储不了表情符号;不需要存储表情符的大字段(longtext)最好使用utf8节省空间
字符串类型的存储需求
M、L 表示存储字符串包含字符个数,也就是字符串长度
列类型 存储需求
- CHAR(M) M个字节,0 <= M <= 255
- VARCHAR(M) L+1个字节,其中L <= M 且0 <= M <= 65535 ( 2的16次方)
- BINARY(M) M个字节,0 <= M <= 255
- VARBINARY(M) L+1个字节,其中L <= M 且0 <= M <= 255
- TINYBLOB, TINYTEXT L+1个字节,其中L < 2的8次方
- BLOB, TEXT L+2个字节,其中L < 2的16次方
- MEDIUMBLOB, MEDIUMTEXT L+3个字节,其中L < 2的24次方
- LONGBLOB, LONGTEXT L+4个字节,其中L < 2的32次方
1、从这个可以看出 VARCHAR类型和TEXT类型能存储的容量是一样的,但VARCHAR类型要比TEXT存储相同字符串要多一个字节,一般使用VARCHAR而不是TEXT
2、像一些大篇幅的文章、不定长的图片base64 可以直接使用LONGTEXT类型了。毕竟只比TEXT多2个字节,直接选最大的可以避免出现数据过大容纳不了的错误
常见数据类型选择
姓名:char(20)
年龄:tinyint(4)
各种状态:tinyint(4) (不推荐使用枚举,枚举不好扩展选项)
价格:DECIMAL(7, 3)
文章内容: LONGTEXT
BASE64: LONGTEXT
手机号:bigint(11)
自增主键:unsigned bigint(20)
md5: char(32)
uuid:char(32)、char(36)
ipv4: 借用inet_aton/inet_ntoa函数,使用 unsigned int(11)
time: int(10)
email:char(32)
身份证号码:char(18)
地址:varchar(50)
文章:longtext
日期时间:datetime
url冗余列:bigint(11)
小细节
使用tinyint(4),不使用tinyint(1)的原因是tinyint(1)会被orm框架认为是布尔类型,在生成实体类时。直接被翻译成布尔类型而不是Integer类型