当前位置: 首页 > 后端技术 > Java

《MySQL数据库》优化的9个正确姿势

时间:2023-04-01 21:24:24 Java

给大家送上下面的java学习资料,文末有获取方法1、选择最合适的字段属性Mysql是关系型数据库,可以很好地支持大量数据的存储,但一般来说,数据库中的表越小,对其执行的查询就越快。因此,在创建表的时候,为了获得更好的性能,我们可以让表中字段的宽度尽可能的小。例如:在定义邮政编码字段时,如果设置为char(255),显然给数据库增加了不必要的空间,甚至使用varchar也是多余的,因为使用char(6)可以很好的完成任务。同样,如果可能,我们应该使用MEDIUMINT而不是BIGINT来定义整数字段。2.尝试将字段设置为NOTNULL。如果可能,尽量将该字段设置为NOTNULL,这样以后在执行查询时,数据库就不需要比较NULL值了。对于一些文本字段,比如“省”或“性别”,我们可以将它们定义为ENUM(枚举)类型。因为在MySQL中,ENUM类型被当作数字数据处理,数字数据的处理速度比文本类型快很多。通过这种方式我们可以提高数据库的性能。3、使用连接(JOIN)代替子查询(Sub-Queries)MySQL从4.1开始支持SQL子查询。这种技术可以使用select语句创建一个单例查询结果,然后在另一个查询中使用这个结果作为过滤条件。例如:如果我们要删除客户基本信息表中没有任何订单的客户,我们可以使用子查询从销售信息表中取出所有下单的客户id,然后将结果传递给主查询,如如下图注意:如果使用JOIN来完成这个工作,速度会快很多,尤其是如果salesinfo表中有CustomerID上的索引,性能会更好。查询如下:JOIN之所以效率更高一些,是因为MySQL不需要在内存中创建临时表来完成这个逻辑上需要两步的查询。此外,如果您的应用程序有很多JOIN查询,您应该确保两个表中的JOIN字段都已建立索引。这样,MySQL就会启动一个机制,为你优化JOIN的SQL语句。并且这些用于JOIN的字段应该是同一类型的。例如:如果你想JOIN一个DECIMAL字段和一个INT字段,MySQL不能使用它们的索引。对于那些STRING类型,还需要相同的字符集。(两个表的字符集可能不一样)。内连接也称为等价连接,左/右连接是外连接。SELECTA.id,A.name,B.id,B.nameFROMALEFTJOINBONA.id=B.id;``````SELECTA.id,A.name,B.id,B.nameFROMARIGHTJOINONBA.id=B.id;``````SELECTA.id,A.name,B.id,B.nameFROMAINNERJOINONA.id=B.id;很多方面已经证明,innerjoin的性能更快,因为innerjoin是等价连接,返回的行数可能比较少。但我们必须记住,有些语句隐式使用等价连接,例如:SELECTA.id,A.name,B.id,B.nameFROMA,BWHEREA.id=B.id;建议:使用innerjoin连接,使用innerjoin连接。sql中有四种连接查询方式:innerjoin、leftjoin、rightjoin、fulljoin。它们之间没有太大区别,只是查询出来的结果不同而已。例如,我们有两个表:Orders表通过外键Id\_P与Persons表相关联。内连接(innerjoin),当连接和查询两个表时,只保留两个表中完全匹配的结果集。我们使用innerjoin来连接和查询两个表。sql如下:SELECTp.LastName,p.FirstName,o.OrderNoFROMPersonspINNERJOINOrdersoONp.Id_P=o.Id_Pand1=1--useandtoconnectmultipleConditionORDERBYp.LastName查询结果集:这种连接方式下,Orders表中的Id\_P字段在Persons表中无法匹配到,不会被列出来。注意:简单的select*froma,b是笛卡尔积。比如a表有5条数据,b表有3条数据,那么最后的结果有5*3=15条数据。但是如果关联两张表:select*froma,bwherea.id=b.id,意思就变了,相当于:select*fromainnerjoinbona.id=b.id。——也就是内在联系。但是这种写法不符合规范,可能只对部分数据库有效,比如sqlserver。建议不要这样写。最好写成innerjoin。内连接查询(select*fromajoinbona.id=b.id)和关联查询(select*froma,bwherea.id=b.id)的区别左连接,两表之间的连接查询,返回左表中的所有行,即使右表中没有匹配的记录。我们使用leftjoin查询两张表,sql如下:SELECTp.LastName,p.FirstName,o.OrderNoFROMPersonspLEFTJOINOrdersoONp.Id_P=o.Id_PORDERBYp.LastName查询结果如下:可以看到,左表(Persons表)中LastName为Bush的行的Id\_P字段在右表(Orders表)中不匹配,但是查询结果仍然保留了这一行。右连接,当连接两个表时,会返回右表的所有行,即使左表没有匹配的记录。我们使用右连接来查询两个表之间的连接。SQL如下:SELECTp.LastName,p.FirstName,o.OrderNoFROMPersonspRIGHTJOINOrdersoONp.Id_P=o.Id_PORDERBYp.LastName查询结果如下:Orders表中最后一条记录Id\_P字段值为65,左表中没有与其匹配的记录,但仍保留。fulljoin,当连接和查询两个表时,返回左右表中所有不匹配的行。我们使用全连接来连接和查询两个表。SQL如下:SELECTp.LastName,p.FirstName,o.OrderNoFROMPersonspFULLJOINOrdersoONp.Id_P=o.Id_PORDERBYp.LastName查询结果如下:左连接右连接联合。4.使用联合(UNION)代替手动创建的临时表。MySQL从4.0版本开始支持联合查询。它可以将需要使用临时表的两个或多个选择查询合并为一个查询。当客户端查询会话结束时,临时表会自动删除,以保证数据库的整洁和高效。在使用union创建查询时,我们只需要使用union作为关键字来连接多个select语句。需要注意的是,所有select语句中的字段数必须相同。以下示例演示了使用联合的查询。当我们可以确认不可能出现重复结果集或者不关心重复结果集时,尽量使用unionall而不是union,因为union和unionall的区别在于前者需要先合并两个或多个结果集发挥独特性。过滤操作,其中涉及到排序,会增加大量的CPU操作,增加资源消耗和延迟。推荐:Java进阶视频资源5.事务虽然我们可以使用子查询(Sub-Queries)、连接(JOIN)和联合(UNION)来创建各种查询,但并不是所有的数据库操作都可以只用一个就可以完成或几个。更多的时候是需要用一系列的语句来完成某种工作。但是在这种情况下,当语句块中的某条语句运行错误时,整个语句块的运行就变得不确定了。想象一下,你想同时向两个相关的表中插入某个数据。可能会出现这样的情况:第一个表更新成功后,数据库突然遇到意外情况,导致第二个表中的操作无法完成,这将导致数据不完整,甚至破坏数据库中的数据。为避免这种情况,应使用事务。它的作用是语句块中的每条语句要么成功,要么全部失败。也就是说,可以保持数据库中数据的一致性和完整性。事务以BEGIN关键字开始,以COMMIT关键字结束。如果SQL语句操作失败,Rollback命令可以将数据库恢复到开始前的状态。开始;INSERTINTOsalesinfoSETCustomerID=14;UPDATEinventorySETQuantity=11WHEREitem='book';COMMIT;事务的另一个作用是,当多个用户同时使用同一个数据源时,他可以锁定数据库,为用户提供安全的访问机制,可以保证用户的操作不会受到其他用户的干扰。一般来说,事务必须满足四个条件(ACID):原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。性:交易中的所有操作要么完成,要么未完成,不会在中间的某个环节结束。如果事务执行过程中出现错误,则会回滚(Rollback)到事务开始时的状态,就好像事务从未执行过一样。一致性:数据库的完整性在事务开始之前和事务结束之后都不会受到损害。这意味着写入的数据必须完全符合所有预设的规则,包括数据的准确性、序列性,后续的数据库才能自发地完成预定的工作。隔离性:数据库允许多个事务同时读取、写入和修改其数据的能力。隔离可以防止多个事务并发执行时交叉执行导致的数据不一致。事务隔离分为不同的级别,包括未提交读(Readuncommitted)、已提交读(Readcommitted)、可重复读(repeateableread)和可序列化(Serializable)。持久性:事务结束后,数据的修改是永久性的,即使系统出现故障也不会丢失。事务的并发问题:1、脏读:事务A读取事务B更新的数据,然后B回滚操作,则A读取的数据为脏数据2、不可重复读:事务A读取相同的数据multipletimes事物,事务B在事务A的多次读取过程中更新并提交了数据,导致事务A多次读取同一个数据时结果不一致。3、幻读:系统管理员A将数据库中所有学生的成绩由具体分数改为ABCDE成绩,但系统管理员B此时插入了一条具体分数的记录。当系统管理员A完成修改后,发现还有一条记录没有修改,就好像出现了幻觉,这叫做幻读。总结:不可重复读和幻读很容易混淆。不可重复读重在修改,幻读重在增删改查。解决不可重复读的问题,只需要锁定满足条件的行即可。解决幻读需要锁定表MySQL事务隔离级别事务控制语句:BEGIN或STARTTRANSACTION:显式打开一个东西。COMMIT:也可以使用COMMITWORK,但两者是等价的。COMMIT提交事务并使对数据库所做的所有修改永久化。回滚:也可以使用回滚工作,但两者是等价的。回滚结束用户的事务并撤消正在进行的任何未提交的修改。SAVEPOINT标识符:SAVEPOINT允许在事务中创建保存点。一个事务中可以有多个SAVEPOINT;RELEASESAVEPOINT标识符:删除事物的保存点。当没有指定保存点时,执行该语句会抛出异常。ROLLBACKTOinditifier:将事务回滚到标记点。SETTRANSACTION:用于设置事务的隔离级别。InnoDB存储引擎提供的事务隔离级别为READUNCOMMITTED、READCOMMITTED、REPEATABLEREAD和SERLALIZABLE。6、使用外键锁表的方法可以保持数据的完整性,但不能保证数据的相关性。这时候我们就可以使用外键了。例如:一个外键可以确保每条销售记录都指向一个现有的客户。这里,外键可以将customerinfo表中的customerid映射到salesinfo表中的customerid,任何没有合法customerid的记录都不会被更新或插入到salesinfo中。CREATETABLEcustomerinfo(customeridintprimarykey)engine=innodb;``````CREATETABLEsalesinfo(salesidintnotnull,customeridintnotnull,主键(customerid,salesid),外键(customerid)引用customerinfo(customerid)删除级联)engine=innodb;参数“关于删除级联”。该参数确保当customerinfo表中的一条客户记录被自动删除时。如果要在mysql中使用外键,一定要记得在建表的时候把表的类型定义为事务安全表InnoDB类型。这种类型不是mysql表的默认类型。定义的方法是在CREATETABLE语句中加入engine=innoDB。7.锁定表虽然事务是维护数据库完整性的一种非常好的方式,但是它们有时会因为其独占性而影响数据库性能,尤其是在大型应用系统中。由于在事务执行过程中数据库会被锁定,其他用户请求只能暂时等待事务结束。如果一个数据库系统只被少数用户使用,事务的影响不会是一个大问题;但是如果成千上万的用户同时访问一个数据库系统,比如访问一个电子商务网站,就会出现比较严重的响应延迟。事实上,在某些情况下我们可以通过锁定表来获得更好的性能。下面的例子就是锁表的方法,完成上一个例子中事务的功能。在这里,我们使用select语句来获取初始数据,并通过一些计算,使用update语句将新值更新到表中。包含WRITE关键字的LOCKTABLE语句确保在执行UNLOCKTABLES命令之前没有其他访问会插入、更新或删除库存。8、使用索引建立索引是提高数据库性能的常用方法。它可以使数据库服务器检索特定行比没有索引快得多,尤其是在包含MAX()、MIN()和ORDERBY等命令的查询语句中,性能提升更加明显。那么哪些字段应该被索引呢?一般情况下,索引应该建立在将要用于join的字段上,where判断和orderby排序。尽量不要索引数据库中包含大量重复值的字段。对于一个ENUM类型的字段,很可能会有大量的重复值。比如customerinfo中的“province”..字段,在这样的字段上建索引也于事无补;相反,它还可能降低数据库的性能。我们可以在建表的时候同时创建一个合适的索引,也可以在后面使用ALTERTABLE或者CREATEINDEX来创建索引。此外,MySQL从3.23.23版本开始支持全文索引和搜索。全文索引是MySQL中的FULLTEXT类型的索引,但它只能用于MyISAM类型的表。对于大型数据库,将数据加载到没有全文索引的表中,然后使用ALTERTABLE或CREATEINDEX创建索引是非常快的。但是如果将数据加载到一个已经有全文索引的表中,执行过程会很慢。推荐:Java进阶视频资源9.优化查询语句1不使用子查询示例:SELECT*FROMt1WHEREid(SELECTidFROMt2WHEREname='hechunyang');子查询在MySQL5.5版本中,内部执行计划器是这样执行的:先查外表再匹配内表,而不是先查内表t2。当外表的数据很大时,查询速度会很慢。在MariaDB10/MySQL5.6版本中,通过使用join关联方式进行了优化,这条SQL会自动转换为SELECTt1.*FROMt1JOINt2ONt1.id=t2.id;但请注意:优化只对SELECT有效,对UPDATE/DELETE子查询无效,实体生产环境应避免使用子查询2避免函数索引示例:SELECT*FROMtWHEREYEAR(d)>=2016;由于MySQL不像Oracle那样支持函数索引,即使d字段有索引也会直接扫描全表。应该改为——>SELECT*FROMtWHEREd>='2016-01-01';3将OR低效查询替换为INSELECT*FROMtWHERELOC_ID=10ORLOC_ID=20ORLOC_ID=30;————>高效查询SELECT*FROMtWHERELOC_ININ(10,20,30);4LIKE双百分号不能使用索引SELECT*FROMtWHEREnameLIKE'%de%';——–>SELECT*FROMtWHEREnameLIKE'de%';目前只有MySQL5.7支持全文索引(支持中文)5读取合适的记录LIMITM,NSELECT*FROMtWHERE1;—–>SELECT*FROMtWHERE1LIMIT10;6避免数据类型不一致SELECT*FROMtWHEREid='19';——–>SELECT*FROMtWHEREid=19;7组统计可以禁止排序SELECTgoods_id,count(*)FROMtGROUPBYgoods_id;GROUPBYcol1,col2...的字段是排序的。如果查询包含GROUPBY并且你想避免使用排序后的结果,你可以指定ORDERBYNULL来禁用排序。—–>SELECTgoods_id,count(*)FROMtGROUPBYgoods_idORDERBYNULL;8避免随机获取记录SELECT*FROMt1WHERE1=1ORDERBYRAND()LIMIT4;MySQL不支持函数索引,会导致全表扫描—–>SELECT*FROMt1WHEREid>=CEIL(RAND()*1000)LIMIT4;9禁止不必要的ORDERBY排序SELECTcount(1)FROMuseruLEFTJOINuser_infoiONu.id=i.user_idWHERE1=1ORDERBYu.create_timeDESC;—–>SELECTcount(1)FROMuseruLEFTJOINuser_infoiONu.id=i.user_id;10BatchINSERTINSERTINTOt(id,name)VALUES(1,'Bea');INSERTINTOt(id,name)VALUES(2,'Belle');INSERTINTOt(id,name)VALUES(3,'Bernice');—–>INSERTINTOt(id,name))VALUES(1,'Bea'),(2,'Belle'),(3,'Bernice');