MySQL 性能优化的 9 种姿势,面试再也不怕了!

6da6403ba61d9b7af61f18528e3cd329.png

大家好,我是磊哥!

今天给大家分享一些简单好用的数据库优化方式!

1、选择最合适的字段属性

Mysql是一种关系型数据库,可以很好地支持大数据量的存储,但是一般来说,数据库中的表越小,在它上面执行的查询也就越快。因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度舍得尽可能小。

例如:在定义邮政编码这个字段时,如果将其设置为char(255),显然给数据库增加了不必要的空间,甚至使用varchar这种类型也是多余的,因为char(6)就可以很好地完成了任务。同样的如果可以的话,我们应该是用MEDIUMINT而不是BIGINT来定义整形字段。

2、尽量把字段设置为NOT NULL

在可能的情况下,尽量把字段设置为NOT NULL,这样在将来执行查询的时候,数据库不用去比较NULL值。

对于某些文本字段来说,例如“省份”或者“性别”,我们可以将他们定义为ENUM(枚举)类型。因为在MySQL中,ENUM类型被当做数值型数据来处理,而数值型数据被处理起来的速度要比文本类型要快得多。这样我们又可以提高数据库的性能。

3、使用连接(JOIN)来代替子查询(Sub-Queries)

MySQL从4.1开始支持SQL的子查询。这个技术可以使用select语句来创建一个单例的查询结果,然后把这个结果作为过滤条件用在另一个查询中。

例如:我们要将客户基本信息表中没有任何订单的客户删除掉,就可以利用子查询先从销售信息表中将所有发出订单的客户id取出来,然后将结果传递给主查询,如下图所示:

a5ee791c10b4a2d3f211076c4cd79bd7.png

如果使用连接(JOIN)来完成这个工作,速度将会快很多,尤其是当salesinfo表中对CustomerID建有索引的话,性能将会更好,查询如下:

70150a7d30a8a5856e310805a65a47cd.png

连接(JOIN)之所以更有效率一些,是因为MySQL不需要在内存中创建临时表来完成这个逻辑上 需要两个步骤的查询工作。

另外,如果你的应用程序有很多JOIN查询,你应该确认两个表中JOIN的字段是被建立过索引的。这样MySQL内部 会启动为你优化JOIN的SQL语句的机制。而且这些被用来JOIN的字段,应该是相同的类型的。

例如:如果你要把DECIMAL字段和一个INT字段JOIN在一起,MySQL就无法使用他们的索引。对于那些STRING类型,还需要有相同的字符集才行。(两个表的字符集可能不相同)。

inner join内连接也叫做等值连接,left/right join是外链接。

SELECT A.id,A.name,B.id,B.name FROM A LEFT JOIN B ON A.id=B.id;
SELECT A.id,A.name,B.id,B.name FROM A RIGHT JOIN ON B A.id= B.id;
SELECT A.id,A.name,B.id,B.name FROM A INNER JOIN ON A.id =B.id;

经过多方面的证实inner join性能比较快,因为inner join是等值连接,或许返回的行数比较少。但是我们要记得有些语句隐形的用到了等值连接,如:

SELECT A.id,A.name,B.id,B.name FROM A,B WHERE A.id = B.id;

sql中的连接查询有inner join(内连接)、left join(左连接)、right join(右连接)、full join(全连接)四种方式,它们之间其实并没有太大区别,仅仅是查询出来的结果有所不同。

例如我们有两张表:

28a54572e78b55d59271d512f280ae62.png

Orders表通过外键Id_P和Persons表进行关联。

inner join(内连接),在两张表进行连接查询时,只保留两张表中完全匹配的结果集。

我们使用inner join对两张表进行连接查询,sql如下:

SELECT p.LastName, p.FirstName, o.OrderNo
FROM Persons p
INNER JOIN Orders o
ON p.Id_P=o.Id_P and 1=1  --用and连接多个条件
ORDER BY p.LastName

查询结果集:

7d534135a6f903fa4b6a78add877d95a.png

此种连接方式Orders表中Id_P字段在Persons表中找不到匹配的,则不会列出来。

注意:单纯的select * from a,b是笛卡尔乘积。比如a表有5条数据,b表有3条数据,那么最后的结果有5*3=15条数据。

但是如果对两个表进行关联:select * from a,b where a.id = b.id 意思就变了,此时就等价于:

select * from a inner join b on a.id = b.id。 -- 即就是内连接。

但是这种写法并不符合规范,可能只对某些数据库管用,如sqlserver。推荐最好不要这样写。最好写成inner join的写法。

内连接查询 (select * from a join b on a.id = b.id) 与 关联查询 (select * from a , b where a.id = b.id)的区别

left join,在两张表进行连接查询时,会返回左表所有的行,即使在右表中没有匹配的记录。

我们使用left join对两张表进行连接查询,sql如下:

SELECT p.LastName, p.FirstName, o.OrderNo
FROM Persons p
LEFT JOIN Orders o
ON p.Id_P=o.Id_P
ORDER BY p.LastName

查询结果如下:

7386ac8fd66cdeac56c9ed2db0e638d8.png

可以看到,左表(Persons表)中LastName为Bush的行的Id_P字段在右表(Orders表)中没有匹配,但查询结果仍然保留该行。

right join,在两张表进行连接查询时,会返回右表所有的行,即使在左表中没有匹配的记录。

我们使用right join对两张表进行连接查询,sql如下:

SELECT p.LastName, p.FirstName, o.OrderNo
FROM Persons p
RIGHT JOIN Orders o
ON p.Id_P=o.Id_P
ORDER BY p.LastName

查询结果如下:

fe458c6181287b08060a3594970e4da8.png

Orders表中最后一条记录Id_P字段值为65,在左表中没有记录与之匹配,但依然保留。

full join,在两张表进行连接查询时,返回左表和右表中所有没有匹配的行。

我们使用full join对两张表进行连接查询,sql如下:

SELECT p.LastName, p.FirstName, o.OrderNo
FROM Persons p
FULL JOIN Orders o
ON p.Id_P=o.Id_P
ORDER BY p.LastName

查询结果如下:

43267e0c8fdb4416d460fb3665d40d05.png

查询结果是left join和right join的并集。

4、使用联合(UNION)来代替手动创建的临时表

MySQL从4.0版本开始支持union查询,他可以把需要使用临时表的两条或更多的select查询合在一个查询中。在客户端查询会话结束的时候,临时表会被自动删除,从而保证数据库整齐、高效。使用union来创建查询的时候,我们只需要用union作为关键字把多个select语句连接起来就可以了,要注意的是所有select语句中的字段数目要相同。

下面一个例子就演示了一个使用union额查询。

04ba98d969269b56d9a3eeb64ed0c406.png

当我们可以确认不可能出现重复结果集或者不在乎重复结果集的时候尽量使用union all而不是union,因为union和union all的差异主要是前者需要将两个或者多个结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的CPU运算,增大资源消耗及延迟。

5、事务

尽管我们可以使用子查询(Sub-Queries)、连接(JOIN)和联合(UNION)来创建各种各样的查询,但不是所有的数据库操作,都可以只用一条或少数几条就可以完成的。更多的时候是需要用一系列的语句来完成某种工作。但是在这种情况下,当这个语句块中的某一条语句运行出错的时候,整个语句块的操作就会变得不确定起来。

设想一下,要把某个数据同时插入两个相关联的表中,可能会出现这样的情况:第一个表中成功更新后,数据库突然出现意外状况,造成第二个表中的操作没有完成,这样就会造成数据的不完整,甚至会破坏数据库中的数据。要避免这种情况,就应该使用事务,它的作用是要么语句块中每条语句都操作成功,要么都失败。

换句话说,就是可以保持数据库中的数据的一致性和完整性。事务以BEGIN关键字开始,COMMIT关键字结束。在这之间的一条SQL语句操作失败,那么Rollback命令就可以把数据库恢复到begin开始之前的状态。

BEGIN; 
INSERTINTOsalesinfoSETCustomerID=14;
UPDATEinventorySETQuantity=11WHEREitem='book';
COMMIT;

事务的另一个作用是当多个用户同时使用相同的数据源时,他可以使用锁定数据库的方式来为用户提供一种安全的访问机制,这样可以保证用户的操作不被其它的用户所干扰。

一般来说,事务必须满足四个条件(ACID):原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability).

  • 原子性:一个事物(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始的状态,就像这个事务从来没有执行过一样。

  • 一致性:在事务开始之前和事务结束之后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。

  • 隔离性:数据库允许多个事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同的级别,包括读未提交(Read uncommitted)、读已提交(Read committed)、可重复读(repeateable read)和串行化(Serializable).

  • 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

事务的并发问题:

1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据就是脏数据

2、不可重复读:事务A多次读取同一事物,事务B在事务A多次读取的过程中,对数据做了更新并提交,导致事务A多次读取同一数据时,结果不一致。

3、幻读:系统管理员A将数据库中的所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

MySQL事务隔离级别:

事务隔离级别脏读不可重复读幻读
读未提交(read-uncommitted)
不可重复读(read-committed)
可重复读(repeatable-read)
串行化(serializable)

事务控制语句:

  • BEGIN或START TRANSACTION:显式的开启一个事物。

  • COMMIT:也可以使用COMMIT WORK,不过二者是等价的。COMMIT会提交事务,并使已对数据库进行的所有修改成为永久性的。

  • Rollback:也可以使用Rollback work,不过二者是等价的。回滚会结束用户的事务,并撤销正在进行的所有未提交的修改。

  • SAVEPOINT identifier:SAVEPOINT允许在事务中创建一个保存点,一个事务中可以有很多个SAVEPOINT;

  • RELEASE SAVEPOINT identifier:删除一个事物的保存点,当没有指定的保存点时,执行该语句会抛出一个异常。

  • ROLLBACK TO inditifier:把事务回滚到标记点。

  • SET TRANSACTION:用来设置事务的隔离级别。InnoDB存储引擎提供事务的隔离级别有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERLALIZABLE。

6、使用外键

锁定表的方法可以维护数据的完整性,但是他却不能保证数据的关联性。这个时候我们可以使用外键。例如:外键可以保证每一条销售记录都指向某一个存在的客户。

在这里,外键可以把customerinfo表中的customerid映射到salesinfo表中customerid,任何一条没有办法合法customerid的记录都不会被跟新或插入到salesinfo中.

CREATE TABLE customerinfo(customerid int primary key) engine = innodb;
CREATE  TABLE   salesinfo( salesid int not null,customerid  int not null, primary key(customerid,salesid),foreign key(customerid)  references  customerinfo(customerid) on delete cascade)engine = innodb;

注意例子中的参数“on delete cascade”.该参数保证当customerinfo表中的一条客户记录也会被自动删除。如果要在mysql中使用外键,一定要记住在创建表的时候将表的类型定义为事务安全表InnoDB类型。该类型不是mysql表的默认类型。定义的方法是在CREATE TABLE语句中加上engine=innoDB。

7、锁定表

尽管事务是维护数据库完整性的一个非常好的方法,但却因为他的独占性,有时会影响数据库的性能,尤其是很大的应用系统中。由于在事务执行的过程中,数据库将会被锁定,因此其他的用户请求只能暂时等待直到该事务结束。

如果一个数据库系统只有少数几个用户来使用,事务造成的影响不会成为太大的问题;但假设有成千上万的用户同时访问一个数据库系统,例如访问一个电子商务网站,就会产生比较严重的响应延迟。

其实,有些情况下我们可以通过锁定表的方式来获得更好的性能。下面的例子就是锁定表的方法来完成前面一个例子中事务的功能。

9c032a250612442b2801435c16613c0a.png

这里,我们用一个select语句取出初始数据,通过一些计算,用update语句将新值更新到表中。包含有WRITE关键字的LOCKTABLE语句可以保证在UNLOCKTABLES命令被执行之前,不会有其他访问来对inventory进行插入、更新或者删除的操作。

8、使用索引

索引是提高数据库性能的常用方法,他可以令数据库服务器比没有索引快得多的速度检索特定的行,尤其是在查询语句当中包含有MAX(),MIN()和ORDERBY这些命令的时候,性能提高更为明显。

那该对那些字段进行索引呢?

一般来说,索引应该建立在那些将用于join,where判断和orderby排序的字段上。尽量不要对数据库中某个含有大量重复的值的字段建立索引,对于一个ENUM类型的字段来说,出现大量重复值是很有可能的情况。

例如customerinfo中的“province”..字段,在这样的字段上建立索引将不会有什么帮助;相反,还有可能降低数据库的性能。我们在创建表的时候可以同时创建合适的索引,也可以使用ALTERTABLE或CREATEINDEX在以后创建索引。

此外,MySQL从版本3.23.23开始支持全文索引和搜索。全文索引在MySQL中是一个FULLTEXT类型索引,但仅能用于MyISAM类型的表。对于一个大的数据库,将数据装载到一个没有FULLTEXT索引的表中,然后再使用ALTERTABLE或CREATEINDEX创建索引,将是非常快的。但如果将数据装载到一个已经有FULLTEXT索引的表中,执行过程将会非常慢。

9、优化de的查询语句

  • 不使用子查询

    例:SELECT * FROM t1 WHERE id (SELECT id FROM t2 WHERE name=’hechunyang’);

    子查询在MySQL5.5版本里,内部执行计划器是这样执行的:先查外表再匹配内表,而不是先查内表t2,当外表的数据很大时,查询速度会非常慢。

    在MariaDB10/MySQL5.6版本里,采用join关联方式对其进行了优化,这条SQL会自动转换为

    SELECT t1.* FROM t1 JOIN t2 ON t1.id = t2.id;

    但请注意的是:优化只针对SELECT有效,对UPDATE/DELETE子查询无效,固生产环境应避免使用子查询

  • 避免函数索引

    例:

    SELECT * FROM t WHERE YEAR(d) >= 2016;

    由于MySQL不像Oracle那样支持函数索引,即使d字段有索引,也会直接全表扫描。

    应改为—–>

    SELECT * FROM t WHERE d >= '2016-01-01';
  • 用IN来替换OR

    低效查询

    SELECT * FROM t WHERE LOC_ID = 10 OR LOC_ID = 20 OR LOC_ID = 30;

    —–> 高效查询

    SELECT * FROM t WHERE LOC_IN IN (10,20,30);
  • LIKE双百分号无法使用到索引

    SELECT * FROM t WHERE name LIKE ‘%de%’;

    —–>

    SELECT * FROM t WHERE name LIKE ‘de%’;

    目前只有MySQL5.7支持全文索引(支持中文)

  • 读取适当的记录LIMIT M,N

    SELECT * FROM t WHERE 1;

    —–>

    SELECT * FROM t WHERE 1 LIMIT 10;
  • 避免数据类型不一致

    SELECT * FROM t WHERE id = ’19’;

    —–>

    SELECT * FROM t WHERE id = 19;
  • 分组统计可以禁止排序

    SELECT goods_id,count(*) FROM t GROUP BY goods_id;

    默认情况下,MySQL对所有GROUP BY col1,col2…的字段进行排序。如果查询包括GROUP BY,想要避免排序结果的消耗,则可以指定ORDER BY NULL禁止排序。另外,MySQL 系列面试题和答案全部整理好了,微信搜索Java技术栈,在后台发送:面试,可以在线阅读。

    —–>

    SELECT goods_id,count(*) FROM t GROUP BY goods_id ORDER BY NULL;
  • 避免随机取记录

    SELECT * FROM t1 WHERE 1=1 ORDER BY RAND() LIMIT 4;

    MySQL不支持函数索引,会导致全表扫描 —–>

    SELECT * FROM t1 WHERE id >= CEIL(RAND()*1000) LIMIT 4;
  • 禁止不必要的ORDER BY排序

    SELECT count(1) FROM user u LEFT JOIN user_info i ON u.id = i.user_id WHERE 1 = 1 ORDER BY u.create_time DESC;

    —–>

    SELECT count(1) FROM user u LEFT JOIN user_info i ON u.id = i.user_id;
  • 批量INSERT插入

    INSERT INTO t (id, name) VALUES(1,’Bea’);
    INSERT INTO t (id, name) VALUES(2,’Belle’);
    INSERT INTO t (id, name) VALUES(3,’Bernice’);

    —–>

    INSERT INTO t (id, name) VALUES(1,’Bea’), (2,’Belle’),(3,’Bernice’);

来源 | 臆想的一只猫 | urls.press/0Y9x0

END

往期推荐

b87acb050b004b0418130ac7ff65036a.png

MyBatis原生批量插入的坑与解决方案!


f95fbcef7ecd7d93f5890cb9e450868b.png

面试官:怎么解决MySQL中的死锁问题?


f3c38ade0355652198b4e2d4202b7243.png

再见收费的Navicat!操作所有数据库靠它就够了!


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/544516.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

containsall_Java Vector containsAll()方法与示例

containsall向量类containsAll()方法 (Vector Class containsAll() method) containsAll() method is available in java.util package. containsAll()方法在java.util包中可用。 containsAll() method is used to test the existence of all the elements of this Vector in t…

Excel的规划求解【详细步骤】

本文目录1.说明2.准备加载项步骤1步骤2步骤33.线性规划问题步骤4步骤5步骤61.说明 使用Lingo程序也可以实现线性规划、非线性规划以及0-1规划,但是在缺少Lingo程序的情况下,我们使用Excel照样可以很容易地完成。在这里我给大家提供了解决此类问题的详细…

4 种方法!检查字符串是否为合法的日期格式

哈喽大家好,今天咱们来讲一下,Java 中如何检查一个字符串是否是合法的日期格式?为什么要检查时间格式?后端接口在接收数据的时候,都需要进行检查。检查全部通过后,才能够执行业务逻辑。对于时间格式&#x…

linux下tomcat无法显示图片验证码

在catalina.sh中的-Djava.io.tmpdir"$CATALINA_TMPDIR" \后一行增加 -Djava.awt.headlesstrue \ 共8处转载于:https://blog.51cto.com/zhangfeikr/1673312

Java Throwable toString()方法与示例

Throwable类toString()方法 (Throwable Class toString() method) toString() Method is available in java.lang package. toString()方法在java.lang包中可用。 toString() Method is used to return a short description of the exception. toString()方法用于返回对该异常的…

【Matlab】根据图生成带权邻接矩阵,并求出最短路径

目录图的简介无向图(Graph)生成带权邻接矩阵求两点最短路径有向图(Digraph)生成带权邻接矩阵求最短路径图的简介 图是拓扑学中的一个重要概念,分为无向图和有向图两种。图有两个重要属性,即点(…

阿里二面:为什么要分库分表?

在高并发系统当中,分库分表是必不可少的技术手段之一,同时也是BAT等大厂面试时,经常考的热门考题。你知道我们为什么要做分库分表吗?这个问题要从两条线说起:垂直方向 和 水平方向。1 垂直方向垂直方向主要针对的是业务…

java scanner_Java Scanner nextDouble()方法与示例

java scanner扫描仪类的nextDouble()方法 (Scanner Class nextDouble() method) nextDouble() method is available in java.util package. nextDouble()方法在java.util包中可用。 nextDouble() method is used to scans the next token of the input scanned into a double v…

Java 中 List 分片的 5 种方法!

作者 | 王磊来源 | Java中文社群(ID:javacn666)转载请联系授权(微信ID:GG_Stone)前些天在实现 MyBatis 批量插入时遇到了一个问题,当批量插入的数据量比较大时,会导致程序执行报错&a…

Matlab仿真炮弹飞行轨迹——探究射弹参数对飞行轨迹的影响

目录1.分析炮弹受力2.设定参数并仿真3.通过仿真寻找最佳射弹速度3.1.射弹角度的影响3.2.射弹速率的影响3.3.炮弹属性和空气的影响3.3.1.空气阻力系数的影响3.3.2.炮弹质量的影响1.分析炮弹受力 假设炮弹在飞行过程中可以看成质点,运动时仅考虑初始速度、重力加速度…

EL与JSTL注意事项汇总

EL使用表达式(5一个 问题) JSTL使用标签(5问题) 什么是EL。它可以用做? EL全名Expression Language在JSP使用页面 格公式${表达式} 样例${requestScope.customer.id} 功能: 1.才四个域对象中取出属性数据显…

nextboolean()_Java Random nextBoolean()方法与示例

nextboolean()随机类nextBoolean()方法 (Random Class nextBoolean() method) nextBoolean() method is available in java.util package. nextBoolean()方法在java.util包中可用。 nextBoolean() method is used to return the next pseudo-random Boolean value from this Ra…

50行代码,搞定敏感数据读写!

每天早上七点三十,准时推送干货一、介绍在实际的软件系统开发过程中,由于业务的需求,在代码层面实现数据的脱敏还是远远不够的,往往还需要在数据库层面针对某些关键性的敏感信息,例如:身份证号、银行卡号、…

【Python】导入资源管理器的文件列表(计算文件和文件夹大小)

文章目录1.按照扩展名进行分类2.导出文件的大小3.计算文件夹大小4.分类到字典5.完整代码及效果1.按照扩展名进行分类 使用Python查询一个路径下的所有文件可以借助glob模块以及os模块。 在导入文件列表之前,我们需要指定我们的操作目录,操作目录一经指定…

SublimeText2使用笔记

将Sublime Text2 加入右键菜单(转) 1. 运行中输入 regedit 打开注册表 2. 在HKEY_CLASSES_ROOT/*/shell/ 下新建’项’ ,名称自己觉得.我用的是Sublime Text 3. 在 Sublime Text 下 新建’项’ 名字:command (这个貌似不可以修改) 4. 点击 command ,在右…

Java Package getPackage()方法与示例

包类的getPackage()方法 (Package Class getPackage() method) getPackage() method is available in java.lang package. getPackage()方法在java.lang包中可用。 getPackage() method is used to search a package by given package name in the callers ClassLoader instanc…

HashMap 中的一个“坑”!

作者 | 王磊来源 | Java中文社群(ID:javacn666)转载请联系授权(微信ID:GG_Stone)最近公司新来了一个小伙伴,问了磊哥一个比较“奇怪”的问题,这个问题本身的难度并不大,但…

【Python】交互式界面创建函数

文章目录简介规则思路示例代码与运行效果简介 运行Python时,在程序运行过程中手动输入一个函数表达式,并将其作为一个函数进行后续的调用工作,类似于Matlab里面的匿名函数。这个功能使用传统语言会相当麻烦,当然,我也…

原生 js前端路由系统实现3之代码 构建工具 和 querystring功能

为什么80%的码农都做不了架构师?>>> 构建 目前前端构建工具流行的是 grunk.js 功能是大而全,但往往大而全的东西为了多样性 需要做额外的配置 我还是想要有一个专门为自己特性项目而生构建工具 我不想加载第三方的node模块,也不…

单例模式,真不简单

前言单例模式无论在我们面试,还是日常工作中,都会面对的问题。但很多单例模式的细节,值得我们深入探索一下。这篇文章透过单例模式,串联了多方面基础知识,非常值得一读。1 什么是单例模式?单例模式是一种非…