没有特殊要求(即 Innodb 无法满足的功能如:列存储,存储空间数据等)的情况下,所有表必须使用 Innodb 存储引擎(MySQL5.5 之前默认使用 Myisam,5.6 以后默认的为 Innodb)。
Innodb 支持事务,支持行级锁,更好的恢复性,高并发下性能更好。
兼容性更好,统一字符集可以避免由于字符集转换产生的乱码,不同的字符集进行比较前需要进行转换会造成索引失效,如果数据库中有存储 emoji 表情的需要,字符集需要采用 utf8mb4 字符集。
使用 comment 从句添加表和列的备注,从一开始就进行数据字典的维护
可以用历史数据归档(应用于日志数据),分库分表(应用于业务数据)等手段来控制数据量大小
分区表在物理上表现为多个文件,在逻辑上表现为一个表;
谨慎选择分区键,跨分区查询效率可能更低;
建议采用物理分表的方式管理大数据。
MySQL 限制每个表最多存储 4096 列,并且每一行数据的大小不能超过 65535 字节。
减少磁盘 IO,保证热数据的内存缓存命中率(表越宽,把表装载进内存缓冲池时所占用的内存也就越大,也会消耗更多的 IO);
更有效的利用缓存,避免读入无用的冷数据;
经常一起使用的列放到一个表中(避免更多的关联操作)。
预留字段的命名很难做到见名识义。
预留字段无法确认存储的数据类型,所以无法选择合适的类型。
对预留字段类型的修改,会对表进行锁定。
通常文件很大,会短时间内造成数据量快速增长,数据库进行数据库读取时,通常会进行大量的随机 IO 操作,文件很大时,IO 操作很耗时。
通常存储于文件服务器,数据库只存储文件地址信息
比如将字符串转化为数字类型存储(mysql提供了如下两个函数):
INET_ATON('255.255.255.255')=4294967295
INET_NTOA(4294967295)='255.255.255.255'
可以看出,如果使用字符串来存储的话至少需要15个字节。而转换为Int类型后,则只需要4个字节。
a.将字符串转换成数字类型存储,如:将 IP 地址转换成整形数据
MySQL 提供了两个方法来处理 ip 地址
inet_aton 把 ip 转为无符号整型 (4-8 位)
inet_ntoa 把整型的 ip 转为地址
插入数据前,先用 inet_aton 把 ip 地址转为整型,可以节省空间,显示数据时,使用 inet_ntoa 把整型的 ip 地址转为地址显示即可。
b.对于非负型的数据 (如自增 ID,整型 IP) 来说,要优先使用无符号整型来存储
另外需要注意的是:
Varchar(N)中的N代表的是字符数,而不是字节数。比如使用UTF8存储汉字Varchar(255)=765个字节,可以存储255个汉字但是需要765个字节。
过大的长度会消耗更多的内存,虽然Varchar类是可变长度的字符类,虽然存储的时候是按照实际长度存储的,但是读取到内存的时候,为了提高效率是按照字段定义的长度来分配内存的。
以最常见的TEXT类型为例,可以存储64k的数据,而很少会存储到64k,所以对于这类数据来说,一般使用varchar类型就足够了。
另外由于mysql的内存表是不支持TEXT和BLOB这种大的数据类型的。如果我们查询中包括这样的数据类型的话,进行排序操作的时候就不能使用内存表,而是使用磁盘表。而且对于这类数据mysql在读取的时候会进行二次查询,所以会对sql性能变得很差。
如果一定使用这两种类型,不要使用select *。
对枚举类型进行修改的时候,需要使用ALTER语句,而且会使用元数据锁,存在一定的操作风险。
而且枚举类型的ORDER BY操作效率低,需要转化为字符串类型,然后才进行排序,这样是无法使用索引的。
最后,要禁止使用数值作为ENUM的枚举值,因为枚举在数据库内部存储实质就是整数,容易造成逻辑上的混淆。如果枚举值是整型的话,直接使用tyny int之类的数据类型就好了。
对于可能为空的列,在这样的列上增加索引NULL列需要额外的空间来保存(来存储列是否为空或者非空),所以要占用更多的空间。前面说过,对于索引占用空间越少越好。
另外,进行计算和比较的时候,NULL值会做特别的处理,这也可能使得索引失效。
比如:应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
使用 TIMESTAMP(4 个字节) 或 DATETIME 类型 (8 个字节) 存储时间。
使用字符串类型:
非精准的浮点包括float和double类型。
精准的浮点包括decimal类型。
一般财务相关的金额类数据,必须使用decimal类型,decimal类型占用空间由其定义的宽度决定,每4个字节可以存储9位数字,并且小数点也会占用一个字节。
另外decimal类型可以用于存储比bigint更大的整数数据,比使用varchar类型来存储更加的高效。
使用Innodb表的时候,它是按照主键来指导索引的。一般来说,每个Innodb表必须要有一个主键,如果没有明确指定主键的话,Innodb表会优先选择表中的第一个非空唯一索引来作为主键,如果连非空唯一索引都没有,Innodb会自动生成一个6个字节的主键,但是其性能并不好。
对于Innodb表,有如下建议:
不得使用外键与级联,一切外键概念必须在应用层解决。
说明:以学生和成绩的关系为例,学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,即为级联更新。
外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风 险;外键影响数据库的插入速度。
索引并不是越多越好!索引可以提高效率同样可以降低效率。
索引可以增加查询效率,但同样也会降低插入和更新的效率,甚至有些情况下会降低查询效率。
因为 MySQL 优化器在选择如何优化查询时,会根据统一信息,对每一个可以用到的索引来进行评估,以生成出一个最好的执行计划,如果同时有很多个索引都可以用于查询,就会增加 MySQL 优化器生成执行计划的时间,同样会降低查询性能。
另外,应当避免建立冗余索引和重复索引。对于频繁的查询优先考虑使用覆盖索引。
之所以要设计索引,也就是为了通过索引来进行查找,减少磁盘的随机IO,增加查询的性能。
所以应该把区分度最高的列放在联合索引的最左侧。
其次,应该把字段长度小的列放在联合索引的左侧。
最后,使用最频繁的列放在联合索引的左侧。
5.6 版本之前,一个 sql 只能使用到一个表中的一个索引,5.6 以后,虽然有了合并索引的优化方式,但是还是远远没有使用一个联合索引的查询方式好。
(增加了查询优化器生成执行计划的时间)
重复索引示例:primary key(id)、index(id)、unique index(id)
冗余索引示例:index(a,b,c)、index(a,b)、index(a)
预编译语句只传递参数,比传递SQL语句更加高效。
相同语句可以一次解析,多次使用,能够提高处理效率。
隐式转换主要出现在where从句中,当参数类型和列类型不一致的时候,就有可能产生隐式转换。比如下面这条SQL语句。隐式转换会导致索引失效。
比如,避免使用双%
号或者前置%
的查询条件,如a like '%123%'
,或者a like '123%'
(后置是可以用到索引的)。
一个 SQL 只能利用到复合索引中的一列进行范围查询。如:有 a,b,c 列的联合索引,在查询条件中有 a 列的范围查询,则在 b,c 列上的索引将不会被用到。
再比如,使用left join
或者not exists
来优化not in
操作。因为not in
也会使得索引失效。
为数据库迁移和分库分表留出余地
降低业务耦合度
避免权限过大而产生的安全风险
select *
进行查询原因:
主要的目的也是为了减少表结构变更带来的影响。
对于分库分表后,join也不友好了。对此,可以使用两次查询,第二次使用in的方式拿到数据。
每Join一个表会占用一部分内存(join_buffer_size)。
会产生临时表操作,影响查询效率。
Mysql最多允许61个表关联,但是建议不超过5个。
比如进行分页操作的时候,如果可以一次提取100行的话,就不要分100次做。
因为数据库更适合处理批量操作。
合并多个相同的操作到一起,可以提高处理效率。
in操作可以有效的利用索引,但是in的值最好不要超过500个。
这种方式会对mysql的性能产生很大的影响,因为它会把表中所有符合条件的数据都装载到内存中进行排序,这样会消耗大量的CPU和IO及内存资源。
推荐使用在程序中获取一个随机值,然后在数据库中获取数据的方式。
对列进行函数转换和计算会导致无法使用索引。
比如下面的语句,第一条进行了函数转换无法使用到索引,所以推荐使用第二条的方式。
因为UNION操作会把所有数据放到临时表中后再进行去重操作。如果结果集太大,会消耗大量的资源。
mysql中,一个sql只能使用一个cpu进行计算,并不会进行并行处理。
如果大sql拆分为多个小sql,可以进行sql的并行处理。
因为HAVING 只会在检索出所有记录之后才对结果集进行过滤
否则将引擎放弃使用索引而进行全表扫描。
否则将导致引擎放弃使用索引而进行全表扫描
注意这里是写操作并不是查询操作,虽然数据库仍然是适合批处理的,但是大批量的写操作会造成如下的问题:
造成严重的主从延迟。在主库写完后,才会与从库进行数据同步,所以如果写操作耗时过长,会造成主从延迟过长。
binlog日志为row格式时会产生大量的日志。binlog会记录每一行数据的修改,所以我们修改的数据越多,产生的日志数据量也就会越多,日志传输和恢复的时间也就越长,这就是造成主从延迟的主要原因。
会造成严重的锁表操作,生产环境中可能导致其他应用无法连接到数据库。
同样修改表结构也会产生锁表操作,生产环境中可能导致其他应用无法连接到数据库。
使用pt-online-schema-change这个工具,能够避免修改大表产生的主从延迟,避免对表字段进行修改时进行锁表。
pt-online-schema-change实际上是复制了一个原来的表,然后将原本的表删除掉,复制的表改为原来的表名。它能够分多个批次进行。
super权限主要是进行数据库维护时使用的。
当达到最大连接数限制时,还允许1个有super权限的用户进行连接。所以,super权限只能留给DBA处理问题的账号使用。
比如,程序使用账号不允许跨库,原则上不准有drop权限。