1. InnoDB支持事务, MyISAM不支持;
2. InnoDB支持外键, 而MyISAM不支持;
3. InnoDB是聚集索引,使用B+Tree作为索引结构,数据文件是和(主键)索引绑在一起的
MyISAM是非聚集索引, 也是使用B+Tree作为索引结构, 索引和数据文件是分离的, 索引保存的是数据文件的指针, 主键索引和辅助索引是独立的
InnoDB的B+树主键索引的叶子节点就是数据文件, 辅助索引的叶子节点是主键的值; 而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针
4. InnoDB支持表、行(默认)级锁, 而MyISAM支持表级锁
InnoDB的行锁是实现在索引上的, 而不是锁在物理行记录上. 潜台词是, 如果访问没有命中索引, 也无法使用行锁, 将要退化为表锁
8、InnoDB表必须有主键(用户没有指定的话会自己找或生产一个主键), 而Myisam可以没有
9、Innodb存储文件有 .frm. ibd, 而Myisam是 .frm .MYD .MYI
Innodb:frm是表定义文件,ibd是数据文件
Myisam:frm是表定义文件,myd是数据文件,myi是索引文件
索引是帮助MySQL高效获取数据的排好序的数据结构
B-Tree
叶节点具有相同的深度,叶节点的指针为空
所有索引元素不重复
节点中的数据索引从左到右递增排列
B+Tree(B-Tree变种)
非叶子节点不存储data,只存储索引(冗余),可以放更多的索引
叶子节点包含所有索引字段
叶子节点用指针连接,提高区间访问的性能
MyISAM索引文件和数据文件是分离的(非聚集)
InnoDB索引实现(聚集)
表数据文件本身就是按B+Tree组织的一个索引结构文件
聚集索引-叶节点包含了完整的数据记录
聚集索引
mysql的innodb主键索引,如果没有主键索引就是唯一索引
InnoDB聚合索引: 索引字段在一起存储到key,按照索引排序排列
innodb联合索引示例(索引最左前缀原理)
sql执行计划 explan + sql
id ID可以如果相同认为是同一组,从上往下执行,在所有组中id越大,优先级越高,越先执行
select_type 查询类型
1)SIMPLE 简单查询,不包括子查询或UNION
2)PRIMARY 查询中包含任何复杂的子部分,最外层查询被标记为
3)SUBQUERY 在select或where里包含了子查询
4)DERIVED 在from列表中包含了子查询被标记为DERIVED(衍生),mysql会递归执行这些子查询,把结果放在临时表
5) UNION 若在第二个select出现在union后,会标记为UNION.若union包含在from子句的查询中,外层会标记为DERIVED
6)UNION RESULT 从UNION中获取select
table 这一行数据显示的表,type是null会直接走索引,不会走表,效率最好
type 从最好到最差的顺序system > const > eq_ref > ref > range > index > ALL 一般来说最少达到range,最好能达到ref
possible_keys 显示可能应用在这张表中的索引,一个或多个,查询涉及到的字段若存在索引,则也列出来,但不一定被查询实际用到
key 实际使用的索引
ken_len 索引字段的最大可能长度,并非实际长度,key_len长度越短越好
ref 表示索引的哪一列被使用,也可能是常量
rows 根据表统计信息和索引选用的情况,大致估算出找到所需记录的读取行数
Extra 其他的信息
1)Using index: 使用覆盖索引
2)Using where: 使用 where 语句来处理结果,查询的列未被索引覆盖
3)Using index condition: 查询的列不完全被索引覆盖, where条件中是一个前导列的范
围
4)Using temporary: mysql需要创建一张临时表来处理查询. 出现这种情况一般是要进行
优化的, 首先是想到用索引来优化
5)Using filesort: 将用外部排序而不是索引排序,数据较小时从内存排序,否则需要在磁盘
完成排序. 这种情况下一般也是要考虑使用索引来优化的
6)Select tables optimized away: 使用某些聚合函数(比如 max、min)来访问存在索引
的某个字段是
索引失效的情况
1.(复合索引)全值匹配我最爱
2.最佳左前缀法则(带头大哥不能死,中间兄弟不能断)
3.不在索引列上做任何操作(计算,函数,(自动or手动)类型转换),会导致索引失效而转向全表扫描
4.存储引擎不能使用索引中范围条件右边的列
5.尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select*
6.mysql在使用不等于(! =或者<>)的时候无法使用索引会导致全表扫描
7.is null,is not nul 也无法使用索引
8.like以通配符开头(“%abc.…)mysql索引失效会变成全表扫描的操作
9.字符串不加单引号索引失效
10.少用or,用它来连接时会索引失效
全值匹配我最爱(聚合索引),最左前缀要遵守;
带头大哥不能死,中间兄弟不能断;
索引列上少计算,范围之后全失效;
Like百分写最右,覆盖索引不写星;
不等空值还有or,索引失效要少用;
VAR引号不可丢,SQL高级也不难!
解决like‘%字符串%’时索引不被使用,使用覆盖索引,即建的索引和查询的字段个数顺序最好完全一致
sql优化小表驱动大表,非要大表驱动小表用exists
mysql支撑Index和FileSort两种方式排序排序,Index效率高,FileSort效率低
mysql慢查询是否开启 > show variables like 'slow_query%';
开启慢查询> set global slow_query_log='ON';
设置慢查询时间> set global long_query_time=1;
查看设置后的参数(重新建连或新开回话查看) > show variables like 'long_query_time';
mysql范围查找要是范围过大有可能不走索引,同时会出现根据效率考虑走不走索引
trace工具
> set session optimizer_trace="enabled=on",end_markers_in_json=on; -- 开启trace(以json展示)
> select * from employees where name > 'a' order by position;
> SELECT * FROM information_schema.OPTIMIZER_TRACE;
查看可能走索引的成本已经索引行数,来判断具体走的索引
优化总结:
1. MySQL支持两种方式的排序filesort和index,Using index是指MySQL扫描索引本身完成排序.index
效率高,filesort效率低
2. order by满足两种情况会使用Using index
1) order by语句使用索引最左前列
2) 使用where子句与order by子句条件列组合满足索引最左前列
3. 尽量在索引列上完成排序, 遵循索引建立(索引创建的顺序)时的最左前缀法则
4. 如果order by的条件不在索引列上, 就会产生Using filesort
5. 能用覆盖索引尽量用覆盖索引
6. group by与order by很类似, 其实质是先排序后分组, 遵照索引创建顺序的最左前缀法则.对于group
by的优化如果不需要排序的可以加上order by null禁止排序. 注意: where高于having, 能写在where中
的限定条件就不要去having限定了
filesort文件排序方式
单路排序: 是一次性取出满足条件行的所有字段,然后在sort buffer中进行排序;用trace工具可
以看到sort_mode信息里显示< sort_key, additional_fields >或者< sort_key,packed_additional_fields >
双路排序(又叫回表排序模式): 是首先根据相应的条件取出相应的排序字段和可以直接定位行
数据的行 ID,然后在 sort buffer 中进行排序,排序完后需要再次取回其它需要的字段;用trace工具
可以看到sort_mode信息里显示< sort_key, rowid >
sql分页优化
让查询尽可能的少,比如覆盖索引用回表关联查询
select * from employees e inner join (select id from employees order by name limit 90000,5) ed
on e.id = ed.id;
mysql表关联的两种方式
1. 嵌套循环连接(Nested-Loop Join(NLJ)算法 链接字段是索引
一次一行循环地从第一张表(称为驱动表,一般是小表)中读取行, 在这行数据中取到关联字段, 根据关联字段在另一张表(被驱动表,一般是大表)里取出满足条件的行, 然后取出两张表的结果合集
2. 基于块的嵌套循环连接 Block Nested-Loop Join(BNL)算法 链接字段不是索引
把驱动表的数据读入到 join_buffer 中, 然后扫描被驱动表, 把被驱动表每一行取出来跟 join_buffer 中的数据做对比
对于Join关联sql的优化
1.关联字段加索引, 让mysql做join操作时尽量选择嵌套循环连接(NLJ)算法
2.小标驱动大表, 写多表连接sql时如果明确知道哪张表是小表可以用straight_join写法固定连接驱动方式, 省去mysql优化器自己判断的时间
straight_join相当于join类似, 但能让左边的表来驱动右边的表, 能改表优化器对于联表查询的执
行顺序. 比如:select * from t2 straight_join t1 on t2.a = t1.a; 代表制定mysql选着 t2 表作为驱动表。
straight_join只适用于inner join, 并不适用于left join, right join. (因为left join,right join已经代表指
定了表的执行顺序)
in和exsits优化
原则:小表驱动大表,即小的数据集驱动大的数据集
in:当B表的数据集小于A表的数据集时,in优于exists
select * from A where id in (select id from B)
exists:当A表的数据集小于B表的数据集时,exists优于in
将主查询A的数据,放到子查询B中做条件验证,根据验证结果(true或false)来决定主查询的数据是否保留
select * from A where exists (select 1 from B where B.id = A.id)
count count不计算null值
select count(1) from employess; 遍历二级索引树,不遍历索引树的值
select count(id) from employess; mysql5.7之后走的辅助索引
select count(name) from employess; name不为空
select count(*) from employess; 走辅助索引
count(1) > count(name) == count(*) > count(id)
mysql锁
手动增加表锁 > lock table 表名称 read(write),表名称2 read(write);
查看表上加过的锁 > show open tables;
删除表锁 > unlock tables;
读锁会阻塞写, 但是不会阻塞读; 而写锁则会把读和写都阻塞
行锁支持事物: 原子性(Atomicity),一致性(Consistent),隔离性(Isolation),持久性(Durable)
并发事务处理带来的问题
更新丢失, 脏读(读其他未提交事物的数据), 不可重读(修改数据), 幻读(新增数据)
mysql事物级别默认 "不可重复读"
常看当前数据库的事务隔离级别: show variables like 'tx_isolation';
设置事务隔离级别:set tx_isolation='REPEATABLE-READ';
可串行化
间隙锁
InnoDB的行锁是针对索引加的锁, 不是针对记录加的锁. 并且该索引不能失效, 否则都会从行锁升级为表锁
mysql MVVC 为了性能和处理大数据基于快照版本
select * from account(创建了查询快照, 记录执行sql这一刻最大的已提交事务id(快照点已提交最大事务id)) 快照基于insert,update,delete
sql
-- 新建数据库 create database `dbname` default character set utf8mb4 collate utf8mb4_unicode_ci;
create database `dbname` default character set utf8 collate utf8_general_ci;
-- 新建数据库并授权: grant all privileges on `dbname`.* to 'userName'@'%' identified by 'password';
-- 刷新服务 flush privileges;
-- 创建mysql触发器没有权限(log_bin_trust_function_creators 1),root登陆到对应数据库
set global log_bin_trust_function_creators = 1;
-- 新建数据库 create database [dbname] default character set utf8 collate utf8_general_ci;
-- 新建数据库并授权: grant all privileges on 'dbname'.* to 'userName'@'%' identified by 'password';
-- 创建用户 create user 'userName'@'%' identified by 'password';
-- 用户授权数据库 grant all privileges on [dbname].* to 'userName';
-- 或 grant select,insert,update,delete,create,drop on [dbname].* to 'userName';
-- 取消用户所有数据库(表)的所有权限 revoke all on *.* from userName;
-- 删除用户 delete from mysql.user where user='userName';
-- 删除数据库 drop database [dbname];
-- 刷新服务 flush privileges;
-- 删除账户 drop user hustjhcg@localhost;
-- 刷新服务 flush privileges;
-- 修改密码 set password for root=password('123456');
-- 切换数据库 use mysql;
-- 查询数据库账号和权限 select host,user from user;
//如果为null则改为0
IFNULL( tsmcs.sign_count, 0 ) signCount,
( SELECT count( DISTINCT somo.erp_cust_id )FROM tb_sup_order_main_original somo WHERE somo.supplier_id = sb.supplier_id ) AS custNum,
格式化金额:
四舍五入:CONVERT(sum(od.purchase_num * od.member_price), DECIMAL(10,2))
千分位:else FORMAT( sod.member_price*sod.purchase_num,2) end as totalPrice,
-- 修改表的创建时间和更新时间字段
alter table t_users add create_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间';
alter table t_users add update_at timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间';
-- 添加联合索引 alter table tb_sup_cust add index INDEX_DANW_BRANCHIDY(索引名) (cust_name,branch_id);
-- 添加唯一索引 alter table tb_sup_orderaudit_totalprice add unique (supplier_id);
-- 创建临时表并把已有的表数据添加到临时表
CREATE TABLE tem_t_users SELECT uid,username,password FROM t_users;
-- 查出一张表的字段插入临时表
insert into tem_t_users (`uid`,`username`,`password`) values (2,'a',(select password from t_users));
或
insert into tem_t_users (`uid`,`username`,`password`) (select uid,username,password from t_users);
-- 根据查询临时表更改已有的表
update out_user as a inner join out_user_copy1 as b on a.tu_id = b.tu_id set a.time_update = a.time_create;
explan + sql
id ID可以如果相同认为是同一组,从上往下执行,在所有组中id越大,优先级越高,越先执行
select_type 查询类型
1)SIMPLE 简单查询,不包括子查询或UNION
2)PRIMARY 查询中包含任何复杂的子部分,最外层查询被标记为
3)SUBQUERY 在select或where里包含了子查询
4)DERIVED 在from列表中包含了子查询被标记为DERIVED(衍生),mysql会递归执行这些子查询,把结果放在临时表
5) UNION 若在第二个select出现在union后,会标记为UNION.若union包含在from子句的查询中,外层会标记为DERIVED
6)UNION RESULT 从UNION中获取select
table 这一行数据显示的表
type 从最好到最差的顺序system > const > eq_ref > ref > range > index > ALL 一般来说最少达到range,最好能达到ref
possible_keys 显示可能应用在这张表中的索引,一个或多个,查询涉及到的字段若存在索引,则也列出来,但不一定被查询实际用到
key 实际使用的索引
ken_len 索引字段的最大可能长度,并非实际长度,key_len长度越短越好
ref 表示索引的哪一列被使用,也可能是常量
rows 根据表统计信息和索引选用的情况,大致估算出找到所需记录的读取行数
Extra 其他的信息
索引失效的情况
1.(复合索引)全值匹配我最爱
2.最佳左前缀法则(带头大哥不能死,中间兄弟不能断)
3.不在索引列上做任何操作(计算,函数,(自动or手动)类型转换),会导致索引失效而转向全表扫描
4.存储引擎不能使用索引中范围条件右边的列
5.尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select*
6.mysql在使用不等于(! =或者<>)的时候无法使用索引会导致全表扫描
7.is null,is not nul 也无法使用索引
8.like以通配符开头(“%abc.…)mysql索引失效会变成全表扫描的操作
9.字符串不加单引号索引失效
10.少用or,用它来连接时会索引失效
全值匹配我最爱,最左前缀要遵守;
带头大哥不能死,中间兄弟不能断;
索引列上少计算,范围之后全失效;
Like百分写最右,覆盖索引不写星;
不等空值还有or,索引失效要少用;
VAR引号不可丢,SQL高级也不难!
解决like‘%字符串%’时索引不被使用,使用覆盖索引,即建的索引和查询的字段个数顺序最
好完全一致