目录
一、存储引擎
二、索引
索引结构
索引分类
索引语法
联合索引
前缀索引
索引使用规则
最左前缀法则
范围查询使索引失效
字段做运算操作索引失效
字符串字段不加单引号索引失效
字段做前模糊查询索引失效
or连接条件索引失效
数据发布情况索引失效
指定使用索引
三、帮助 SQL优化的工具
慢查询日志记录
profiles
explain
四、SQL优化
为什么不要使用select *
插入优化 insert
主键优化
排序优化 order by
分组优化group by
limit优化
count优化
update优化
一、存储引擎
MySQL体系结构:
查询当前数据库支持的存储引擎:
show engines ;
InnoDB是默认的存储引擎,也是最常用的,广泛应用于各种需要高并发和高可靠性的应用场景(支持事务、行级锁和外键)
InnoDB引擎的数据文件:
xxx.ibd:xxx代表的是表名,innoDB引擎的每张表都会对应这样一个表空间文件,存储该表的表结构、数据和索引。
- 表空间 : InnoDB存储引擎逻辑结构的最高层,ibd文件其实就是表空间文件,在表空间中可以 包含多个Segment段。
- 段 : 表空间是由各个段组成的, 常见的段有数据段、索引段、回滚段等。InnoDB中对于段的管 理,都是引擎自身完成,不需要人为对其控制,一个段中包含多个区。
- 区 : 区是表空间的单元结构,每个区的大小为1M。 默认情况下, InnoDB存储引擎页大小为 16K, 即一个区中一共有64个连续的页。
- 页 : 页是组成区的最小单元,页也是InnoDB 存储引擎磁盘管理的最小单元,每个页的大小默 认为 16KB。为了保证页的连续性,InnoDB 存储引擎每次从磁盘申请 4-5 个区。
- 行 : InnoDB 存储引擎是面向行的,也就是说数据是按行进行存放的,在每一行中除了定义表时 所指定的字段以外
创建表时,没有指定存储引擎,默认是InnoDB存储引擎。查看指定表使用的存储引擎等信息:
show create table emp;
MyISAM是MySQL早期的默认存储引擎。(不支持事务,不支持外键 支持表锁,不支持行锁 访问速度快)
储存的文件 xxx.sdi:存储表结构信息 xxx.MYD: 存储数据 xxx.MYI: 存储索引
Memory引擎的表数据时存储在内存中的,由于受到硬件问题或断电问题的影响,只能将这些表作为临时表或缓存使用
在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据 实际情况选择多种存储引擎进行组合。
- InnoDB: 是Mysql的默认存储引擎,支持事务、外键。如果应用对事务的完整性有比较高的要 求,在并发条件下要求数据的一致性,数据操作除了插入和查询之外,还包含很多的更新、删除操 作,那么InnoDB存储引擎是比较合适的选择。
- MyISAM : 如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完 整性、并发性要求不是很高,那么选择这个存储引擎是非常合适的。
- MEMORY:将所有数据保存在内存中,访问速度快,通常用于临时表及缓存。MEMORY的缺陷就是 对表的大小有限制,太大的表无法缓存在内存中,而且无法保障数据的安全性。
二、索引
索引是一种用于提高数据库查询效率的数据结构。通过为表中的数据创建索引,即利用数据结构为数据创建一种映射关系,可以显著减少查询所需的时间,从而提高数据库的整体性能
索引结构
MySQL的索引是在存储引擎层实现的,不同的存储引擎有不同的索引结构,索引主要有以下几种:
索引结构 | 描述 |
B+Tree索引 | 最常见的索引类型,大部分引擎都支持 B+ 树索引 |
Hash索引 | 底层数据结构是用哈希表实现的, 只有精确匹配索引列的查询才有效, 不 支持范围查询 |
R-tree(空间索 引) | 空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据类 型,通常使用较少 |
Full-text(全文 索引) | 是一种通过建立倒排索引,快速匹配文档的方式。类似于Elasticsearch |
B Tree的结构:
对于二叉树而言,最多只能有两个孩子节点,(度数最多为 2),但B树可以有多个孩子节点
最多有 n 个节点,那么就称为 n 阶B树,以 5 阶B树为例:
那这个B树每个节点最多存储4个key,5 个指针,即每个节点最多存储4个值和指向孩子节点的5个指针
查数据时,与二叉搜索树类似,比插入值大的走右边,小的走左边,以下5阶B树插入数值19为例,其结构示意图:
key的数量为5超过了4,需要分裂,中间元素上移。
B+Tree索引
B+Tree是B-Tree的变种,在分裂上移时,只是将key值赋值一份上移,值仍然存储在最下面的叶子节点上,也就是说,数据只存储在叶子节点 上,其它节点储存的是索引(key),在Mysql的B+Tree索引中,所有的叶子节点会形成双向链表,这样就可以做范围查询和排序等操作
为什么InnoDB存储引擎选择使用B+tree索引结构?
A. 相对于二叉树,层级更少,搜索效率高;
B. 对于B-tree,无论是叶子节点还是非叶子节点,都会保存数据,这样导致一页中存储 的键值减少,指针跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低;
C. 相对Hash索引,B+tree支持范围匹配及排序操作;
MySQL中除了支持B+Tree索引,还支持一种索引类型---Hash索引。
Hash索引
将数据库中每一行数据有对应的哈希值,当我们对name字段添加索引时,就会为name所有值生成对应的hash值,映射到对应的槽位上,然后将name值和每一行得hash值存储在 hash表中,如果两个(或多个)键值,映射到一个相同的槽位上,他们就产生了hash冲突(也称为hash碰撞),可以通过链表来解决,也就是相同hash值得使用链表储存。
- Hash索引只能用于对等比较(=,in),不支持范围查询(between,>,< ,...)
- 无法利用索引完成排序操作
- 查询效率高,通常(不存在hash冲突的情况)只需要一次检索就可以了,效率通常要高于B+tree索 引
索引分类
在MySQL数据库,将索引的具体类型主要分为以下几类:
主键索引(PRIMARY)、唯一索引(UNIQUE)、常规索引、全文索引
其中主键索引只能有一个其它可以有多个
主键索引(PRIMARY)、唯一索引(UNIQUE)在建表时指定主键或唯一约束时会自动创建索引
常规索引为对特定数据创建索引
聚集索引、二级索引
在InnoDB存储引擎中,根据索引的存储形式,又可以分为以下两种:
- 聚集索引(Clustered Index):将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据(必须有,而且只 有一个)
- 二级索引(Secondary Index):(辅助索引)将数据与索引分开存储,索引结构的叶子节点存放的值是对应的主键和索引列的值
聚集索引选取规则:
如果存在主键,主键索引就是聚集索引。 如果不存在主键,将使用第一个唯一(UNIQUE)索引作为聚集索引。 如果表没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索 引。
在查询二级索引中的数据时,会根据创建的索引去查询对应的主键,再根据主键去查询对应的整行数据,例如以下SQL:
select * from emp where name='Arm' ;
查询过程是:实现在二级索引中查询对应的主键id,再根据主键id去聚集索引去查询对应的行数据
而下面的SQL则是直接从聚集索引中查询整条数据,效率会更高些:
select * from emp where id= 10 ;
回表查询: 这种先到二级索引中查找数据,找到主键值,然后再到聚集索引中根据主键值,获取 数据的方式,就称之为回表查询。
假设: 一行数据大小为1k,一页中可以存储16行这样的数据,即一页(一个叶子节点)可以存放16K的数据
InnoDB的指针占用6个字节的空间,主键假使为bigint,占用字节数为8。一个节点中指针数量永远比主键数量多一,所以:
高度为2: (n + 1) * 6 + n * 8 = 16*1024 , 算出n约为 1170,即指针有1171个,所以叶子节点有1171个,又每个叶子节点可以存储16行数据,所以:
1171 * 16 = 18736
即高度为2的树可以存储 18000 多条记录。
高度为3: 1171 * 1171 * 16 = 21939856,因为1170个节点中,每个节点又可以存储1171个子节点,也就是说,如果树的高度为3,则可以存储 2200w 左右的记录。
阿里巴巴 Java 开发手册V1.2中:
14. 【推荐】单表行数超过500万行或者单表容量超过2GB,才推荐进行分库分表。 说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
索引语法
#查询索引
show index from tb_user;
#创建唯一索引
create unique index idx_user_phone on tb_user(phone);
#创建单列索引
create index idx_email on tb_user(email);
#创建联合索引
create index idx_profession_age_status on tb_user (profession, age, status);
联合索引
(Composite Index)是由两个或多个列组成的索引。这种索引可以提高在多个列上执行查询时的性能。使用联合使用需要遵循最左前缀法则
单列索引和联合索引的选择:
当需要多个条件查询时,建议创建联合索引。例如:
select id,username,password from tb_user where phone = '18081404110' and username = '张三' ;
若phone和username都是单列索引,只能走一个索引,所以需要去查询另一个字段,必然会走回表查询,只是可以对两字段建立联合索引避免。能使用联合索引尽量使用联合索引
前缀索引
前缀索引是指在针对某一字段创建索引时,字段类型为字符串(varchar,text,longtext等)(例如文章数据)时,该字段的内容可能会很庞大,建立的索引的体积就会非常大,浪费大量的磁盘IO, 影响查询效率。可以减少索引的存储空间和维护成本
语法:通过截取该字段的前一部分来做为索引的值
#以email字段的前五个字符创建索引
create index idx_content on tb_user(content(5));
前缀长度
可以根据不重复的索引值(基数)和数据表的记录总数的比值来确定(也称选择性), 比值越高则查询效率越高, 唯一索引的选择性是 1,这是最好的索引选择性,性能也是最好的。
比值查询示例:
select count(distinct content) / count(*) from tb_user ;
select count(distinct substring(content,1,5)) / count(*) from tb_user ;
查询流程:先通过前缀去索引中查,在通过id回表查询查出该字段的整体值,比对是否与给出条件符合。
索引设计原则
- 1). 针对于数据量较大,且查询比较频繁的表建立索引。
- 2). 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索 引。
- 3). 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。
- 4). 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。
- 5). 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间, 避免回表,提高查询效率。
- 6). 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增 删改的效率。
- 7). 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含 NULL值时,它可以更好地确定哪个索引最有效地用于查询。
索引使用规则
最左前缀法则
在使用联合索引时应注意遵守的法则:
最左前缀法则:查询必须需包含的最左列, 并且不跳过索引中的列。如果跳跃某一列,索引将会部分失效(后面的字段索引失效)。
例如:以tb_user表的profession, age, status三个字段创建联合索引为例:
使用explain关键字可以显示 SQL 查询的执行计划,详细看【帮助 SQL优化的工具】
正常使用三字段时,走索引,三字段都走索引,索引长度为49:
当查询时没有最左列profession则不会使用索引查询:
当只使用profession字段,正常走索引,索引长度只有profession的长度47:
当使用前两个字段时会走索引,profession和age两字段时索引长度为49:
当中间age字段跳过,没有使用时,则此后面的字段status使用索引失效,索引长度只有profession的长度47:
范围查询使索引失效
若某字段出现范围查询小于(<)、大于(>)则该字段右侧的列索引会失效,例如age使用此范围查询时,status的索引会失效,但是使用小于等于(<=)、大于等于(>=)时不会失效
【注意】这与我们写的SQL条件先后顺序无关,例如必须需包含的最左列,是指查询条件中有索引的最左列profession即可使索引失效。SQL中的条件书写顺序无需与索引顺序一致
字段做运算操作索引失效
当我们在使用某字段索引时,若对该索引做了运算,则会使索引失效。
例如:我们对手机号phone字段创建了索引,在使用时加了字符串截取的运算,查询手机号后四位为4110的用户信息,此时会使索引失效
select * from tb_user where substring(phone,7,4) = '4110';
字符串字段不加单引号索引失效
在字符串类型的字段做条件查询时,没加单引号,虽然能查出数据,但是不会走索引
select * from tb_user where substring(phone,7,4) = 4110 ;
字段做前模糊查询索引失效
当对某字段做模糊查询时,只要%加在前面就会使索引失效,比如:查询姓名以”三“结尾的用户信息
select * from tb_user where username like '%三' ;
而以下形式就不会使索引失效:查询姓张的用户
select * from tb_user where username like '张%' ;
or连接条件索引失效
当使用or做连接条件查询时,需要or连接的条件的字段都要有索引才能使索引生效,有其中一个字段没有索引,索引都不会生效
例如:查询性别为男或则年龄为20的用户:若age有索引而gender没有索引,则索引失效
select * from tb_user where gender = '男' or age = 20 ;
数据发布情况索引失效
当我们在做查询时,绝大部分数据是满足我们的条件时,MySQL评估使用索引比全表更慢,则不使用索引,相反,Mysql评估走索引会更快才会走索引。
指定使用索引
当我们在做条件查询时,字段有多个索引时,可以指定Mysql使用哪个索引,有以下三种方式:
use 建议Mysql走指定索引,其中Mysql会做评估,有更快的索引时,不会走建议的索引
explain select * from tb_user use index(idx_profession) where profession = '英语' ;
ignore 忽略指定的索引:
explain select * from tb_user ignore index(idx_profession) where profession = '英语' ;
force 强制使用指定索引:
explain select * from tb_user force index(idx_profession) where profession = '英语' ;
三、帮助 SQL优化的工具
在MySQL中,
SHOW GLOBAL STATUS
是一个非常有用的命令,它显示了服务器级别的状态变量。这些状态变量提供了关于服务器运行时的各种统计信息和性能指标。使用这个命令可以帮助你了解服务器的当前状态,诊断问题,或者优化数据库性能
使用like关键字进行过滤(Com后面7个 _ 模糊查询Com后7个字符的状态信息):
show global status like 'Com_______';
索引主要是针对查询,当数据库查询占绝大部分比例时,可能需要考虑创建索引来优化数据库性能
慢查询日志记录
Mysql默认关闭,需要配置开启才能失效,可以通过这些记录去优化这些SQL
查询是否开启慢查询日志记录
show variables like 'slow_query_log' ;
编辑my.cnf文件,末尾追加以下内容:
sudo vim /etc/mysql/my.cnf
我的是Ubuntu系统,其它系统类似,windows中是my.ini
[mysqld]
#开启慢查询日志记录,当SQL执行时间超过指定时间时就会记录日志到/var/lib/mysql/localhost-slow.log
slow_query_log=1
#日志存放地址
slow_query_log_file = /var/log/mysql/mysql-slow.log
#指定时间
long_query_time=2
查询是否配置成功:
profiles
使用profiles查询SQL执行时间等情况:
查看当前MySQL是否支持profile操作:
SELECT @@have_profiling ;
开启profiling:
SET profiling = 1;
查询所有命令执行耗时和根据命令id查询耗时:
explain
在MySQL中,
EXPLAIN
是一个非常有用的命令,用于显示 SQL 查询的执行计划。执行计划是数据库如何执行查询的详细说明,包括它如何使用索引、表的连接顺序、预计需要扫描的行数等。通过分析执行计划,你可以优化查询性能,理解查询的工作原理。
EXPLAIN 或者 DESC命令获取 MySQL 如何执行 SELECT 语句的信息,多条数据时包括在 SELECT 语句执行过程中表如何连接和连接的顺序
使用:直接在select语句之前加上关键字 explain / desc
字段 | 含义 |
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,则没有使用索引。 |
key_len | 表示索引中使用的字节数, 该值为索引字段最大可能长度,并非实际使用长 度,在不损失精确性的前提下, 长度越短越好 。 |
rows | MySQL认为必须要执行查询的行数,在innodb引擎的表中,是一个估计值, 可能并不总是准确的。 |
filtered | 表示返回结果的行数占需读取行数的百分比, filtered 的值越大越好。 |
extra | 额外信息。这里可以显示一些额外的执行计划信息,如是否使用了临时表、是否需要进行文件排序等。 using index condition : 查找使用了索引,但需要回表查询 using where;using index: 查找使用了索引,在索引中能查到所需的数据,无需回表查询 |
四、SQL优化
为什么不要使用select *
因为 * 会包含没有创建索引的字段,此时通过索引不能直接查到信息的字段,需要通过id做回表查询,也就是说尽量使用覆盖索引(查询的字段写明,并且这些字段都可以通过走一边索引即可查询到所需的数据,无需回表查询),索引有聚集索引和二级索引两类,聚集索引包含整行数据,二级索引也就是我们对字段创建的索引,只包含该索引字段的数据和id。
以根据用户名查询用户的id例(当已经为username创建单列索引时):
select * from tb_user where username = '张三' ;
该查询会走username的索引,能走索引查出username 和 id,但是select * 还需要查询其它信息,索引还要根据id在聚集索引中查询其它信息,即回表查询。这样走了两次索引,效率就有所降低。
而使用以下SQL不用走回表查询时,效率更高:
select id from tb_user where username = '张三' ;
再以用户名查询用户id、用户名、用户密码为例:
select id,username,password from tb_user where username = '张三' ;
当我们要查询效率更高时,可以为username和password建立联合索引时,不需要走回表查询去查询其它信息,查询效率更高。
插入优化 insert
批量插入,防止频繁的执行SQL语句
insert into tb_user values
('1','张三'),
('2','李四'),
('3','王五');
手动提交事务,防止事务频繁的开启和提交
start transaction;
insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'Jerry');
insert into tb_test values(4,'Tom'),(5,'Cat'),(6,'Jerry');
insert into tb_test values(7,'Tom'),(8,'Cat'),(9,'Jerry');
commit;
主键顺序插入,性能要高于乱序插入
比如,按照主键id 1、2、3、4、5...的顺序插入
大批量数据插入
如果一次性需要插入大批量数据(比如: 几百万的记录),使用insert语句插入性能较低,此时可以使 用MySQL数据库提供的 load 指令进行插入。操作如下:
1、客户端连接服务端时,加上参数 -–local-infile
mysql –-local-infile -u root -p
2、设置全局参数local_infile为1,开启从本地加载文件导入数据的开关
set global local_infile = 1;
3、执行load指令将准备好的数据,加载到表结构中
load data local infile '/root/load_user.sql' into table tb_user fields terminated by ',' lines terminated by '\n' ;
其中 /root/load_user.sql 是大批量数据文件的路径
插入到 tb_user 表中,字段之间是 ',' 分割,每行之间是 '\n' 分割
这样插入性能高于insert,若主键是顺序插入性能高于乱序插入
主键优化
在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表
逻辑存储结构是表空间-->段-->区-->页-->行
页分裂
因为B+Tree索引中,数据都是存储在叶子节点,并且数据是按照索引字段(主键)顺序存放,当乱序插入时,会出现以下情况:
页合并
当相邻两页之间的数据只有50%时,InnoDB会判断是否可以将两页进行合并,若可以则会将该相邻两页的数据进行合并,50%可以在创建表或者创建索引时指定参数MERGE_THRESHOLD设置,默认是50%。
- 满足业务需求的情况下,尽量降低主键的长度,减小索引体积
- 插入数据时,尽量选择顺序插入,选择使用AUTO_INCREMENT自增主键。
- 尽量不要使用UUID做主键或者是其他自然主键,如身份证号。 业务操作时,避免对主键的修改。因为这些都是乱序的主键,会出现页分裂的情况
排序优化 order by
MySQL的排序,有两种方式:
Using filesort : 通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。
Using index : 通过有序索引顺序扫描直接返回有序数据,这种情况即为 using index,不需要 额外排序,操作效率高。
Using filesort
通常比使用索引排序慢得多,因为它需要在磁盘上创建临时文件并进行排序操作。这会增加 I/O 操作,从而影响查询性能。在大数据量的情况下,Using filesort
可能会显著降低查询效率。Using index的性能高,而Using filesort的性能低,我们在优化排序 操作时,尽量要优化为 Using index
当根据年龄(age)字段排序查询时,可以看出extra额外信息显示的是Using filesort
当我们对price字段创建了索引进行排序时,正常走索引,效率高,当使用select * 即需要回表查询做排序操作时,还是会另开辟空间做Using filesort 查询
当根据age倒叙排列,而B+Tree中索引中,数据是根据索引字段正序排列,反向扫描索引(Backward index scan;)
对多字段进行排序:
先对价格(price)进行排序,若价格相同,则根据评分排序:
explain select id, price, score from tb_hotel order by price, score;
针对多字段进行排序的情况需要对这多字段创建联合索引,才能让排序走索引:
这时需要注意,两字段要都是升序或都是降序才能走索引,并且必须是先根据price排序再根据score排序才会走索引,因为这是创建的联合索引,若是先根据score排序则违背了最左前缀法则,不会走索引,Collection列是A表示索引中的数据根据升序排列
若一个升序一个降序,即price升序,score降序,则根据price排序时会走索引,而price相同时根据score降序排序则不会走索引,这时若想走索引则需要再创建一个索引指定索引中的字段数据price升序,score降序:
创建索引时指定规则(ASC/DESC),默认是升序ASC。
create index idx_price_score_AD on tb_hotel(price asc,score desc);
若不得不走缓存区使用Using filesort 进行排序,在大数据量的情况下可以增加缓冲区的大小:
查看缓冲区大小:
show variables like 'sort_buffer_size';
order by优化总结:
- 根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则。
- 尽量使用覆盖索引。
- 多字段排序, 一个升序一个降序,此时需要注意联合索引在创建时的规则(ASC/DESC)。
- 如果不可避免的出现filesort,大数据量排序时,可以适当增大排序缓冲区大小 sort_buffer_size(默认256k)。
分组优化group by
需要对分组优化,同样需要对需要分组的字段添加索引,走索引进行分组查询效率更高,需要注意的是也需要满足最左前缀法则,尽量使用联合索引
没有索引走临时表,type为ALL走全表扫描效率低
建立索引后:
联合索引时不满足最左前缀法则:
有where条件时是满足最左前缀法则的,走索引:
limit优化
在数据量比较大时,如果进行limit分页查询,在查询时,越往后,分页查询效率越低。
优化方式:使用覆盖索引加子查询形式进行优化。
explain select * from
tb_sku t,
(select id from tb_sku order by id limit 2000000,10) a #将该页数据的id查询,然后对做多表查询
where t.id = a.id;
Mysql不支持 in 后加 limit 关键字
count优化
用法:count(*)、count(主键)、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(*)。
update优化
在执行update更新操作时要根据有索引的字段进行更新:
将城市为成都的都改为四川,若city字段没有创建索引,则InnoDB会对整个表tb_hotel都加锁,即表级锁,若事务还未提交,此时就不能执行该表的其它更新操作
若创建了索引,则会对该行数据添加锁,即行级锁。
update tb_hotel set city = '四川' where city = '成都';
在执行上述的SQL时, 当开启多个事务,发现行锁升级为了表锁。 导致该update语句的性能大大降低。会影响效率。
所以在执行update更新操作时,尽量使用有索引的字段做为where条件
InnoDB的行锁是针对索引加的锁,不是针对记录加的锁 ,并且该索引不能失效,否则会从行锁 升级为表锁 。
\G :SQL最后加,以键值对一列形式返回