mysql设计表结构数据类型的选择
选择合适的数据类型
在使用MySQL创建数据表的时候会遇到一个问题,如何为字段选择合适的数据类型.比如创建一个员工信息表,每个字段都可以用很多种类型来定义,
int,char,float等等.
char和varchar
char和varchar都是用来存储字符串类型的数据,但是他们保存和检索的方式不一样.char属于固定长度的字符类型,二varchar属于可变成的字符类型
值 | char(4) | 存储需求 | varchar(4) | 存储需求 |
'' | ' ' | 4个字节 | '' | 1个字节 |
'ab' | 'ab ' | 4个字节 | 'ab' | 3个字节 |
'abcd' | 'abcd' | 4个字节 | 'abcd' | 5个字节 |
'abcdefg' | 'abcd' | 4个字节 | 'abcd' | 5个字节 |
由于char是固定长度的,所以它的处理速度比varchar快得多,但是其缺点是浪费存储空间,程序需要对尾行空格进行处理,所以对那些变化不打并且查询速度有较高的要求的数据可以考虑使用char类型来存储
在mysql中,不同的存储引擎对char和varchar的使用原则有所不同
- MyISAM存储引擎
- 建议使用固定长度的数列代替可变长度的数据列
- InnoDB存储引擎
- 建议使用varchar类型,对于InnnoDB数据表,内部的行存储格式没有区分固定长度和可变长度,因此使用char列不一定比可变长度的varchar性能好
- 由于char平均占用空间多余varchar,因此varchar来UI消化需要处理的数据航的存储总量和磁盘I/O是比较好的.
TEXT和BLOB
介绍
在选择大文本的时候我们会优先选择text类型或者blob比如文章.
那么TEXT和BLOB最主要的区别是 BLOB能用来保存二进制数据比如照片.而text智能保存字符串数据,比如文章和日记.
根据存储的文本长度不同和存储的字节不同我们可以使用
MEDIUMTEXT,LONGTEXT和MUDIUMBLOB,LONGBLOB
常见问题
空洞问题
BLOB和text在执行了大量的删除操作时,会留下很大的'空洞',以后填入这些'空洞'的记录在插入的性能上回有影响.为了提高性能,建议使用OPTIMIZE TABLE功能对类表进行碎片整理,
避免空洞带来的性能问题
空洞例子:
CREATE TABLE t ( id VARCHAR(100), context TEXT ); INSERT INTO t VALUES(1,repeat('haha',100)); INSERT INTO t VALUES(2,repeat('haha',100)); INSERT INTO t VALUES(3,repeat('haha',100)); insert into t select * from t; insert into t select * from t; ..... insert into t select * from t;
这时候文件的大小为:
删除id=1的数据,那么就是删除了1/3的数据:
mysql> delete from t where id=1; Query OK, 32768 rows affected (0.63 sec)
再看文件大小,我们可以看到文件大小还是96MB,这就形成了空洞.
我们用OPTIMIZE进行优化:
mysql> optimize table t;
此时我们再看文件,已经变成了60MB,文件大大的缩小了,说明'空洞被收回了'
文件索引
使用合成索引来提高大文本字段的查询性能:
合成索引就是根据大文本的字段的内容建立一个散列值,并且把这个值存储在单独的数据列中,接下来可以通过检索散列值来找到数据.
但是,要注意这种技术只能用于精确匹配(对于< >=等范围搜索是没有用的)
可以使用MD5()函数来生成散列值.
下面介绍一下合成索引的方法:
CREATE TABLE t ( id VARCHAR(100), context BLOB, hash_value char(32) ); INSERT INTO t VALUES(1,repeat('beijing',2),MD5(context)); INSERT INTO t VALUES(1,repeat('beijing2008',2),MD5(context)); mysql> select * from t where hash_value=md5(repeat('beijing2008',2)); +------+------------------------+----------------------------------+ | id | context | hash_value | +------+------------------------+----------------------------------+ | 1 | beijing2008beijing2008 | 0fe88accc8741a9d1bc323bd286866bb | +------+------------------------+----------------------------------+
由于这种技术只能用于精确匹配,从一定程度上减少了I/O,提高了查询效率.如果需要对BLOB字段进行模糊查询,MYsql提供了前缀索引,也就是只为字段的前n列创建索引
create index idx_blob on t(context(100));
mysql> desc select * from t where context like "beijing%" \G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t type: range possible_keys: idx_blob key: idx_blob key_len: 103 ref: NULL rows: 2 Extra: Using where 1 row in set (0.04 sec)
对context的前100个字符进行模糊查询,就可以用前缀索引.
注意这里的%不能放在前面,否则不能命中索引
避免使用select *
不要用select * 检索大型的BLOB或者TEXT值
除非能够确定约束条件where只会找到需要的数据,否则很可能毫无目的在网络上传输大量的值.
用户可以用搜索索引列,决定需要的那些数据行,然后从符合条件的数据中检索BLOB或者TEXT
分表
水平分表,在某些环境中,如果把这些大型的列数据移动到第二章表数据中,那么把原数据表中的数据列转换成固定长度的数据行格式,
那么它就是有意义的.这会减少表中的碎片,可以得到固定长度的性能优势.
浮点数和定点数
在MYSQL中用float,double来标识浮点数.当一个字段被定义浮点类型后,如果插入的数据精度超过该列定义的实际精度,那么会采取四舍五入的办法来得到实际的值.
定点数不同于浮点数,他是用字符串的形式存存放的,所以插入的实际值精度大于实际定义的精度,如果在传统模式下,会直接报错,不能插入数据.
CREATE TABLE test(c1 float(10,2),c2 decimal(10,2)); INSERT INTO text VALUES(131072.32,131072.32); mysql> select * from test; +-----------+-----------+ | c1 | c2 | +-----------+-----------+ | 131072.31 | 131072.32 | +-----------+-----------+
可以看到c1列的值从131072.32 变成了131072.31,这是上面的数值在使用单精度浮点数表示时,产生了误差.
注意:
- 浮点数存在误差问题
- 对货币等对精度要求比较高的数据,应该用定点数表示或存储
- 在变成中,如果用到浮点数,要特别注意误差问题,尽量避免做浮点数比较
日期类型的选择
- 根据实际需要选择能够满足的最小存储的日期类型,如果只需要记录年,那么用一个字节来存储的YEAR类型就而已满足.而不需要用4个字节的date,不仅节省空间,还提高查询效率
- 如果要记录年月日时分秒,并记录比较久远,那么最好使用datetime,不要使用timestamp
- 如果记录的日期需要不同的时区的用户使用,那么最好使用timestamp,因为日期类型中,只有他能够和实际时区相对应