MySQL 쿼리 속도를 향상시키는 방법: 1. 가장 적합한 필드 속성을 선택합니다. 2. JOIN을 사용하여 하위 쿼리를 교체합니다. 3. UNION을 사용하여 수동으로 생성된 임시 테이블을 교체합니다. 5. 쿼리를 최적화하고 피합니다. 6. 임시 테이블 등 대신 테이블 변수를 사용해 보십시오.
이 튜토리얼의 운영 환경: windows7 시스템, mysql8 버전, Dell G3 컴퓨터.
프로그래머 입장에서
쿼리 문이 제대로 작성되지 않았습니다
인덱스가 빌드되지 않았거나, 인덱스가 합리적으로 빌드되지 않았거나 인덱스가 잘못되었습니다
관련 쿼리에 조인이 너무 많습니다
서버 관점에서
1. 가장 적합한 필드 속성을 선택하세요
MySQL은 대용량 데이터에 대한 액세스를 잘 지원하지만 일반적으로 데이터베이스의 테이블이 작을수록 쿼리 실행 속도가 빨라집니다. 것입니다. 따라서 테이블을 생성할 때 더 나은 성능을 얻기 위해 테이블의 필드 너비를 최대한 작게 설정할 수 있습니다.
예를 들어 우편번호 필드를 정의할 때 CHAR(255)로 설정하면 분명히 데이터베이스에 불필요한 공간이 추가됩니다. CHAR(6)을 사용하면 쉽게 가능하므로 VARCHAR을 사용해도 중복됩니다. 마찬가지로 가능하다면 BIGIN 대신 MEDIUMINT를 사용하여 정수 필드를 정의해야 합니다.
효율성을 높이는 또 다른 방법은 가능할 때마다 필드를 NOTNULL로 설정하여 나중에 쿼리를 실행할 때 데이터베이스가 NULL 값을 비교할 필요가 없도록 하는 것입니다.
"지방" 또는 "성별"과 같은 일부 텍스트 필드의 경우 ENUM 유형으로 정의할 수 있습니다.MySQL에서는 ENUM 유형이 숫자 데이터로 처리되기 때문에 숫자 데이터는 텍스트 유형보다 훨씬 빠르게 처리됩니다. 이러한 방식으로 데이터베이스 성능을 향상시킬 수 있습니다.
2. 하위 쿼리(Sub-Queries) 대신 조인(JOIN)을 사용하세요
MySQL은 4.1부터 SQL 하위 쿼리를 지원합니다. 이 기술을 사용하면 SELECT 문을 사용하여 쿼리 결과의 단일 열을 만든 다음 이 결과를 다른 쿼리의 필터 조건으로 사용할 수 있습니다. 예를 들어 기본 고객 정보 테이블에서 주문이 없는 고객을 삭제하려면 하위 쿼리를 사용하여 먼저 판매 정보 테이블에서 주문을 한 모든 고객의 ID를 검색한 후 그 결과를 다음으로 전달하면 됩니다.DELETE FROM customerinfo WHERE CustomerID NOT IN (SELECT CustomerID FROM salesinfo)
subquery를 사용하면 논리적으로 한 번에 여러 단계를 완료해야 하는 많은 SQL 작업을 완료할 수 있으며 트랜잭션이나 테이블 잠금을 피할 수도 있고 작성하기도 쉽습니다. 그러나 경우에 따라 하위 쿼리를 보다 효율적인 조인(JOIN)으로 대체할 수 있습니다.. 예를 들어, 주문 기록이 없는 모든 사용자를 가져오려고 한다고 가정하면 다음 쿼리를 사용하여 이를 완료할 수 있습니다.
SELECT * FROM customerinfo WHERE CustomerID NOT IN (SELECTC ustomerID FROM salesinfo)
연결(JOIN)을 사용하는 경우... 이 쿼리를 완료하려면 속도가 빨라집니다. 훨씬 더 빨라지세요. 특히 salesinfo 테이블에 CustomerID에 대한 인덱스가 있는 경우 쿼리는 다음과 같습니다.
SELECT * FROM customerinfo LEFT JOIN salesinfo ON customerinfo.CustomerID=salesinfo.CustomerID WHERE salesinfo.CustomerID ISNULL
3. 유니온(UNION)을 사용하여 수동으로 생성된 임시 테이블 교체
MySQL은 버전 4.0부터 유니온 쿼리를 지원합니다. 이 유니온 쿼리는 임시 테이블 사용이 필요한 두 개 이상의 선택 쿼리를 병합할 수 있습니다. . 클라이언트의 쿼리 세션이 종료되면 데이터베이스가 깔끔하고 효율적으로 유지되도록 임시 테이블이 자동으로 삭제됩니다. Union을 사용하여 쿼리를 생성하는 경우 여러 Select 문을 연결하는 키워드로 UNION만 사용하면 됩니다. 모든 Select 문의 필드 수는 동일해야 합니다. 다음 예에서는 UNION을 사용한 쿼리를 보여줍니다.SELECT Name,Phone FROM client UNION SELECT Name,BirthDate FROM author UNION SELECT Name,Supplier FROM product
4. 업무
尽管我们可以使用子查询(Sub-Queries)、连接(JOIN)和联合(UNION)来创建各种各样的查询,但不是所有的数据库操作都可以只用一条或少数几条SQL语句就可以完成的。更多的时候是需要用到一系列的语句来完成某种工作。但是在这种情况下,当这个语句块中的某一条语句运行出错的时候,整个语句块的操作就会变得不确定起来。设想一下,要把某个数据同时插入两个相关联的表中,可能会出现这样的情况:第一个表中成功更新后,数据库突然出现意外状况,造成第二个表中的操作没有完成,这样,就会造成数据的不完整,甚至会破坏数据库中的数据。要避免这种情况,就应该使用事务,它的作用是:要么语句块中每条语句都操作成功,要么都失败。换句话说,就是可以保持数据库中数据的一致性和完整性。事物以BEGIN关键字开始,COMMIT关键字结束。在这之间的一条SQL操作失败,那么,ROLLBACK命令就可以把数据库恢复到BEGIN开始之前的状态。
BEGIN; INSERT INTO salesinfo SET CustomerID=14; UPDATE inventory SET Quantity=11 WHERE item='book'; COMMIT;
事务的另一个重要作用是当多个用户同时使用相同的数据源时,它可以利用锁定数据库的方法来为用户提供一种安全的访问方式,这样可以保证用户的操作不被其它的用户所干扰。
5、锁定表
尽管事务是维护数据库完整性的一个非常好的方法,但却因为它的独占性,有时会影响数据库的性能,尤其是在很大的应用系统中。由于在事务执行的过程中,数据库将会被锁定,因此其它的用户请求只能暂时等待直到该事务结束。如果一个数据库系统只有少数几个用户来使用,事务造成的影响不会成为一个太大的问题;但假设有成千上万的用户同时访问一个数据库系统,例如访问一个电子商务网站,就会产生比较严重的响应延迟。
其实,有些情况下我们可以通过锁定表的方法来获得更好的性能。下面的例子就用锁定表的方法来完成前面一个例子中事务的功能。
LOCK TABLE inventory WRITE SELECT Quantity FROM inventory WHERE Item='book'; ... UPDATE inventory SET Quantity=11 WHERE Item='book'; UNLOCKTABLES
这里,我们用一个select语句取出初始数据,通过一些计算,用update语句将新值更新到表中。包含有WRITE关键字的LOCKTABLE语句可以保证在UNLOCKTABLES命令被执行之前,不会有其它的访问来对inventory进行插入、更新或者删除的操作。
6、使用外键
锁定表的方法可以维护数据的完整性,但是它却不能保证数据的关联性。这个时候我们就可以使用外键。
例如,外键可以保证每一条销售记录都指向某一个存在的客户。在这里,外键可以把customerinfo表中的CustomerID映射到salesinfo表中CustomerID,任何一条没有合法CustomerID的记录都不会被更新或插入到salesinfo中。
CREATE TABLE customerinfo(CustomerIDINT NOT NULL,PRIMARYKEY(CustomerID))TYPE=INNODB; CREATE TABLE salesinfo(SalesIDNT NOT NULL,CustomerIDINT NOT NULL, PRIMARYKEY(CustomerID,SalesID), FOREIGNKEY(CustomerID) REFERENCES customerinfo(CustomerID) ON DELETE CASCADE)TYPE=INNODB;
注意例子中的参数“ON DELETE CASCADE”。该参数保证当customerinfo表中的一条客户记录被删除的时候,salesinfo表中所有与该客户相关的记录也会被自动删除。
7、使用索引
索引是提高数据库性能的常用方法,它可以令数据库服务器以比没有索引快得多的速度检索特定的行,尤其是在查询语句当中包含有MAX(),MIN()和ORDERBY这些命令的时候,性能提高更为明显。
那该对哪些字段建立索引呢?
一般说来,索引应建立在那些将用于JOIN,WHERE判断和ORDERBY排序的字段上。尽量不要对数据库中某个含有大量重复的值的字段建立索引。对于一个ENUM类型的字段来说,出现大量重复值是很有可能的情况
例如customerinfo中的“province”..字段,在这样的字段上建立索引将不会有什么帮助;相反,还有可能降低数据库的性能。我们在创建表的时候可以同时创建合适的索引,也可以使用ALTERTABLE或CREATEINDEX在以后创建索引。此外,MySQL从版本3.23.23开始支持全文索引和搜索。全文索引在MySQL中是一个FULLTEXT类型索引,但仅能用于MyISAM类型的表。对于一个大的数据库,将数据装载到一个没有FULLTEXT索引的表中,然后再使用ALTERTABLE或CREATEINDEX创建索引,将是非常快的。但如果将数据装载到一个已经有FULLTEXT索引的表中,执行过程将会非常慢。
8、优化的查询语句
绝大多数情况下,使用索引可以提高查询的速度,但如果SQL语句使用不恰当的话,索引将无法发挥它应有的作用。
1、应尽量避免在 where 子句中使用 != 或者 <> 操作符,否则将引擎放弃使用索引而进行全表扫描。
2、应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null;
可以在 num 上设置默认值 0 ,确保表中 num 列没有 null 值,然后这样查询:
select id from t where num = 0;
3、查询语句的查询条件中只有OR关键字,并且OR前后的两个条件中的列都是索引时,查询中才使用索引。否则,查询将不使用索引。
4、对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
5、前导模糊查询不能利用索引(like '%XX'或者like '%XX%'),可以使用索引覆盖避免。
6、in 和 not in 也要慎用,否则会导致全表扫描。如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
7、如果在 where 子句中使用参数,也会导致全表扫描。因为 SQL 只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择到运行时;它必须在编译时进行选择。然而,如果在编译时简历访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
select id from t where num=@num
可以改为强制查询使用索引:
select id from t with(index(索引名)) where num=@num
8、应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2 = 100;
应改为:
select id from t where num = 100 * 2;
9、应尽量避免在 where 子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name, 1, 3) = ’abc’–name; //以abc开头的id select id from t where datediff(day,createdate,’2005-11-30′) = 0–’2005-11-30′; //生成的id
应改为:
select id from t where name like ‘abc%’ select id from t where createdate >= ’2005-11-30′ and createdate < ’2005-12-1′;
10、不要在 where 子句中的 “=” 左边进行函数,算术运算或者其他表达式运算,否则系统将可能无法正确使用索引。
11、在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
12、很多时候用 exists 代替 in 是一个好的选择:
select num from a where num in(select num from b);
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num);
13、索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
14、并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段 sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。
15、尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会 逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
16、尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。较一次就够了。
17、任何地方都不要使用 select * from t
,用具体的字段列表代替 *
,不要返回用不到的任何字段。
18、尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。
19、避免频繁创建和删除临时表,以减少系统表资源的消耗。
20、临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。
21、在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先 create table,然后 insert。
22、如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。
23、尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。
24. 커서 기반 방법이나 임시 테이블 방법을 사용하기 전에 먼저 문제를 해결하기 위한 집합 기반 솔루션을 찾아야 합니다. 일반적으로 집합 기반 방법이 더 효과적입니다.
25. 임시 테이블과 마찬가지로 커서를 사용할 수 없습니다. 작은 데이터 세트에 FAST_FORWARD 커서를 사용하는 것이 다른 행별 처리 방법보다 나은 경우가 많습니다. 특히 필요한 데이터를 얻기 위해 여러 테이블을 참조해야 하는 경우에는 더욱 그렇습니다. 결과 집합에 "합계"를 포함하는 루틴은 일반적으로 커서를 사용하는 것보다 빠릅니다. 개발 시간이 허락한다면 커서 기반 방법과 세트 기반 방법을 모두 시도하여 어떤 방법이 더 효과적인지 확인할 수 있습니다.
26. 모든 저장 프로시저 및 트리거의 시작 부분에 SET NOCOUNT ON을 설정하고 끝 부분에 SET NOCOUNT OFF를 설정합니다. 저장 프로시저 및 트리거의 각 문 후에 클라이언트에 DONE_IN_PROC 메시지를 보낼 필요가 없습니다.
27. 클라이언트에 많은 양의 데이터를 반환하지 않도록 하세요. 데이터 양이 너무 많으면 해당 요구 사항이 합리적인지 고려해야 합니다.
28. 대규모 트랜잭션 작업을 피하고 시스템 동시성을 개선하세요.
【관련 추천: mysql 비디오 튜토리얼】
위 내용은 mysql의 쿼리 속도를 향상시키는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!