这里记录一下目前我了解的mysql优化的一些方法。不一定是最优方法,是目前我这个阶段能了解到的最好方法。
表结构:
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`nickname` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '佚名' COMMENT 'qq昵称',
`figureurl_qq` varchar(150) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT 'https://resource.guanchao.site/uploads/userlogo/default/defaultlogo.png' COMMENT '头像路径',
`open_id` varchar(70) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '唯一标识',
`gender` varchar(5) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT '' COMMENT '性别',
`province` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT '' COMMENT '省份',
`city` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT '' COMMENT '城市',
`year` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT '' COMMENT '出生年',
`logintime` datetime(0) NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '最新登陆时间',
`regtime` datetime(0) NOT NULL COMMENT '注册时间',
`email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT '' COMMENT '邮箱(用户登录账号)',
`mini_openid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '小程序openid',
`mini_unionid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT '' COMMENT '小程序unionid',
`phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT '' COMMENT '用户手机号',
`figureurl_wx` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT '' COMMENT '微信头像链接',
`session_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '小程序用户session_key',
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '密码',
`user_login_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '一个账号一人登录标识',
`java_password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT 'java系统密码',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX ```id```(`id`) USING BTREE,
UNIQUE INDEX `mini_openid`(`mini_openid`) USING BTREE,
UNIQUE INDEX `qq_openid`(`open_id`) USING BTREE,
UNIQUE INDEX `zuhe`(`id`, `nickname`, `email`) USING BTREE,
INDEX `nickname`(`nickname`) USING BTREE,
INDEX `year`(`year`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2727 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;
1:对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
2:应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from user where year is null
| Id | Select_type | table | type | Possible_keys | key | Key_len | ref | rows | extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | user | ref | year | year | 43 | const | 1 | Using where; Using index |
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from user where year = 0
| Id | Select_type | table | type | Possible_keys | key | Key_len | ref | rows | extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | user | index | year | year | 43 | 2273 | Using where; Using index |
3:应尽量避免在 where 子句中使用!=或<>操作符,否则引擎将放弃使用索引而进行全表扫描。
SELECT id,nickname FROM user WHERE city != 3
| Id | Select_type | table | type | Possible_keys | key | Key_len | ref | rows | extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | user | ALL | 2273 | Using where |
4: 应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描:
SELECT id,nickname FROM user where city = 'camellia' OR nickname = '李方亮'
| Id | Select_type | table | type | Possible_keys | key | Key_len | ref | rows | extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | user | ALL | nickname | 2273 | Using where |
应该像下边这么写:
explain
(SELECT id,nickname FROM user where city = 'camellia' )
UNION ALL
(SELECT id,nickname FROM user where nickname = '李方亮')
| Id | Select_type | table | type | Possible_keys | key | Key_len | ref | rows | extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | PRIMARY | user | ALL | 2273 | Using where | ||||
| 2 | UNION | user | ref | nickname | nickname | 202 | const | 1 | Using where; Using index |
5:in 和 not in 也要慎用,否则会导致全表扫描,如:
SELECT id,nickname FROM user where year in (1996,1997,1998)
| Id | Select_type | table | type | Possible_keys | key | Key_len | ref | rows | extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | user | ALL | year | 2273 | Using where |
6:应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
SELECT id FROM user where year/2 = 100
| Id | Select_type | table | type | Possible_keys | key | Key_len | ref | rows | extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | user | index | year | 43 | 2273 | Using where; Using index |
应该这么写:
explain SELECT id FROM user where year = 100*2
| Id | Select_type | table | type | Possible_keys | key | Key_len | ref | rows | extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | user | index | year | year | 43 | 2273 | Using where; Using index |
7: 应尽量避免在 where 子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
explain select id from user where substring(nickname,1,3)='abc' -- name以abc开头的id
| Id | Select_type | table | type | Possible_keys | key | Key_len | ref | rows | extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | user | index | nickname | 202 | 2273 | Using where; Using index |
应该这么写:
explain select id from user where nickname like 'abc%'
| Id | Select_type | table | type | Possible_keys | key | Key_len | ref | rows | extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | user | range | nickname | nickname | 202 | 1 | Using where; Using index |
8:不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引,where 函数操作=值
explain select id from user where LENGTH(year) > 1996
| Id | Select_type | table | type | Possible_keys | key | Key_len | ref | rows | extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | user | index | year | 43 | 2273 | Using where; Using index |
把函数去掉就可以:
explain select id from user where year > 1996
| Id | Select_type | table | type | Possible_keys | key | Key_len | ref | rows | extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | user | index | year | year | 43 | 2273 | Using where; Using index |
9:在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
explain select id from user where email = 'camellia' AND nickname = 'camellia'
| Id | Select_type | table | type | Possible_keys | key | Key_len | ref | rows | extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | user | ref | nickname | nickname | 202 | const | 1 | Using index condition; Using where |
这里就没有使用到zuhe这个索引,而是是使用到了nickname索引。
10: 不要写一些没有意义的查询,如需要生成一个空表结构:
select id from user where 1 = 0
| Id | Select_type | table | type | Possible_keys | key | Key_len | ref | rows | extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | user | ref | nickname | nickname | 202 | const | 1 | Using index condition; Using where |
这类代码不会返回任何结果集,但是会消耗系统资源的
11: 很多时候用 exists 代替 in 是一个好的选择
explain select id from user where year in (select year from user)
| Id | Select_type | table | type | Possible_keys | key | Key_len | ref | rows | extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | PRIMARY | user | index | year | year | 43 | 2273 | Using index | |
| 1 | PRIMARY | eq_ref | distinct_key | distinct_key | 43 | func | 1 | ||
| 2 | MATERIALIZED | user | index | year | year | 43 | 2273 | Using index |
使用exists
explain select id from user p1 where EXISTS (select 1 from user WHERE year = p1.year)
| Id | Select_type | table | type | Possible_keys | key | Key_len | ref | rows | extra |
|---|---|---|---|---|---|---|---|---|---|
| 1 | PRIMARY | p1 | index | year | year | 43 | 2273 | Using index | |
| 1 | PRIMARY | eq_ref | distinct_key | distinct_key | 43 | func | 1 | ||
| 2 | MATERIALIZED | user | index | year | year | 43 | 2273 | Using index |
12:并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。
13:索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
14:尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
15:尽可能的使用 varchar 代替 char ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
16:任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段
17: 在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table ,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先 create table ,然后 insert 。
**
**
18:mysql中1000万条数据查询优化:
1:明确查询字段,避免使用SELECT *
2:按需查找字段,减少网络IO消耗
3:查询字段索引堵盖,通过辅助索引提升查询效率
4:借助nosql缓存数据,减少mysql数据库压力
19:偏移量越大优化方法:
1:明确查询字段,避免使用SELECT *
2:按需查找字段,减少网络IO消耗
3:查询字段索引堵盖,通过辅助索引提升查询效率
4:借助nosql缓存数据,减少mysql数据库压力
5:增加where条件(将偏移量改为id限定的方式来提升查询效率)缩减扫描条数,再limit再进行数据筛选
SELECT FROM user_login_log LIMIT 1000000,100; 这个查询慢
SELECT FROM user_login_log where id > 1000000 LIMIT 100; 这个查询快
持续更新中