MySQL5.7数据库优化之表设计

时间:2024-03-13 15:24:14

一个数据库、表设计的优劣会影响到数据库的性能,所以合理的设计数据库是非常重要的。
最近看了MySQL5.7手册,手册第八章就是关于优化的,第十一章详细的介绍了各个字段。如果你有兴趣可以去看看,相信会收获颇丰。下面根据手册及结合平时开发经验还有大学学的数据库原理来谈谈一些自己的见解。由于水平有限,难免会有错误及疏漏的地方,欢迎指正。

一、数据库的创建

创建数据库非常简单,只不过,建议给数据库指定默认的字符集和排序规则。虽然表、字段均可指定字符集和排序规则。

字符集一般选utf8或者utf8mb4,utf8mb4是utf8的超集,兼容四个字节。utf8是utf8mb3的别名,最大支持三个字节,所以存储四个字节的字符时会出错,例如单字符表情占四个字节,使用utf8存储就会出错。

指定字段长度时,在绝大多数情况下,utf8与utf8mb4是一样的,但有一个需要注意,那就是定长类型(char)。char类型会预先分配存储空间,对于ut8是按照三个字节计算的,对于utf8mb4是按照四个字节分配的,所以utf8mb4会比ut8多占存储空间。根据业务特点选择是的字符,这里推荐如下:

CREATE DATABASE `test` DEFAULT CHARACTER SET utf8mb4 COLLATE=utf8mb4_general_ci;

二、表的设计

1、存储引擎的选择

我们可以使用SHOW ENGINES来查看所支持的存储引擎。
MySQL5.7数据库优化之表设计
一般常见的有InnoDB、MyISAM、MEMORY、Archive。用的最多的是InnoDB、MyISAM。Archive 是存档型存储引擎,提供无阻塞插入和查询,但是也只能做插入和查询操作,MEMORY是内存型存储引擎,数据存储于内存中,常用于缓存。

此版本对InnoDB做了大量的优化、增加了很多特性,比如开始支持全文索引(fulltext)等。只不过mysql自带的全文索引不支持中文分词,中文分词可以使用coreseek。coreseek是在sphinx的基础上,增加了中文分词功能,所以可以用来做中文的全文索引。直接编译进mysql中性能更强。

下面说说Innodb和MyISAM这两个存储引擎的几个最重要的区别(只有了解了区别,才知道如何选择合适的存储引擎):

1)存储方式不同

MyISAM数据、索引、结构分别存在不同的文件中(.MYD.MYI.frm);
InnoDB默认数据和索引存储在同一个表空间文件中(.ibd),结构存储在另一个文件中(.MYI)。当然,也可以使用SET global innodb_file_per_table=1;使数据和索引存储在不同的表空间文件中。如果一张大表需要很多索引,这能起到很不错的优化效果。 对于小表就无所谓了。

2)锁的粒度不同

MyISAM只支持表级锁
InnoBD既支持表级锁,还支持行级锁
这也就是为什么InnoBD并发写(修改、删除)操作性能更好

3)InnoDB支持事务、外键约束等

4)索引实现方式不同

为了减少一次性磁盘IO开销,可以读取到更多的关键字数量,MyISAM和InnoDB均采用B+树作为索引结构,但是实现的方式却有不同。InnoDB采用的是聚簇索引,即主键的叶子节点保存了完整的数据记录。

下面举例说明这两者的差异:
假设我们要存储以下数据:

id name age
1 zhaoyi 13
3 sunsan 14
2 qianer 15
4 lisi 13
5 zhouwu 11
6 wuliu 10
7 zhengqi 21
8 wangba 15
9 fengjiu 17
10 chenshi 16
11 chushiyi 13
12 weishier 14
假设用MyISAM存储引擎存储,那么存储方式如下图

MySQL5.7数据库优化之表设计
由上图可以看出,主键索引的叶子节点存储的是数据的地址,数据并不是按主键顺序存储的。 所以查询数据时,如果不指定排序,则会按存储的顺序检索出来,所以你会发现id=3却在id=2后面。
由于主键保存的时数据的地址,所以查询非常快,这也就是MyISAM适合高速读的原因
又由于插入时数据不需要排序,所以插入速度也非常快,这就是MyISAM适合高速并发插入的原因

假设用InnoDB存储引擎存储,那么存储方式如下图

MySQL5.7数据库优化之表设计
由上图可以看出,主键索引是聚簇索引,叶子节点存储的是数据。
每次插入数据都会根据主键id进行排序插入,这也就是为什么InnoDB不适合高速并发插入的原因。 也是InnoDB表必须有一个主键的原因,而主键往往推荐使用自增整型,避免使用字符类型(char、varchar),因为整型排序更快,自增插入会加快插入的速度。

对于二级索引,MyISAM和Innodb实现也是不同的,虽然都是B+树,但是MyISAM的叶子节点保存的是数据的地址,与主键的实现方式是一致的。而InnoDB的二级索引的叶子节点保存的是主键id。

假设我们在name字段创建一个普通索引,那么MyISAM和InnoDB的实现方式分别如下:
MySQL5.7数据库优化之表设计

MySQL5.7数据库优化之表设计
由上图可以看出,InnoDB二级索引叶子节点存储的是主键id,这也间接的表明对于InnoDB,每个二级索引都有一个主键字段隐藏在索引中,当查询索引+id时会走索引覆盖,不需要去查表记录
即当我们需要查询id和name时,使用SELECT id,name FROM student时,只会走索引,不会去查表记录,速度极快。

综上所述:

当表只需要高速的查询和插入时,可以选择MyISAM存储引擎,
当表需要并发的修改和删除、事务外键约束等保持数据完整性的要求时,可以选择InnoDB存储引擎。
需要注意的是,MyISAM虽然高并发的插入性能非常优越,但是这是建立在表没有太多空行的情况下,如果表有过多的删除动作,就会造成很多空行,使并发插入性能急剧下降。
另外主键建议使用整型自增。

2、分区分表

分区:
对于大型表,比如订单表,建议使用分区来增强性能。
使用Partition by 分区算法 分区选项来对表进行分区。
mysql提供了四种分区算法,分别是Key,hash,List,range。各有优缺点,一般常用的是key算法,即求模算法。
分表:
对于大型表,尽量做到“动静分离”,即把不常改动的字段放一张表里,把经常改动的字段放另一种扩展表里,以此来提高性能。

3、在BCNF的基础上适当做一些冗余。

适当的冗余可以减少联表操作,提高查询性能。当然,并不是所有的字段都适合做冗余,要根据业务特点,选择极少变更但又经常用到的字段 来做冗余。

4、字段类型的选择

关于字段类型的选择,有几个原则:

  1. 字段要尽可能的使用整型
  2. 字段要尽可能的小
  3. Innodb引擎表字段要尽可能的使用变长(虽然定长检索比变长要快,但会预先分配较多的存储空间,MySQL手册也推荐使用变长,因为MySQL优化了Innodb的varchar的存储)
  4. 字段要尽可能声明为NOT NULL,尽量指定一个默认值,比如状态字段status TINYINT NOT NULL DEFAULT '0' COMMENT...,因为NULL是一个特殊类型,是占用存储空间的,并且会使索引查询变得复杂,特别是联表查询,所以外键(逻辑外键,一般情况下不要使用MySQL声明为外键,减少数据库的开销)字段一定要声明为NOT NULL,并且尽可能的为整型

MySQL支持多种数据类型,有数字类型、日期和时间类型 、字符串类型、空间数据类型、JSON类型。下面一一介绍各类型及推荐使用:

1、数字类型:

数字类型的使用原则是:

  1. 尽可能的使用范围小的类型 ,能使用TINYINT就绝不使用SMALLINT,比如状态字段,一般状态不会太多,使用TINYINT就足够了,但见过很多人使用INT(1)来作为状态的字段类型,但 INT(1) 和 INT 实质是没有区别的,TINYINT 占一个字节,INT 占四个字节,所以会浪费大量存储空间。
  2. 能指定为无符号(UNSIGNED)尽量指定为无符号,比如自增整型主键id,一般不会为负数,所以应该指定UNSIGNED来增加正整数的范围,否则会浪费符号位。最典型的主键字段设计id INT UNSIGNED NOT NULL AUTO_INCREMENT

下面详细介绍各个数字类型:

1)整数类型:TINYINT、SMALLINT、MEDIUMINT、INT (INTEGERd的同义词)、BIGINT

下面给出这五种类型的有符号和无符号数的范围,可以根据实际情况选择使用。

类型 存储(字节) 有符号最小值 有符号最大值 无符号最小值 无符号最大值
TINYINT 1 -128 127 0 255
SMALLINT 2 -32768 32767 0 65535
TINYINT 3 -8388608 8388607 0 16777215
MEDIUMINT 4 -2147483648 2147483647 0 4294967295
BIGINT 8 -263 263 - 1 0 264 - 1
2)定点类型:DECIMAL、NUMERIC

当你需要存储精确到小数点后几位的数值时,就可以用它们来声明。
DECIMAL的标准语法时DECIMAL(M,N),当你只声明DECIMAL而省略M和N时。默认M为10,N为0。M表示数字个数(不包含负号和小数点),N表示小数点位数。比如你声明DECIMAL(5,2),它表示的范围是-999.99到999.99。

3)浮点类型:FLOAT、DOUBLE

FLOAT是单精度,占四个字节。DOUBLE是双精度,占八个字节。由于精度问题,一般情况下极少被用到。

4)比特值类型:BIT

用来存储二进制数的,用到的频率也比较低

小结:数字类型用的最多的是TINYINT、SMALLINT、MEDIUMINT、INT和DECIMAL,实际设计时,根据字段的范围来选择合适的类型。

2、日期和时间类型:

日期和时间类型主要有DATE、 TIME、 DATETIME、 TIMESTAMP 和 YEAR这几种。
DATE一般格式为:xxxx-xx-xx
TIME一般格式为:xx:xx:xx
DATETIME一般格式为:xxxx-xx-xx xx:xx:xx
TIMESTAMP 一般格式为:xxxx-xx-xx xx:xx:xx
YEAR一般格式为:xxxx
MySQL默认这些类型时不能保存“零值”的。比如0000-00-00,如果你想保存这样值,需要禁用NO_ZERO_DATE模式。
对于DATE、TIME、YEAR这三种没什么特别强调的
下面说说DATETIME和TIMESTAMP的区别:
相同点:
1)默认的格式都是xxxx-xx-xx xx:xx:xx
2)都可以指定位数。
DATETIME和TIMESTAMP的完整写法是DATETIME(M)和TIMESTAMP(M),
M的范围是0~6的整数,默认值是0。比如你声明了DATETIME(1),则日期格式为xxxx-xx-xx xx:xx:xx.x。最高可精确到微秒。不过一般声明DATETIME(即DATETIME(0))即能满足日常需求。
注:TIME也可以指定位数,TIME(M)
不同点:
1)所占存储空间不同
DATETIME占8个字节,TIMESTAMP占4个字节。所以DATETIME比TIMESTAMP占更多的存储空间。
2)所表示的时间范围不同
由于DATETIME占更多的字节,所以DATETIME比TIMESTAMP表示的时间范围要大的多。
DATETIME能表示的时间范围(NO_ZERO_DATE模式下)为:'1000-01-01 00:00:00.000000' 到 '9999-12-31 23:59:59.999999
TIMESTAMP能表示的时间范围(NO_ZERO_DATE模式下)为:'1970-01-01 00:00:01.000000' 到 '2038-01-19 03:14:07.999999'
3)检索速度不同
TIMESTAMP要比DATETIME检索速度快

小结:根据需求以及考虑以后可能的业务扩展,选择合适的类型。
另外,时间建议不要使用int存时间戳,虽然整型查找速度很快,但是整型的时间戳不直观,不利于排查问题

3、字符串类型:

字符串主要的类型有字符串类型CHAR, VARCHAR, BINARY, VARBINARY, BLOB, TEXT, ENUM,和 SET。

1)CHAR和VARCHAR类型

char(M)表示存储长度为M的字符串,M表示字符串的长度,最多可存储255个字符。

如果一张表指定字符集是utf8(utf8mb3、三个字节),声明一个字段为char(4),那么MySQL就会预先分配 4 * 3个字节,哪怕你只保存了'abcd'四个单字节字符,也要占用4 * 3个字节 = 12个字节的空间。如果你存储的是你好ab这几个字符,所占存储空间也是 4 * 3个字节,总共12个字节的存储空间。
如果你想存储一个字符a,MySQL也会在后面补充三个空格,组成四个字符存入,查询出来时,会自动去除后面的空格,所以你如果想存入'a 'a和空格,查询出来也只有a,空格被去除了。

所以char(M)所占存储空间是M*(字符集最大编码长度)。

varchar(M)表示存储最大长度为M的字符串,M表示字符串的长度。与char不同的是,M值只受最大行长度影响(MySQL规定一张表所有字段长度加一起,最大长度不能超过65535字节),所以理论上M的最大值是65533(对于支持多字节字符集,此值有变动,如utf8,还需要除以3 = 21844),因为还需要两个字节保存长度值。
当M小于等于255时,需要额外的一个字节来保存长度。
当M大于255时,需要额外的两个字节来保存长度。

举个例子:
如果一张表指定字符集是utf8(utf8mb3、三个字节),声明一个字段为varchar(4),如果你存储的是四个单字节字符,如abcd,那么所占空间是4 * 1个字节 + 1个字节(保存长度值用的),总共占五个字节存储空间。如果你存储的是你好这两个中文,那么所占存储空间就是2 * 3个字节 + 1个字节,总共占7个字节存储空间。
对比char的例子就可以发现,对于支持多字节的字符集,varchar比char更省存储空间。

如果一张表指定字符集是utf8(utf8mb3、三个字节),声明一个字段为varchar(256),超过了255,所以需要两个额外的字节保存长度值。如果你存储的是四个单字节字符,如abcd,那么所占空间是4 * 1个字节 + 2个字节(保存长度值用的),总共占6个字节存储空间。如果你存储的是你好这两个中文,那么所占存储空间就是2 * 3个字节 + 2个字节,总共占8个字节存储空间。

同时,MySQL5.7对Innodb存储引擎的varchar存储格式做了优化,这也就是为什么推荐优先使用varchar的原因。

还有,要遵从大的原则,长度值M尽可能的小,尽量声明为NOT NULL DEFAULT ''

虽然varcahr类型存储时是按实际字符串大小存储的,但是在使用到临时表时,MySQL还是会按照设置的M值来分配空间,所以M值还是越小越好,可以节省空间。这里的空间有可能是内存空间、也有可能是文件空间。当临时表比较小时(设定值),临时表会在内存中生成,当临时表比较大时,临时表会在文件中生成。所以M值越小,所分配的空间也就越小。

2)BINARY和VARBINARY类型

这两个其实与char和varchar相似,只不过这两个存储的是字符串的二进制值

3)BLOB和TEXT类型

这两个平时见的最多的就是TEXT了,BLOB是存储二进制值的,主要存储二进制的文件等。

BLOB 类型有四种TINYBLOB,BLOB, MEDIUMBLOB,和LONGBLOB,存储值的长度依次增大,由于平时基本不使用,这里就不做过多介绍。

下面介绍TEXT的四种存储类型: TINYTEXT,TEXT, MEDIUMTEXT,和LONGTEXT。
TINYTEXT最多可存储28 = 256个字符串。
TEXT最多可存储216 = 65535个字符串。
MEDIUMTEXT最多可存储224 = 16777216个字符串。
LONGTEXT最多可存储232个字符串。

平时使用最多的就是TEXT,不过还是需要根据实际情况来选择。
注意:
在设计表的时候要把TEXT类型字段拆出来单独存在一张表中

4)ENUM类型

枚举类型用的较少,除非特殊情况,不然一般使用TINYINT代替。比如状态、类型这些字段,如果使用枚举,后期有新需求就需要改动字段。但是你的枚举值如果是字符串,那最好还是用枚举类型。如果是整型值,还是建议使用整型系列类型。

5)SET类型

集合类型,集合中成员使用,(逗号)隔开。只不过对集合数据操作起来不方便,需要使用特定函数进行操作。

4、空间数据类型

存储空间数据,例如地理位置等,一般业务上用到的较少,这里不做讨论。

5、JSON数据类型

这是MySQL5.7.7的labs版本开始支持的数据类型,可以使用MySQL提供的JSON函数来直接对数据进行操作。实际业务中有时候也需要保存JSON文档,对JSON文档进行操作。
JSON类型会对JSON文档进行校验,也不允许设置默认值。JSON列的存储要求与 LONGBLOB或 LONGTEXT列的存储要求大致相同,所占存储空间也大致相同。
JSON 列不能直接索引,要创建间接引用这样一个列的索引,您可以定义一个生成的列,它提取应该被索引的信息,然后在生成的列上创建一个索引,如下例所示(MySQL文档上的例子):

mysql> CREATE TABLE jemp (
    ->     c JSON,
    ->     g INT GENERATED ALWAYS AS (c->"$.id"),
    ->     INDEX i (g)
    -> );
Query OK, 0 rows affected (0.28 sec)

mysql> INSERT INTO jemp (c) VALUES
     >   ('{"id": "1", "name": "Fred"}'), ('{"id": "2", "name": "Wilma"}'),
     >   ('{"id": "3", "name": "Barney"}'), ('{"id": "4", "name": "Betty"}');
Query OK, 4 rows affected (0.04 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> SELECT c->>"$.name" AS name
     >     FROM jemp WHERE g > 2;
+--------+
| name   |
+--------+
| Barney |
| Betty  |
+--------+
2 rows in set (0.00 sec)

mysql> EXPLAIN SELECT c->>"$.name" AS name
     >    FROM jemp WHERE g > 2\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: jemp
   partitions: NULL
         type: range
possible_keys: i
          key: i
      key_len: 5
          ref: NULL
         rows: 2
     filtered: 100.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec)

mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
  Level: Note
   Code: 1003
Message: /* select#1 */ select json_unquote(json_extract(`test`.`jemp`.`c`,'$.name'))
AS `name` from `test`.`jemp` where (`test`.`jemp`.`g` > 2)
1 row in set (0.00 sec)

通过上面的例子可以看出,JSON类型如果想使用索引,就必须将JSON文档中的属性提取出来生成一列,然后在该列上创建索引。
一般实际应用时,存储的JSON文档尽量避免使用文档的属性值作为查找条件。
避无可避时记得使用上面的方法创建索引来提高查询效率。

5、索引的选择

索引在查询中起到至关重要的作用,直接影响查询效率,好的索引的设计能极大的增强MySQL的性能。但,索引并不是越多越好。 因为每次的插入、删除、修改操作都可能需要重新维护索引,影响这些操作的性能。所以需要在“查”和“增、删、改”之间找一个平衡。
这个平衡点不好找,需要设计人员根据业务特点及自身设计经验,还有经常性对SQL语句的分析得出的结论来不停的尝试。因为同一个查询语句,在不同的数据量(记录数)下,默认使用的索引可能不同(MySQL优化器会根据统计量选择合适的索引)。
想查看一条SQL语句索引的使用情况,可以使用查询执行计划EXPLAIN来分析。
如果MySQL没有按照设想的使用索引,可以尝试使用ANALYZE TABLE语法 来更新表统计信息。

MySQL提供了PRIMARY KEY(主键索引)、 UNIQUE(唯一索引)、INDEX(普通索引)和 FULLTEXT(全文索引)等索引。

大多数MySQL索引(PRIMARY KEY, UNIQUE,INDEX和 FULLTEXT)是使用 B-Trees存储的。但也有例外,空间数据类型的索引使用R-Trees; MEMORY 表也​​支持哈希索引 ; InnoDB使用反向列表作为FULLTEXT索引。

我们常看到使用SQL语句创建索引时,有人使用KEY,有人使用INDEX,那么这两者有什么区别呢?
INDEX通常表述为普通索引,没有约束条件,而KEY呢,通常表述约束索引,比如PRIMARY KEYUNIQUE KEY等。但在MySQL中,KEY代替了INDEX的声明,就算你声明INDEX index_name(index_col),MySQL也会改成KEY index_name(index_cols),但它只是普通索引,没有任何约束条件。

下面来说说则几个索引:

1、主键索引(PRIMARY KEY)

建议每张表都有一个主键(InnoDB表必须要有主键),主键通常使用无符号整型自增。自增的步长默认是1,如果你有多个写库,可以通过改变步长来解决主键冲突问题。
整型选择也是越小越好,例如,你的表最多不超过255条记录,你可以这么设计:

CREATE TABLE `xxxx` (
	`id` tinyint UNSIGNED NOT NULL AUTO_INCREMENT,
	PRIMARY KEY (`id`),
) ENGINE=InnoDB ...;

如果你的表记录绝对不会不超过65535,你可以这么设计:

CREATE TABLE `xxxx` (
	`id` smallint UNSIGNED NOT NULL AUTO_INCREMENT,
	PRIMARY KEY (`id`),
) ENGINE=InnoDB ...;

关于主键为什么建议使用整型,上面在介绍InnoDB和MyISAM时已经说了,这里就不在赘述。

2、唯一索引(UNIQUE KEY)

这个就没什么好说的了,当表中一个或多个字段需要做唯一限制时,就需要为这一个字段或多个字段创建唯一索引UNIQUE KEY key_name (key_col)

3、普通索引(INDEX)

普通索引使用的最多,通常出现在一些子句中。例如where子句、order by子句、group by子句等。
给普通索引指定长度就会变成前缀索引,例如INDEX index_name( index_col(5) ),MySQL就会利用该字段前五个字符创建索引。
如果一个字段过长,而前n个字符可以肯定不相同,就可以使用前缀索引INDEX index_name( index_col(n) ),来减小索引大小、提高查询效率。

普通索引并不是每个字段都适合创建区分度较小的字段通常不需要建索引,例如状态字段,一般状态只有几个,而记录数非常多,所以区分度接近与零,没有必要建索引。

外键字段也需要创建普通索引,在实际项目中,极少使用外键约束,一般给外键字段创建一个普通索引,在业务代码中维持外键约束。

where子句、order by子句、group by子句等子句中出现字段通常需要创建索引,有的需要创建复合索引。要注意复合索引是使用最左前缀来查找行,所以复合索引左边的前n字段就不用创建复合索引了。
比如有a,b,c,d四列创建复合索引index a_b_c_d(a,b,c,d)
那么就没必要创建诸如index a(a)index a_b(a,b)index a_b_c(a,b,c)这类复合a_b_c_d前缀的索引了,因为无论是a还是a_b都满足a_b_c_d的最左前缀,所以会使用a_b_c_d索引。
例如以下语句就会使用a_b_c_d索引:

select * from table_name where a=val1;
select * from table_name where a=val1 and b=val2;
select * from table_name where a=val1 and b=val2 and c=val3;
select * from table_name where a=val1 and b=val2 and c=val3 and d=val4;

但是以下语句就不会使用a_b_c_d索引:

select * from table_name where a=val1 and c=val2;
select * from table_name where b=val1 and c=val2;
select * from table_name where b=val1 and c=val2 and d=val3;
...

因为这些语句的where子句不满足a_b_c_d索引的最左匹配,所以不会使用a_b_c_d索引。
所以设计索引时,要避免设计这些浪费性能而无效的索引。
另外单个索引名称建议使用字段名,复合索引名称建议使用字段名加下划线,按索引字段的顺序命名,这样比较直观的知道这条索引包含的列以及顺序情况。
还有个要注意,主键索引和唯一索引都是索引,无需再为其创建一个普通索引了。

我之前见过一个这么设计的,差点没吐血:

create table table_name(
id varchar(20),
...
PRIMARY KEY (`id`),
index id(id)
)...;

要注意,上面时错误的设计

4、全文索引(FULLTEXT)

我们都知道只要要检索的索引字段左边不使用’%’、’_'等通配符就会使用索引。
例如以下语句就会使用索引:

select * from table_name where col_name like 'world%';
select * from table_name where col_name like 'world_bb%';
...

但是以下语句就不会使用索引:

select * from table_name where col_name like '%world%';
...

那么要想快速检索出包含world的列,就需要使用全文索引了。
但是MySQL提供的全文索引只支持英文分词,不支持中文分词。中文分词可以使用coreseek。
coreseek是在sphinx的基础上,增加了中文分词功能,所以可以用来做中文的全文索引。直接编译进mysql中性能更强。
也有很多使用ElasticSearch做全文搜索服务,它是分布式的全文搜索引擎,性能更强,速度更快。

索引也就介绍这儿多了,还是要强调一下:
索引并不是越多越好,不要创建无用的索引,维护索引的开销是很大的

三、总结

1、如果你清楚要设计的表的用处即常用操作,可以根据各存储引擎的特点来进行选择,如果你不清楚,那就选InnoDB。

2、表字段设计尽可能的小

3、根据业务查询特点,设计合理的索引

关于表设计基本说完了,以上是本人的一些总结,水平有限,难免有错误疏漏之处,欢迎指正。后续如果还想到别的再来补充。