Mysql高级1
Linux安装Mysql
索引篇
索引的概述
索引就是一种数据结构
作用就是用来高效获取数据
数据被整表扫描是非常慢的了解一下
加入索引机制后
说明
数据库维护着索引和表的对应关系,索引以某种方式指向数据。
图中数据结构是二叉树结构。根节点就是表中的第一条数据,往下的每一条数据会和根节点做值的比较,值大的靠右,小的靠左。
索引会消耗硬盘空间的哦,而且还不小呢。相比整表扫描查询,索引只是减少了查询的次数罢了。
可以理解为书籍当中的目录。
优势和劣势
优势:
- 提高数据的检索效率,降低数据库的IO成本。
- 降低CPU的消耗,节能hh。
劣势:
- 实际上索引也是一张表,表中保存了主键和索引字段,并指向实体类的记录,所以索引列也是要占用空间的。
- 会降低表更新的速率。每一次表中的insert,update,delete都会使索引维护。
索引结构
Btree结构
总结:
相比之前的二叉树,衍生的层级比较浅。查询速率更快。
key是子节点
B+tree结构
总结:
b+tree结构是btree的变种。
叶子节点往上部分只起到索引效果,数据都是存放在叶子节点当中的。
查数据都要走到叶子节点,所以说稳定性很高。
Mysql中的B+tree结构
总结:
索引分类
单值索引
这也是最常见到的
就是对单个字段(列)建一个索引
每个索引只负责它接管的列数据
一个表中可以包含多个单值索引
唯一索引
前提就是单个字段(列)的值是唯一的,可以包含空值
复合索引
一个索引当中包含了多个字段(列)
对应sql查询,相当于根据XX查找XX。
主键索引
mysql默认创建的
索引语法
https://www.cnblogs.com/peijz/p/12400437.html
索引设计原则
视图篇
视图概述
用来简化查询的
视图是由数据库中的一个表或多个表导出的虚拟表,是一种虚拟存在的表,方便用户对数据的操作。
操作视图
https://blog.csdn.net/pan_junbiao/article/details/86132535
存储过程和函数篇
存储过程和函数概述
总结:
应用程序和数据层之间存在网络传输,交互的次数越多,性能就会越低。
将所有操作的sql语句封装在一起,就可以之交互一次,提高效率
触发器篇
概述
总结:
可以理解为 js 的事件机制。
Mysql高级2
Mysql体系结构
组成部分:
每一层的内容:
总结:
类似三层架构
存储引擎
存储引擎概述
各个存储引擎的特性
总结:
不支持事务的操作数据更快。
MEMORY:数据是存放在内存当中的,对数据的操作更快。但是内存珍贵,而且还容易丢失数据。
MERGE:类似数据当中的视图。有一张主表。可以有很多附表。操作主表,其实就是操作附表。
存储引擎的选择
总结:
如果不是非要Mysql存储数据的话,其实很多nosql型数据库也是可以代替的。
优化sql步骤
不错的文章:
https://www.cnblogs.com/rouqinglangzi/p/11144960.html
https://blog.csdn.net/qq_17033579/article/details/82950096
https://www.cnblogs.com/roadlandscape/p/12753790.html
1.查看sql执行频率
MySQL 客户端连接成功后,通过 show [session|global] status 命令可以提供服务器状态信息。show [session|global] status 可以根据需要加上参数“session”或者“global”来显示 session 级(当前连接)的统计结果和 global 级(自数据库上次启动至今)的统计结果。如果不写,默认使用参数是“session”。
显示当前 session 中所有统计参数的值:
show status like ‘Com_______‘; (7个 ‘_‘)
显示全局所有统计参数的值:
show global status like ‘Com_______‘;
show status like ‘Innodb_rows_%‘; (针对innodb引擎使用的)
Com_xxx 表示每个 xxx 语句执行的次数,我们通常比较关心的是以下几个统计参数。
Com_*** : 这些参数对于所有存储引擎的表操作都会进行累计。
Innodb_*** : 这几个参数只是针对InnoDB 存储引擎的,累加的算法也略有不同。
定位低效率的执行sql
可以通过以下两种方式定位执行效率较低的 SQL 语句:
- 慢查询日志 : 通过慢查询日志定位那些执行效率较低的 SQL 语句,用 --log-slow-queries[=file_name] 选项启动时,mysqld 写一个包含所有执行时间超过long_query_time 秒的 SQL 语句的日志文件。在mysql日志部分详解(https://www.cnblogs.com/roadlandscape/p/12809443.html)
- show processlist : 慢查询日志在查询结束以后才记录,所以在应用反映执行效率出现问题的时候查询慢查询日志并不能定位问题,==可以使用 ==show processlist 命令查看当前MySQL在进行的线程,包括线程的状态、是否锁表等,可以实时地查看 SQL 的执行情况,同时对一些锁表操作进行优化。
- id列:用户登录mysql时,系统分配的"connection_id",可以使用函数connection_id()查看
- user列:显示当前用户。如果不是root,这个命令就只显示用户权限范围的sql语句
- host列:显示这个语句是从哪个ip的哪个端口上发的,可以用来跟踪出现问题语句的用户
- db列:显示这个进程目前连接的是哪个数据库
- command列:显示当前连接的执行的命令,一般取值为休眠(sleep),查询(query),连接(connect)等
- time列:显示这个状态持续的时间,单位是秒
- state列:显示使用当前连接的sql语句的状态,很重要的列。state描述的是语句执行中的某一个状态。一个sql语句:以查询为例,可能需要经过copying to tmp table、sorting result、sending data等状态才可以完成
- info列:显示这个sql语句,是判断问题语句的一个重要依据
explain分析执行计划
explain:说明;解释
用来查看sql语句的执行计划的,说白点就是查看 查询sql语句的详细信息的。
通过以上步骤查询到效率低的 SQL 语句后,可以通过 EXPLAIN 或者 DESC 命令获取 MySQL如何执行 SELECT 语句的信息,包括在 SELECT 语句执行过程中表如何连接和连接的顺序。
查询SQL语句的执行计划 :
explain select * from tb_item where id = 1;
图示:
字段解释:
explain 之 id
id 字段是 select查询的序列号,是一组数字,表示的是查询中执行select子句或者是操作表的顺序。id 情况有三种:
-
id 相同表示加载表的顺序是从上到下。
-
explain select * from t_role r, t_user u, user_role ur where r.id = ur.role_id and u.id = ur.user_id ;
-
-
id 不同,id值越大,优先级越高,越先被执行。
-
EXPLAIN SELECT * FROM t_role WHERE id = (SELECT role_id FROM user_role WHERE user_id = (SELECT id FROM t_user WHERE username = ‘stu1‘))
-
-
id 有相同,也有不同,同时存在。id相同的可以认为是一组,从上往下顺序执行;在所有的组中,id的值越大,优先级越高,越先执行。
-
EXPLAIN SELECT * FROM t_role r , (SELECT * FROM user_role ur WHERE ur.`user_id` = ‘2‘) a WHERE r.id = a.role_id ;
-
explain 之 select_type
表示 SELECT 的类型,从上到下查询效率越来越慢,常见的取值
explain 之 table
展示这一行的数据是关于哪一张表的
explain 之 type
type 显示的是访问类型,是较为重要的一个指标,可取值为:
结果值从最好到最坏依次是:
NULL > system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
system > const > eq_ref > ref > range > index > ALL
一般来说, 我们需要保证查询至少达到 range 级别, 最好达到ref 。
explain 之 key
- possible_keys : 显示可能应用在这张表的索引, 一个或多个。
- key : 实际使用的索引, 如果为NULL, 则没有使用索引。
- key_len : 表示索引中使用的字节数, 该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下, 长度越短越好 。
explain 之 rows
扫描行的数量。
explain 之 extra
using filesort 和 using temporary 效率低。
show profile分析sql
Mysql从5.0.37版本开始增加了对 show profiles 和 show profile 语句的支持。show profiles 能够在做SQL优化时帮助我们了解时间都耗费到哪里去了。
执行show profiles 指令, 可以查看SQL语句执行的耗时:
通过show profile for query query_id 语句可以查看到该SQL执行过程中每个线程的状态和消耗的时间:
Sending data 状态表示MySQL线程开始访问数据行并把结果返回给客户端,而不仅仅是返回给客户端。因为在Sending data状态下,MySQL线程往往需要做大量的磁盘读取操作,所以经常是整个查询中耗时最长的状态。
在获取到最消耗时间的线程状态后,MySQL支持进一步选择all、cpu、block io 、context switch、page faults等明细类型类查看MySQL在使用什么资源上耗费了过高的时间。例如,选择查看CPU的耗费时间 :
字段解释:
trace分析优化器执行计划
MySQL5.6提供了对SQL的跟踪trace, 通过trace文件能够进一步了解为什么优化器选择A计划, 而不是选择B计划。
打开trace , 设置格式为 JSON,并设置trace最大能够使用的内存大小,避免解析过程中因为默认内存过小而不能够完整展示。
set optimizer_trace="enabled=on",end_markers_in_json=on;
set optimizer_trace_max_mem_size=1000000;
开启后执行我们的sql语句:
select * from tb_item where id < 4;
最后, 检查information_schema.optimizer_trace就可以知道MySQL是如何执行SQL的 :
select * from information_schema.optimizer_trace\G;
索引的使用
索引优化:避免索引失效
创建了索引就能提高查询效率吗?不,合理利用了索引才可以提高效率。
1. 全值匹配
对索引中所有的列都指定具体的值进行查询。
建立几个复合索引字段,最好就用上几个字段。且按照顺序来用。
2. 最左前缀法则
//创建三个字段的索引,复合索引。
create index idx_seller_name_sta_addr on tb_seller(name,status,address);
如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始,并且不跳过索引中的列
如果符合最左法则,但是出现跳跃某一列,只有最左列索引生效
简单来说就相当与我们爬楼梯。
3. 范围查询右边的列,不能使用索引
如果查询条件中出现了范围的查询,再往后的条件不走索引。
4. 不要在索引列上进行运算操作, 索引将失效
5. 字符串不加单引号,造成索引失效
6. 尽量使用覆盖索引,避免select *
select * 会造成回表查询(用索引查出数据后,当前索引字段没有具体的数据内容,又要回到表中查数据)。
尽量使用覆盖索引(只访问索引列的查询(索引列完全包含查询列)),减少select * 。
查询的时候select后面加上具体的字段(加过索引的字段)
如果查询的列,超出索引的列,也会降低性能。
7. 用or分割开的条件,前有后无,索引失效
如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。
8. 以%开头的Like模糊查询,索引失效。
如果仅仅是尾部模糊匹配,索引不会失效。
如果是头部模糊匹配,索引失效(可以通过覆盖索引来解决)。
‘苹果%‘ //不失效
‘%苹果‘ //失效
‘%苹果%‘ //失效
覆盖索引:SQL只需要通过索引就可以返回查询所需要的数据,而不必通过二级索引查到主键之后再去查询数据
9. 如果MySQL评估使用索引比全表更慢,则不使用索引。(mysql底层优化)
10. is NULL , is NOT NULL 有时索引失效。
原因:如果条件的值为null占表的大部分,mysql底层会判断还不如全表扫描更快,反之not null也一样
11. in 走索引, not in 索引失效。
12. 单列索引和复合索引。尽量使用复合索引,而少使用单列索引 。
因为如果查询条件包含多个单列索引,只会使用其中一个最优的索引(辨识度最高索引),并不会使用全部索引 。
查看索引的使用情况
sql优化
大批量插入数据
当使用load 命令导入数据的时候,适当的设置可以提高导入的效率。
字段没有值的记录用\N代替
load data local infile ‘数据文件‘ into table ‘插入的表名‘ fields terminated by ‘数据分隔符‘ lines terminated by ‘换行符‘;
对于 InnoDB 类型的表,有以下几种方式可以提高导入的效率:
-
主键顺序插入
- 因为InnoDB类型的表是按照主键的顺序保存的,所以将导入的数据按照主键的顺序排列,可以有效的提高导入数据的效率。如果InnoDB表没有主键,那么系统会自动默认创建一个内部列作为主键,所以如果可以给表创建一个主键,将可以利用这点,来提高导入数据的效率。
-
关闭唯一性校验
- 在导入数据前执行 SET UNIQUE_CHECKS=0,关闭唯一性校验。
- 在导入结束后执行SET UNIQUE_CHECKS=1,恢复唯一性校验,可以提高导入的效率。
-
手动提交事务
- 如果应用使用自动提交的方式,建议在导入前执行 SET AUTOCOMMIT=0,关闭自动提交。
- 导入结束后再执行 SETAUTOCOMMIT=1,打开自动提交,也可以提高导入的效率。
优化insert语句
当进行数据的insert操作的时候,可以考虑采用以下几种优化方案
1、如果需要同时对一张表插入很多行数据时,应该尽量使用多个值表的insert语句,这种方式将大大的缩减客户端与数据库之间的连接、关闭等消耗。使得效率比分开执行的单个insert语句快。
原始方式为:
insert into tb_test values(1,‘Tom‘);
insert into tb_test values(2,‘Cat‘);
insert into tb_test values(3,‘Jerry‘);
优化后的方案为 :
insert into tb_test values(1,‘Tom‘),(2,‘Cat‘),(3,‘Jerry‘);
2、把自动提交事务改为手动提交,在事务中进行数据插入。
start transaction;
insert into tb_test values(1,‘Tom‘);
insert into tb_test values(2,‘Cat‘);
insert into tb_test values(3,‘Jerry‘);
commit;
3、数据有序插入
原始方式为:
insert into tb_test values(4,‘Tim‘);
insert into tb_test values(1,‘Tom‘);
insert into tb_test values(3,‘Jerry‘);
insert into tb_test values(5,‘Rose‘);
insert into tb_test values(2,‘Cat‘);
优化后的方案为 :
insert into tb_test values(1,‘Tom‘);
insert into tb_test values(2,‘Cat‘);
insert into tb_test values(3,‘Jerry‘);
insert into tb_test values(4,‘Tim‘);
insert into tb_test values(5,‘Rose‘);
优化order by语句
两种排序方式
- 第一种是通过对返回数据进行排序,也就是通常说的 filesort 排序,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。(using filesort)
- 第二种通过有序索引顺序扫描直接返回有序数据,这种情况即为 using index,不需要额外排序,操作效率高。
多字段排序:
要么全是升序,要么全是降序。
总结:
了解了MySQL的排序方式,优化目标就清晰了:尽量减少额外的排序,通过索引直接返回有序数据。where 条件和Order by 使用相同的索引,并且 Order By 的字段顺序和索引的字段顺序相同, Order by 的字段都是升序,或者都是降序。否则肯定需要额外的操作,这样就会出现FileSort。
Filesort 的优化
通过创建合适的索引,能够减少 Filesort 的出现,但是在某些情况下,条件限制不能让Filesort消失,那就需要加快 Filesort的排序操作。
对于Filesort , MySQL 有两种排序算法:
- 两次扫描算法 :MySQL4.1 之前,使用该方式排序。
- 首先根据条件取出排序字段和行指针信息。
- 然后在排序区sort buffer 中排序,如果sort buffer不够,则在临时表 temporary table 中存储排序结果。
- 完成排序之后,再根据行指针回表读取记录,该操作可能会导致大量随机I/O操作。
- 一次扫描算法:一次性取出满足条件的所有字段,然后在排序区 sort buffer 中排序后直接输出结果集。排序时内存开销较大,但是排序效率比两次扫描算法要高。
MySQL 通过比较系统变量 max_length_for_sort_data 的大小和Query语句取出的字段总大小, 来判定采用哪种排序算法,如果max_length_for_sort_data 更大,那么使用第二种优化之后的算法;否则使用第一种。
可以适当提高 sort_buffer_size 和 max_length_for_sort_data 系统变量,来增大排序区的大小,提高排序的效率。
实操:
https://www.jianshu.com/p/c31c1e90436b
优化group by 语句
由于GROUP BY 实际上也同样会进行排序操作,而且与ORDER BY 相比,GROUP BY 主要是多了排序之后的分组操作。当然,如果在分组的时候还使用了其他的一些聚合函数,那么还需要一些聚合函数的计算。所以,在GROUP BY 的实现过程中,与 ORDER BY 一样也可以利用到索引。
如果查询包含 group by 但是用户想要避免排序结果的消耗, 则可以执行order by null 禁止排序。如下 :
select age,count(*) from emp group by age order by null;
使用explain分析执行计划:
explain select age,count(*) from emp group by age order by null;
发现即使禁止了排序还是会出现
可以对需要查询的字段建立索引,提高查询效率。建立索引后 using temporary 变为 using index
优化嵌套查询
俗称:子查询。
记住一个原则就可:尽量少使用子查询,而使用多表连接查询替换子查询。
Mysql4.1版本之后,开始支持SQL的子查询。这个技术可以使用SELECT语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。使用子查询可以一次性的完成很多逻辑上需要多个步骤才能完成的SQL操作,同时也可以避免事务或者表锁死,并且写起来也很容易。但是,有些情况下,子查询是可以被更高效的连接(JOIN)替代。
优化or条件
对于包含OR的查询子句,如果要利用索引,则OR之间的每个条件列都必须用到索引,而且不能使用到复合索引; 如果没有索引,则应该考虑增加索引。
建议使用 union 或 in 替换 or
使用SQL提示
SQL提示,是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的。
1.USE INDEX
在查询语句中表名的后面,添加 use index 来提供希望MySQL去参考的索引列表,就可以让MySQL不再考虑其他可用的索引。
2.IGNORE INDEX
如果用户只是单纯的想让MySQL忽略一个或者多个索引,则可以使用 ignore index 作为 hint 。
select * from tb_seller ignore index(idx_seller_name) where name = ‘小米科技‘;
3.FORCE INDEX
为强制MySQL使用一个特定的索引,可在查询中使用 force index 作为hint 。
select * from tb_seller force index(idx_seller_address) where address=‘北京市‘;
Mysql高级3
应用优化
使用连接池
对于访问数据库来说,建立连接的代价是比较昂贵的,因为我们频繁的创建关闭连接,是比较耗费资源的,我们有必要建立数据库连接池,以提高访问的性能。
减少对Mysql的访问
避免对数据进行重复检索:
在编写应用代码时,需要能够理清对数据库的访问逻辑。能够一次连接就获取到结果的,就不用两次连接,这样可以大大减少对数据库无用的重复请求。
增加cache层:
在应用中,我们可以在应用中增加 缓存 层来达到减轻数据库负担的目的。缓存层有很多种,也有很多实现方式,只要能达到降低数据库的负担又能满足应用需求就可以。
因此可以将部分数据从数据库中抽取出来放到应用端以文本方式存储, 或者使用框架(Mybatis, Hibernate)提供的一级缓存/二级缓存,或者使用redis数据库来缓存数据 。
负载均衡
负载均衡是应用中使用非常普遍的一种优化方法,它的机制就是利用某种均衡算法,将固定的负载量分布到不同的服务器上, 以此来降低单台服务器的负载,达到优化的效果。
利用MySQL复制分流查询:
通过MySQL的主从复制,实现读写分离,使增删改操作走主节点,查询操作走从节点,从而可以降低单台服务器的读写压力。
采用分布式数据库架构:
分布式数据库架构适合大数据量、负载高的情况,它有良好的拓展性和高可用性。通过在多台服务器之间分布数据,可以实现在多台服务器之间的负载均衡,提高访问效率。
Mysql中查询缓存优化
概述
开启Mysql的查询缓存,当执行完全相同的SQL语句的时候,服务器就会直接从缓存中读取结果,当数据被修改,之前的缓存会失效,修改比较频繁的表不适合做查询缓存。
查询缓存配置
查看当前mysql数据是否支持缓存
SHOW VARIABLES LIKE ‘have_cache_type‘
查看当前mysql是否开启了缓存
SHOW VARIABLES LIKE ‘query_cache_type‘
查看缓存的占用大小
SHOW VARIABLES LIKE ‘query_cache_size‘
查看缓存的状态变量
SHOW STATUS LIKE ‘Qcache%‘
查看缓存的状态变量
SHOW STATUS LIKE ‘Qcache%‘
开启查看缓存
MySQL的查询缓存默认是关闭的,需要手动配置参数 query_cache_type , 来开启查询缓存。query_cache_type 该参数的可取值有三个 :
在 /usr/my.cnf 配置中,增加以下配置 :
# 任意位置添加
# 开启mysql的查询缓存
query_cache_type=1
配置完毕之后,重启服务既可生效 ;
然后就可以在命令行执行SQL语句进行验证 ,执行一条比较耗时的SQL语句,然后再多执行几次,查看后面几次的执行时间;或者通过查看查询缓存的缓存命中数,来判定是否走查询缓存。
指定select选项是否缓存
可以在SELECT语句中指定两个与查询缓存相关的选项 :
SQL_CACHE : 如果查询结果是可缓存的,并且 query_cache_type 系统变量的值为ON或 DEMAND ,则缓存查询结果 。
SQL_NO_CACHE : 服务器不使用查询缓存。它既不检查查询缓存,也不检查结果是否已缓存,也不缓存查询结果。
例子:
SELECT SQL_CACHE id, name FROM customer;
SELECT SQL_NO_CACHE id, name FROM customer;
查询缓存失效的情况
1、SQL 语句不一致的情况, 要想命中查询缓存,查询的SQL语句必须一致。
SQL1 : select count(*) from tb_item;
SQL2 : Select count(*) from tb_item; -- 不走缓存(大小写)
2、当查询语句中有一些不确定的值时,则不会缓存。如 : now() , current_date() , curdate() , curtime() , rand() ,uuid() , user() , database() 。
SQL1 : select * from tb_item where updatetime < now() limit 1;
SQL2 : select user();
SQL3 : select database();
3、不使用任何表查询语句。
select ‘A‘;
4、查询 mysql, information_schema或 performance_schema 数据库中的表时,不会走查询缓存。
select * from information_schema.engines;
5、在存储的函数,触发器或事件的主体内执行的查询。
6、如果表更改,则使用该表的所有高速缓存查询都将变为无效并从高速缓存中删除。这包括使用 MERGE 映射到已更改表的表的查询。一个表可以被许多类型的语句,如被改变 INSERT, UPDATE, DELETE, TRUNCATETABLE, ALTER TABLE, DROP TABLE,或 DROP DATABASE 。
Mysql内存管理及优化
内存优化原则
- 将尽量多的内存分配给MySQL做缓存,但要给操作系统和其他程序预留足够内存。
- MyISAM 存储引擎的数据文件读取依赖于操作系统自身的IO缓存,因此,如果有MyISAM表,就要预留更多的内存给操作系统做IO缓存。
- 排序区、连接区等缓存是分配给每个数据库会话(session)专用的,其默认值的设置要根据最大连接数合理分配,如果设置太大,不但浪费资源,而且在并发连接较高时会导致物理内存耗尽。
MyISAM内存优化
myisam存储引擎使用 key_buffer 缓存索引块(不缓存数据块儿),加速myisam索引的读写速度。对于myisam表的数据块,mysql没有特别的缓存机制,完全依赖于操作系统的IO缓存。
key_buffer_size:
key_buffer_size决定MyISAM索引块缓存区的大小,直接影响到MyISAM表的存取效率。可以在MySQL参数文件中设置key_buffer_size的值,对于一般MyISAM数据库,建议至少将1/4可用内存分配给key_buffer_size。
# 在/usr/my.cnf 中做如下配置:
key_buffer_size=512M
read_buffer_size:
如果需要经常顺序扫描myisam表,可以通过增大read_buffer_size的值来改善性能。但需要注意的是read_buffer_size是每个session独占的,如果默认值设置太大,就会造成内存浪费。
read_rnd_buffer_size:
对于需要做排序的myisam表的查询,如带有order by子句的sql,适当增加 read_rnd_buffer_size 的值,可以改善此类的sql性能。但需要注意的是 read_rnd_buffer_size 是每个session独占的,如果默认值设置太大,就会造成内存浪费。
InnoDB内存优化
innodb用一块内存区做IO缓存池,该缓存池不仅用来缓存innodb的索引块,而且也用来缓存innodb的数据块。(缓存索引块儿和数据块儿)
innodb_buffer_pool_size:
该变量决定了 innodb 存储引擎表数据和索引数据的最大缓存区大小。在保证操作系统及其他程序有足够内存可用的情况下,innodb_buffer_pool_size 的值越大,缓存命中率越高,访问InnoDB表需要的磁盘I/O 就越少,性能也就越高。
# 尽量设置大点会降低对磁盘io操作的频率
innodb_buffer_pool_size=512M
innodb_log_buffer_size:
决定了innodb重做日志缓存的大小,对于可能产生大量更新记录的大事务,增加innodb_log_buffer_size的大小,可以避免innodb在事务提交前就执行不必要的日志写入磁盘操作。
innodb_log_buffer_size=10M
Mysql并发参数调整
从实现上来说,MySQL Server 是多线程结构,包括后台线程和客户服务线程。
多线程可以有效利用服务器资源,提高数据库的并发性能。
在Mysql中,控制并发连接和线程的主要参数包括
max_connections
back_log
thread_cache_size
table_open_cahce
1.max_connections
采用max_connections 控制允许连接到MySQL数据库的最大数量,默认值是 151。如果状态变量connection_errors_max_connections 不为零,并且一直增长,则说明不断有连接请求因数据库连接数已达到允许最大值而失败,这是可以考虑增大max_connections 的值。
Mysql 最大可支持的连接数,取决于很多因素,包括给定操作系统平台的线程库的质量、内存大小、每个连接的负荷、CPU的处理速度,期望的响应时间等。在Linux 平台下,性能好的服务器,支持 500-1000 个连接不是难事,需要根据服务器性能进行评估设定。
show variables like ‘max_connections’ 最大连接数
show status like ‘max_used_connections’ 响应的连接数
max_used_connections / max_connections * 100% (理想值≈ 85%)
2.back_log
back_log 参数控制MySQL监听TCP端口时设置的积压请求栈大小。如果MySql的连接数达到max_connections时,新来的请求将会被存在堆栈中,以等待某一连接释放资源,该堆栈的数量即back_log,如果等待连接的数量超过back_log,将不被授予连接资源,将会报错。5.6.6 版本之前默认值为 50 , 之后的版本默认为 50 +(max_connections / 5), 但最大不超过900。
如果需要数据库在较短的时间内处理大量连接请求, 可以考虑适当增大back_log 的值。
3.table_open_cache
该参数用来控制所有SQL语句执行线程可打开表缓存的数量(针对整个数据库,不是session会话), 而在执行SQL语句时,每一个SQL执行线程至少要打开 1 个表缓存。该参数的值应该根据设置的最大连接数 max_connections 以及每个连接执行关联查询中涉及的表的最大数量来设定 :max_connections*N
4.thread_cache_size
为了加快连接数据库的速度,MySQL 会缓存一定数量的客户服务线程以备重用,通过参数 thread_cache_size 可控制 MySQL 缓存客户服务线程的数量。
5.innodb_lock_wait_timeout
该参数是用来设置InnoDB 事务等待行锁的时间,默认值是50ms , 可以根据需要进行动态设置。对于需要快速反馈的业务系统来说,可以将行锁的等待时间调小,以避免事务长时间挂起; 对于后台运行的批量处理程序来说,可以将行锁的等待时间调大, 以避免发生大的回滚操作。
Mysql锁问题
概述
锁是计算机协调多个进程或线程并发访问某一资源的机制(避免资源争抢的引发的问题)。
锁的目的就是为了 解决并发问题。
在数据库中,除传统的计算资源(如 CPU、RAM、I/O 等)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。
锁分类
从对数据操作的粒度分 :
- 表锁:操作时,会锁定整个表。
- 行锁:操作时,会锁定当前操作行。
从对数据操作的类型分:
- 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响。
- 写锁(排它锁):当前操作没有完成之前,它会阻断其他写锁和读锁。
Mysql锁
MyISAM 表锁
MyISAM 存储引擎只支持表锁,这也是MySQL开始几个版本中唯一支持的锁类型。
如何加表锁
MyISAM 在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATE、DELETE、INSERT 等)前,会自动给涉及的表加写锁,这个过程并不需要用户干预,因此,用户一般不需要直接用 LOCK TABLE 命令给 MyISAM 表显式加锁。
显示加表锁语法:
加读锁 : lock table table_name read;
加写锁 : lock table table_name write;
锁模式的相互兼容性
由上表可见:
-
对MyISAM 表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求;
-
对MyISAM 表的写操作,则会阻塞其他用户对同一表的读和写操作;
简而言之,就是读锁会阻塞写,但是不会阻塞读。而写锁,则既会阻塞读,又会阻塞写。
此外,MyISAM 的读写锁调度是写优先,这也是MyISAM不适合做写为主的表的存储引擎的原因。因为写锁后,其他线程不能做任何操作,大量的更新会使查询很难得到锁,从而造成永远阻塞。
查看锁的争用情况
show open tables;
In_user : 表示当前被查询使用的次数(表加了读锁)。如果该数为零,则表是打开的,但是当前没有被使用。
Name_locked:表示是否被锁定。用于取消表或对表进行重命名等操作。InnoDB锁问题
show status like ‘Table_locks%‘;
Table_locks_immediate : 指的是能够立即获得表级锁的次数,每立即获取锁,值加1。
Table_locks_waited : 指的是不能立即获取表级锁而需要等待的次数,每等待一次,该值加1,此值高说明存在着较为严重的表级锁争用情况。
InnoDB 行锁
行锁特点:偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概览最低,并发度也最高。
InnoDB和MyISAM的最大不同有两点:
- 一是:事务的支持。
- 二是:加锁的机制不同。
出现的背景
事务:
事务隔离级别:
Mysql默认隔离级别为Repeatable read
查看方式:
show variables like ‘tx_isolation‘;
关闭事务的自动提交
set autocommit=0;
InnoDB 的行锁模式
InnoDB 实现了以下两种类型的行锁:
- 共享锁(S):又称为读锁,简称S锁,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改
- 排他锁(X):又称为写锁,简称X锁,排他锁就是不能与其他锁并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据进行读取和修改的。
对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);
对于普通SELECT语句,InnoDB不会加任何锁;
可以通过以下语句显示给记录集加共享锁或排他锁 。
共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE
排他锁(X) :SELECT * FROM table_name WHERE ... FOR UPDATE
索引失效行锁升级为表锁
如果不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,实际效果跟表锁一样。
查看当前表的索引 :
show index from table_name;
间隙锁危害
当我们用范围条件,而不是使用相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据进行加锁; 对于键值在条件范围内但并不存在的记录,叫做 "间隙(GAP)" , InnoDB也会对这个 "间隙" 加锁,这种锁机制就是所谓的 间隙锁(Next-Key锁)。
如果此时插入加了间隙锁的数据,并不能插入成功,即使数据库并不存在这样的数据。
InnoDB 行锁争用情况
使用下列语句查看行锁争用情况:
show status like ‘innodb_row_lock%‘;
- Innodb_row_lock_current_waits: 当前正在等待锁定的数量
- Innodb_row_lock_time: 从系统启动到现在锁定总时间长度
- Innodb_row_lock_time_avg:每次等待所花平均时长
- Innodb_row_lock_time_max:从系统启动到现在等待最长的一次所花的时间
- Innodb_row_lock_waits: 系统启动后到现在总共等待的次数
当等待的次数很高,而且每次等待的时长也不小的时候,我们就需要分析系统中为什么会有如此多的等待,然后根据分析结果着手制定优化计划。
总结
InnoDB存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面带来了性能损耗可能比表锁会更高一些,但是在整体并发处理能力方面要远远高于MyISAM的表锁的。当系统并发量较高的时候,InnoDB的整体性能和MyISAM相比就会有比较明显的优势。
但是,InnoDB的行级锁同样也有其脆弱的一面,当我们使用不当的时候,可能会让InnoDB的整体性能表现不仅不能比MyISAM高,甚至可能会更差。
优化建议:
- 尽可能让所有数据检索都能通过索引来完成,避免无索引行锁升级为表锁。
- 合理设计索引,尽量缩小锁的范围
- 尽可能减少索引条件,及索引范围,避免间隙锁
- 尽量控制事务大小,减少锁定资源量和时间长度
- 控制事务不要操作过多的表,或者过多的记录,减少锁定资源的数量和锁定的时间
- 尽可能使用低级别事务隔离(但是需要业务层面满足需求)
常用sql技巧
SQL执行顺序
编写顺序:
执行顺序:
正则表达式使用
正则表达式(Regular Expression)是指一个用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串。
select * from emp where name regexp ‘^T‘;
select * from emp where name regexp ‘2$‘;
select * from emp where name regexp ‘[uvw]‘;
MySQL 常用函数
数字函数:
字符串函数:
日期函数:
聚合函数: