1. 存储引擎
1.1 MySQL体系结构
1.2 存储引擎
存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表的,而不是基于库的
,所以存储引擎也可被称为表类型。
存储引擎是用来控制数据库如何存,如何取,如何组织的。
不同的存储引擎,索引结构不同。
1.2.1 创建存储引擎
CREATE TABLE 表名 (
字段1 字段1类型[COMMENT 字段1注释],
字段2 字段2类型[COMMENT 字段2注释],
字段3 字段3类型[COMMENT 字段3注释],
......
字段6 字段6类型[COMMENT 字段6注释] # 最后没逗号
) ENFINE = INNODB [COMMENT 表注释];
1.2.2 查看数据库支持的存储引擎
SHOW ENGINES;
1.3 存储引擎特点
1.3.1 InnoDB
- 介绍:
InnoDB是一种兼顾高可靠性和高性能的通用存储引擎,在MySQL 5.5之后,InnoDB是默认的MySQL存储引擎。
- 特点:
DML操作遵循ACID模型,支持事务
;
行级锁
,提高并发访问性能;
支持外键
FOREIGN KEY约束,保证数据的完整性和正确性。 - 文件:
xxx.ibd
: xxx代表的是文件名,innoDB引擎的每张表都会对应一个这样的表空间文件,存储该表的结构(frm、sdi)、数据和索引。
参数:innodb_file_per_table
这个文件是二进制文件,是看不懂的,想要看懂这个文件,要执行
ibd2sdi table01.ibd
InnoDB结构特点
1.3.2 MyISAM
- 介绍:
MyISAM是MySQL早期的默认存储引擎。 - 特点:
不支持事务,不支持外键。
支持表锁
,不支持行锁。
访问速度快。 - 文件
xxx.sdi: 存储表结构信息
xxx.MYD: 存储数据
xxx.MYI: 存储索引
1.3.2 MEMORY
介绍:
MEMORY引擎的表数据时存储在内存中的,由于受到硬件问题,或断电问题的影响,只能将这些表作为临时表或缓存使用。
特点:
内存存放
hash
索引(默认)
文件:
xxx.sdi: 存储表结构信息。
1.3.3 对比
特点 | InnoDB | MyISAM | MEMORY |
---|---|---|---|
存储限制 | 64TB | 有 | 有 |
事务安全 | 支持 | - | - |
锁机制 | 行锁 | 表锁 | 表锁 |
B+tree索引 | 支持 | 支持 | 支持 |
Hash索引 | - | - | 支持 |
全文索引 | 支持(5.6版本后) | 支持 | - |
空间使用 | 高 | 低 | N/A |
内存使用 | 高 | 低 | 中等 |
批量插入速度 | 低 | 高 | 高 |
支持外键 | 支持 | - | - |
1.3.4 存储引擎选择
在选择存储引擎的时候,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合。
- InnoDB:是MySQL的默认存储引擎,支持事务、外键。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询之外,还包含很多的更新、删除操作,那么InnoDB是比较合适的选择。
- MyISAM:如果应用是以读操作和插入操作为主,只有少量的更新和删除,并且对事务的完整性、并发性要求不是很高,那么选择这个存储引擎是非常适合的。
- MEMORY: 将所有数据保存在内存中,访问速度快,通常用于临时表及缓存。MEMORY的缺陷就是对表的大小有限制,太大的表无法缓存在内存中,而且无法保障数据的安全性。
1.3.5 总结
- 体系结构
连接层、服务层、引擎层、存储层 - 存储引擎特点:
InnoDB和MyISAM: 事务、外键、行锁 - 存储引擎应用
InnoDB: 存储业务系统对于事务、数据完整性要求较高的核心数据。
MyISAM: 存储业务系统的非核心业务。
2. 索引
介绍:
索引(index)是帮助MySQL高效获取数据
的数据库结构
(有序
),在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。
2.1 索引优缺点
优势 | 劣势 |
---|---|
提高数据检索的效率,降低数据库的IO成本 | 索引列也是要占据空间的 |
通过索引对数据进行排序,降低数据排序的成本,降低CPU的消耗 | 索引大大提高了查询效率,同时也降低了更新表的速度 ,如对表进行INSERT、UPDATE、DELETE时、效率降低 |
2.2 索引结构
索引结构 | 描述 |
---|---|
B+tree树 | 最常见的索引类型,大部门引擎都支持B+树索引 |
Hash索引 | 底层数据结构使用哈希表实现的,只有精确匹配索引列的查询才有效,不支持范围查询 |
R-tree索引(空间索引) | 空间索引是MyISAM 引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少 |
Full-text(全文索引) | 是一种通过建立倒排索引,快速匹配文档的方式,类似于Lucene,Solr,ES |
索引 | InnoDB | MyISAM | MEMORY |
---|---|---|---|
B+tree索引 | 支持 | 支持 | 支持 |
Hash索引 | 不支持 | 不支持 | 支持 |
R-tree索引 | 不支持 | 支持 | 不支持 |
Full-text | 5.6版本之后又支持 | 支持 | 不支持 |
我们平常说的索引,如果没有特殊指明,都是B+树结构组织的索引。
2.3 索引语法
创建索引
CREATE [UNIQUE|FULLTEXT] INDEX 索引名 ON 表名(字段名);
查询索引
SHOW INDEX FROM 表名;
删除索引
DROP INDEX 索引名 ON 表名;
例子:
# 给员工表的name创建索引
CREATE INDEX idx_emp_name ON emp(name);
## 查询员工表的全部索引
SHOW INDEX FROM emp;
# 给员工表的age和salary创建联合索引
CREATE INDEX idx_emp_age_salary ON emp(age, salary);
# 给员工表的name删除索引
DROP INDEX idx_emp_name ON emp;
2.4 SQL性能分析
2.4.1 SQL执行频率
MySQL数据库连接成功后,通过show [global|session] status命令可以提供服务器状态信息,通过如下指令,可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频率:
SHOW GLOBAL STATUS LIKE 'Com_______';
图片仅为测试数据库
2.4.2 慢查询日志
慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10s)的所有SQL语句的日志。
MySQL的慢查询日志默认没有开启,需要在MySQL的配置文件(/etc/my.cnf) (linux下)中配置。
windows下可以查找my.cnf文件
mysql --help | findstr /i "my.cnf"
#服务端基本设置
[mysqld]
#开启MySQL慢查询开关
slow_query_log = 1
#慢日志查询文件路径
slow_query_log_file = D:\dev\mysql-8.1.0-winx64\mysql-8.1.0-winx64\lib/mysql/log-file
# 设置慢查询日志的时间为2s,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志
long_query_time = 2
配置完成之后,通过以下指令重新启动MySQL服务器进行测试,查看日志文件中记录的信息/var/lib/mysql/localhost-slow.log(linux默认位置)。
查询
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'slow_query_log_file';
SHOW VARIABLES LIKE 'long_query_time';
注意:
linux实用知识
tail 命令可用于查看文件的内容,有一个常用的参数 -f 常用于查阅正在改变的日志文件。
tail -f filename 会把 filename 文件里的最尾部的内容显示在屏幕上,并且不断刷新,只要 filename 更新就可以看到最新的文件内容。
2.4.3 SQL性能分析(profile)
show profiles能够在做SQL优化时帮助我们了解时间都小号到哪里去了。通过have_profileing参数,能否看到当前MySQL是否支持profile操作:
SELECT @@HAVE_PROFILING;
默认profiling是关闭的
profile详情:
执行一系列的业务SQL操作,然后通过如下指令查看指令的执行耗时:
# 查看每一条的SQL的耗时基本情况
SHOW PROFILES;
# 查看指定Query_ID的SQL语句各个阶段的耗时情况
SHOW PROFILE FOR QUERY Query_ID;
# 查看指定Query_ID的SQL语句各CPU的使用情况
SHOW PROFILE CPU FOR QUERY Query_ID;
2.5 SQL性能分析(explain执行计划)
EXPLAIN或者DESC命令获取MySQL如何执行SELECT语句的信息,包括在SELECT语句执行过程中表如何连接和连接的顺序。
语法:
EXPLAIN SELECT 字段列表 FROM 表名 WHERE 条件;
例如:
# 1. 查询研发部和财务部的所有员工信息
EXPLAIN SELECT e.* FROM emp e WHERE e.dept_id IN (SELECT d.id FROM dept d WHERE d.name = '研发部' OR d.name = '财务部');
explain执行计划各字段含义:
- id:select查询的序号,标识查询中执行select’子句或者操作表的顺序(id相同,执行顺序从上到下;id不同,值越大,越先执行)。
- select_type::表示SELECT的类型,常见的取值有SIMPLE(简单表,即不使用表连接或子查询)、PRIMARY(主查询,即外层的查询)、UNION(UNION中的第二个或者后面的查询语句)、SUBQUERY(SELECT/WHERE之后包含了子查询)等。
- type::表示连接类型,性能由好到差的连接类型为NULL、system、const、eq_ref、ref、range、index、all。
- possible_key:表示可能应用在这张表上的索引,一个或多个。
- key:实际使用的索引,如果为NULL,则没有使用索引。
例子:
EXPLAIN SELECT * FROM emp WHERE name = '黎明';
- key_len:表示索引中的使用字段,该值为索引最大可能长度,并非实际使用长度,在不损失精确性的前提下,长度越短越好。
- rows:MySQL认为必须要执行查询的行数,在innodb引擎中,是一个估计值,可能并不是准确的。
- filtered:表示返回结果的行数占需读取行数的
百分比
,filtered的值越大越好。
2.6 索引失效情况
2.6.1 最左前缀法则
CREATE INDEX idx_emp_name_age_salary ON emp(name, age, salary);
SHOW INDEX FROM emp;
EXPLAIN SELECT * FROM emp WHERE name = '黎明1' AND age = 66 AND salary = 50000; # key(实际用到的索引)为idx_emp_name_age_salary key_len为92
EXPLAIN SELECT * FROM emp WHERE name = '黎明1' AND age = 66; # key(实际用到的索引)为idx_emp_name_age_salary key_len为87
EXPLAIN SELECT * FROM emp WHERE name = '黎明1'; # key(实际用到的索引)为idx_emp_name_age_salary key_len为82
EXPLAIN SELECT * FROM emp WHERE age = 66 AND salary = 50000; # key(实际用到的索引)NULL
EXPLAIN SELECT * FROM emp WHERE salary = 50000; # key(实际用到的索引)NULL
EXPLAIN SELECT * FROM emp WHERE name = '黎明1' AND salary = 50000; # key(实际用到的索引)为idx_emp_name_age_salary key_len为82, name后的索引失效,所以为82
如果索引了多列(联合索引),要遵守最左前缀法则。最左前缀法则值得是查询从索引的最左列开始,并且不跳过索引中的列。
如果条约某一列,索引将部分失效(后面的字段索引失效)
。
2.6.2 范围查询
EXPLAIN SELECT * FROM emp WHERE name = '黎明1' AND age = 66 AND salary = 50000; # key(实际用到的索引)为idx_emp_name_age_salary key_len为92
EXPLAIN SELECT * FROM emp WHERE name = '黎明1' AND age > 66 AND salary = 50000; # key(实际用到的索引)为idx_emp_name_age_salary key_len为87
# age后面的列失效,索引索引长度职位name和age的,没有salary
EXPLAIN SELECT * FROM emp WHERE name = '黎明1' AND age >= 66 AND salary = 50000; # key(实际用到的索引)为idx_emp_name_age_salary key_len为92
# 针对非联合索引
联合索引中,出现范围查询(>,<),范围查询右侧的索引列失效
。
所以一般为>=, <=
2.6.3 索引列计算
CREATE INDEX idx_emp_name ON emp(name);
DROP INDEX idx_emp_name_age_salary ON emp;
EXPLAIN SELECT * FROM emp WHERE name = 'XXX'; # key(实际用到的索引)为idx_emp_name key_len为82
EXPLAIN SELECT * FROM emp WHERE SUBSTRING(name, 1, 6) = 'XXX'; # key(实际用到的索引)为NULL 针对非联合索引
不要在索引列上进行运算操作,索引将失效
。
2.6.4 非单
CREATE INDEX idx_emp_name ON emp(name);
EXPLAIN SELECT * FROM emp WHERE name = 'XXX'; # key(实际用到的索引)为idx_emp_name key_len为82
EXPLAIN SELECT * FROM emp WHERE name = 111; # key(实际用到的索引)为NULL 针对非联合索引
字符串不加单引号
,造成索引失效
。
2.6.4 前模糊
CREATE INDEX idx_emp_name ON emp(name);
EXPLAIN SELECT * FROM emp WHERE name LIKE 'XXX%'; # key(实际用到的索引)为idx_emp_name key_len为82
EXPLAIN SELECT * FROM emp WHERE name LIKE '%XXX'; # key(实际用到的索引)为NULL 针对非联合索引
前模糊,索引会失效
。
2.6.4 or连接的条件
CREATE INDEX idx_emp_name ON emp(name);
SHOW INDEX FROM emp;
EXPLAIN SELECT * FROM emp WHERE name = 'XXX' OR id = 1; # key(实际用到的索引)为idx_emp_name,PRIMARY key_len为82,4
EXPLAIN SELECT * FROM emp WHERE name = 'XXX' OR salary = 50000; # key(实际用到的索引)为NULL OR针对非联合索引
用or分割开的条件,如果or前的条件中的列有索引,而后面的列没有索引,那么涉及到的索引都不会被用到。
2.6.5 数据分布
如果MySQL评估使用索引比全表更慢,则不使用索引。
2.7 SQL提示
SQL提示:是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的。
# USE INDEX
EXPLAIN SELECT * FROM emp USE INDEX (idx_emp_name) WHERE name = '黎明1';
# FORCE INDEX
EXPLAIN SELECT * FROM emp FORCE INDEX (idx_emp_name) WHERE name = '黎明1';
# IGNORE INDEX
EXPLAIN SELECT * FROM emp IGNORE INDEX (idx_emp_name) WHERE name = '黎明1';
2.8 覆盖索引
尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到),减少select;
CREATE INDEX idx_emp_name_job_salary ON emp(name, job, salary);
EXPLAIN SELECT name, job, salary FROM emp WHERE name = '黎明1' AND job = 'xxx' AND salary = 50000; # 不需回表
EXPLAIN SELECT name, job, salary, entrydate FROM emp WHERE name = '黎明1' AND job = 'xxx' AND salary = 50000; #需回
using index condition:查找使用了索引,但是需要回表查询数据
using where; using index; :查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据。
聚集索引叶子结点挂的是row,非聚集索引挂的是id。
例子:
一张表,有四个字段(id, username, password, status),由于数据量大,需要对SQL语句进行优化,该如何进行才是最优方案?
CREATE INDEX idx_表_username_password ON 表(username, password);
建立联合索引。
2.9 索引使用
2.9.1 前缀索引
当字段类型为字符串(varchar、text等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO,影响查询的效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。
语法:
CREATE INDEX idx_xxx ON table_name(column(n));
索引长度:
可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1
,这是最好的索引选择性,性能也是最好的。
SELECT COUNT(DISTINCT email) / COUNT(*) FROM email_test;
SELECT COUNT(DISTINCT SUBSTRING(email, 1, 6)) / COUNT(*) FROM email_test;
例子:
CREATE TABLE email_test(
id INT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(20)
);
INSERT INTO email_test VALUES (1, 'caocao@qq.com'), (2, '1777999@qq.com'), (3, '1777990@qq.com'),(4, 'lisi@qq.com'),(5, 'wangwu@163.com'),(6, '1777999@q163.com'),(7, '657389530@qq.com'),(8, '5353636363@qq.com'),(9, 'gbfbfd@163.com'),(10, '177799559@qq.com');
SELECT COUNT(DISTINCT email) / COUNT(*) FROM email_test;
SELECT COUNT(DISTINCT SUBSTRING(email, 1, 2)) / COUNT(*) FROM email_test;
COUNT(DISTINCT email) / COUNT(*)为1
COUNT(DISTINCT SUBSTRING(email, 1, 2)) 为0.7
COUNT(DISTINCT SUBSTRING(email, 1, 3)) 为0.7
COUNT(DISTINCT SUBSTRING(email, 1, 4)) 为0.7
所以选择前缀索引长度为2
CREATE INDEX idx_email_test_2 ON email_test(email(2));
2.9.2 单列索引和联合索引
单列索引:即一个索引只包含了一个列。
联合索引: 即一个索引包含了多个列。
在业务场景中,如果存在多个查询条件,考虑针对查询字段建立索引时,建议建立联合索引
,而非单列索引。
2.9.3 索引设计原则
- 针对数据量较大,且查询比较频繁的表建立索引。
- 针对常作为查询条件(where)、排序(order by)、分组(group by)的字段建立索引。
- 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。
- 如果是字符串类型的字段,字段的长度较长,可以针对字段的特点,建立前缀索引。
- 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。
- 要控制索引的数量,索引并不是多多益善,索引越多,维护索引的代价也就越大,会影响增删改的效率。
- 如果索引不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地应用于查询。
3. SQL优化
3.1 insert插入
- 批量插入:
INSERT INTO email_test VALUES (1, 'caocao@qq.com'), (2, '1777999@qq.com'), (3, '1777990@qq.com'),(4, 'lisi@qq.com'),(5, 'wangwu@163.com'),(6, '1777999@q163.com'),(7, '657389530@qq.com'),(8, '5353636363@qq.com'),(9, 'gbfbfd@163.com'),(10, '177799559@qq.com');
- 手动提交事务
START TRANSACTION;
INSERT INTO email_test VALUES(11, 'xxx1@qq.com');
INSERT INTO email_test VALUES(12, 'xxx2@qq.com');
INSERT INTO email_test VALUES(13, 'xxx3@qq.com');
COMMIT;
- 主键顺序插入
主键乱序插入:8 1 9 21 88 2 6 89 7 10
主键顺序插入:1 2 8 10 28 30 31 60 70 90
大批量插入数据: 如果要一次性插入大批量数据,使用insert语句插入性能较低,此时可以使用MySQL数据库提供的Load指令进行插入。
# 客户端连接服务器时, 加上参数--local-infile
mysql --local-infile -uroot -p
# 设置全局参数local_infile为1,开启从本地加载文件导入数据的开关
SET GLOBAL LOCAL_INFILE = 1;
# 指令load指令将准备好的数据,加载到表结构中
LOAD DATA LOCAL INFILE 'D:\\aaa.log' INTO TABLE email_test FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';
SELECT @@LOCAL_INFILE;
3.2 主键插入
主键设计原则:
- 满足业务需求的前提下,尽量降低主键的长度。
- 插入数据时,尽量选择顺序插入,使用AUTO_INCREMENT自增主键。
- 尽量不要使用UUID做主键或者是其他自然主键,比如身份证号。
- 业务操作时,避免对主键的修改。
3.3 order by优化
- Using filesort:通过表的索引或者全表扫描,读取满足的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫FileSort排序。
- Using index:通过有序索引顺序扫描直接返回数据,这种情况叫using index,不需要额外排序,操作效率高。
# order by优化
# 没有创建索引时,根据age,job进行排序
EXPLAIN SELECT id, age, job FROM emp ORDER BY age, job; #Using filesort
# 创建索引
CREATE INDEX idx_age_job ON emp(age,job);
# 创建索引后,根据age,job进行升序排序
EXPLAIN SELECT id, age, job FROM emp ORDER BY age, job; #Using index
# 创建索引后,根据age,job进行降序排序
EXPLAIN SELECT id, age, job FROM emp ORDER BY age DESC, job DESC; #Backward index scan; Using index
EXPLAIN SELECT id, age, job FROM emp ORDER BY job, age; #Using index; Using filesort job在age前
EXPLAIN SELECT id, age, job FROM emp ORDER BY age ASC , job DESC; #Using index; Using filesort
优化根据age,job一个升序,一个降序
CREATE INDEX idx_emp_age_job_ad ON emp(age ASC , job DESC);
EXPLAIN SELECT id, age, job FROM emp ORDER BY age ASC , job DESC; #Using index
优化准则:
- 根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则。
- 尽量使用覆盖索引。
- 多字段排序,一个升序一个降序,此时需要注意联合索引在创建时的规则(ASC/DESC)。
- 如果不可避免的出现filesort,大数据量排序时,可以适当增加缓冲区大小sort_buffer_size(默认256k)。
3.4 group by优化
# 删除不必要的索引
# 1. 根据age进行分组
EXPLAIN SELECT age, COUNT(*) FROM emp GROUP BY age; #Using temporary
# 2. 创建索引
CREATE INDEX idx_age_job_salary ON emp(age, job, salary);
# 有索引后,根据age进行分组
EXPLAIN SELECT age, COUNT(*) FROM emp GROUP BY age; #Using index
# 有索引后,根据age,job 分组
EXPLAIN SELECT age, job, COUNT(*) FROM emp GROUP BY age, job; #Using index
EXPLAIN SELECT job, COUNT(*) FROM emp GROUP BY job ; # Using index; Using temporary
EXPLAIN SELECT job, COUNT(*) FROM emp WHERE age = 22 GROUP BY job; # Using index
- 在分组操作时,可以通过索引来提高效率。
- 分组操作时,索引的使用也是满足最左前缀法则。
3.5 limit优化
一个常见又头疼的问题就是limit 2000000, 10,此时需要MySQL排序前2000010条数据,仅仅返回2000000-2000010的记录,其他记录丢弃,查询排序的代价非常大。
SELECT * FROM emp ORDER BY id LIMIT 2000000, 10;
SELECT * FROM emp WHERE id IN (SELECT * FROM emp ORDER BY id LIMIT 2000000, 10); #[42000][1235] This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
# 上面的不行,所以只能使用内连接了
SELECT e1.* FROM emp e1, (SELECT * FROM emp ORDER BY id LIMIT 2000000, 10) e2 WHERE e1.id = e2.id;
优化思路:一般分页查询时,通过创建覆盖索引能够比较地提高性能,可以通过覆盖索引加子查询
形式进行优化。
3.6 count优化
EXPLAIN SELECT COUNT(*) FROM emp;
MyISAM:引擎把一个表的总行数存在了磁盘上,因此执行count(*)
的时候会直接返回这个数,效率很高。
InnoDB:引擎就麻烦了,它执行count(*)
的时候,需要把数据一行一行地从引擎里读出来,然后累积计数。
优化思路:自己计数
count的几种用法:
- count(主键)
InnoDB引擎会遍历整张表,把每一行的主键id值都取出来
,返回给服务层。服务层拿到主键后,直接按行进行累加(主键不可能为null)。 - count(字段)
没有not null约束: InnoDB引擎会遍历整张表把每一行的字段值取出来
,返回给服务层,服务层判断是否为null,不为null,计数累加。
有not null约束:InnoDB引擎会遍历整张表把每一行的字段值都取出来
,返回给服务层,直接按行进行累加。 - count(1)
InnoDB引擎遍历整张表,但不取值
。服务层对于返回的每一行,放一个数字“1”进去,直接按行进行累加。 - count(*)
InnoDB引擎并不会把全部字段取出来,而是专门做了优化,不取值
,服务层直接按行进行累加。
按照效率排序的话, count(字段) < count(主键id) < count(1) < count(*)。所以进行使用count(*)