当前位置: 首页 > 科技观察

MySQL高并发优化,性能调优应该是这样的~

时间:2023-03-18 12:51:50 科技观察

1.数据库结构设计表设计具体问题:1.数据行长度不超过8020字节。如果超过这个长度,在物理页中存储的这块数据会占用两行,造成存储碎片,降低查询效率。2、可以使用数值类型的字段,尽量选择数值类型,不要选择字符串类型(电话号码),这样会降低查询和连接的性能,也会增加存储开销。这是因为引擎正在处理查询并返回连接以逐个比较字符串中的每个字符,而对于数字类型,只需比较一次就足够了。3、不可变字符类型char和可变字符类型varchar都是8000字节,char查询速度快,但是比较消耗存储空间,varchar查询速度相对较慢但是节省存储空间。设计字段时可以灵活选择。例如用户名、密码等长度变化较小的字段可以选择CHAR,评论等长度变化较大的字段可以选择VARCHAR。4、字段的长度在最大程度满足可能需要的前提下尽量设置的短,以提高查询效率,减少索引时的资源消耗。2、查询的优化,保证在实现功能的基础上,尽量减少访问数据库的次数(可以使用缓存保存查询结果,减少查询次数);通过搜索参数,最小化访问表的行数,最小化结果集,从而减轻网络负担;可以分开的操作,尽量分开处理,提高每次的响应速度;在数据窗口中使用SQL时,尽量将使用的索引放在选择的第一列;算法的结构应尽可能简单;查询时,不要过多使用通配符,如SELECT*FROMT1语句,根据需要选择多少列,如:SELECTCOL1,COL2FROMT1;尽量限制结果集中的行数,比如:SELECTTOP300COL1,COL2,COL3FROMT1,因为有些情况下用户不需要那么多数据。1.尽量避免在where子句中判断字段的空值,否则引擎会放弃使用索引,进行全表扫描,如:selectidfromtwherenumisnull可以在num上设置默认值0,保证num表中的列没有空值,然后这样查询:selectidfromtwherenum=02。应该尽量避免在where子句中使用!=或<>运算符,否则引擎会放弃使用索引而进行全表扫描。优化器将无法通过索引确定要***的行数,因此需要搜索表的所有行。3、尽量避免在where子句中使用ortoconnect条件,否则引擎会放弃使用索引,进行全表扫描,如:selectidfromtwherenum=10ornum=20可以这样查询:selectidfromtwherenum=10unionallselectidfromtwherenum=204.in和notin也要谨慎使用,因为IN会使系统无法使用索引,而只能直接查找表中的数据。例如:selectidfromtwherenumin(1,2,3)对于连续值,可以使用between代替in:selectidfromtwherenumbetween1and35。尽量避免使用非首字母在索引字符数据中进行搜索。这也使得引擎无法利用索引。看下面的例子:SELECT*FROMT1WHERENAMELIKE'%L%'SELECT*FROMT1WHERESUBSTING(NAME,2,1)='L'SELECT*FROMT1WHERENAMELIKE'L%'即使NAME字段有索引,前两个查询还是不能用索引完成加速操作,引擎必须对整个表中的所有数据进行一一操作才能完成任务。而第三个查询可以使用索引来加速运算。6、如果有必要,强制查询优化器使用索引,比如在where子句中使用参数,这样也会造成全表扫描。因为SQL只在运行时解析局部变量,所以优化器不能将访问计划的选择推迟到运行时;它必须在编译时选择。但是,如果访问计划是在编译时建立的,变量的值仍然是未知的,因此不能用作索引选择的输入。例如,下面的语句将执行全表扫描:selectidfromtwherenum=@num可以更改为强制查询使用索引:selectidfromtwith(index(indexname))wherenum=@num7。您应该尽量避免对where子句中的字段执行表达式操作。会导致引擎放弃使用索引而进行全表扫描。例如:SELECT*FROMT1WHEREF1/2=100应更改为:SELECT*FROMT1WHEREF1=100*2SELECT*FROMRECORDWHERESUBSTRING(CARD_NO,1,4)='5378'应更改为:SELECT*FROMRECORDWHERECARD_NOLIKE'5378%'SELECTmember_number,first_name,last_nameHROM(yy,datofbirth,GETDATE())>21应该改为:SELECTmember_number,first_name,last_nameFROMmembersWHEREdateofbirth='2005-11-30'andcreatedate<'2005-12-1'9。不要在where子句的“=”左边进行函数、算术运算或其他表达式操作,否则索引可能无法正确使用。10、使用索引字段作为条件时,如果索引是复合索引,那么必须使用索引中的第一个字段作为条件,保证系统使用索引,否则索引不会被使用,以及字段的顺序应尽可能匹配索引的顺序。11.在很多情况下,使用exists是一个不错的选择:selectnumfromawherenumin(selectnumfromb)被下面的语句代替:selectnumfromawhereexists(select1frombwherenum=a.num)SELECTSUM(T1.C1)FROMT1WHERE((SELECTCOUNT(*)FROMT2WHERET2.C2=T1.C2>0)SELECTSUM(T1.C1)FROMT1WHEREEXISTS(SELECT*FROMT2WHEREEXISTS(SELECT*FROMT2WHERET2.C2=T1.C2)两者产生的结果相同,但后者显然比前者效率更高。因为后者不产生大量的lockedtablescansorindexscan.如果要检查表中是否存在一条记录,不要使用count(*),效率低,浪费服务器资源,可以使用EXISTS代替。例如:IF(SELECTCOUNT(*)FROMtable_nameWHEREcolumn_name='xxx')可以写成:IFEXISTS(SELECT*FROMtable_nameWHEREcolumn_name='xxx')经常需要写T_SQL语句比较父结果集和子结果集是否父结果集中有记录但子结果集中没有resultset,suchas:SELECTa.hdr_keyFROMhdr_tbla----tbla表示tbl用别名a代替WHERENOTEXISTS(SELECT*FROMdtl_tblbWHEREa.hdr_key=b.hdr_key)SELECTa.hdr_keyFROMhdr_tblaLEFTJOINdtl_tblbONa.hdr_key=b.hdr_keyWHEREb.hdr_keyISNULLSELECThdr_keyFROMhdr_tblWHEREhdr_keyNOTIN(SELECThdr_keyFROMdtl_tbl)12.尽量使用表变来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。13、避免频繁创建和删除临时表,减少系统表资源的消耗。14.临时表不是不能用,适当使用它们可以让一些例程变得更有效率,例如,当需要重复引用一个大表或经常使用的表中的某个数据集时。但是,对于一次性事件,最好使用导出表。15、创建临时表时,如果一次插入的数据量较大,可以使用selectinto代替createtable,避免创建大量日志,提高速度;如果数据量不大,为了缓解系统表的资源,应该先建表,再插入。16、如果使用临时表,必须在存储过程结束时显式删除所有临时表,先truncatetable,再droptable,避免系统表长期锁定。17.在所有存储过程和触发器的开头设置SETNOCOUNTON,在结束时设置SETNOCOUNTOFF。存储过程和触发器的每条语句执行完后,不需要向客户端发送DONE_IN_PROC消息。18、尽量避免大事务操作,提高系统并发度。19、尽量避免向客户端返回大量数据。如果数据量太大,就要考虑对应的需求是否合理。20.避免使用不兼容的数据类型。例如,float和int、char和varchar、binary和varbinary是不兼容的(有条件时)。数据类型不兼容可能会阻止优化器执行某些原本可以执行的优化操作。例如:SELECTnameFROMemployeeWHEREsalary>60000这条语句中,如果salary字段是money类型,优化器很难优化,因为60000是一个整数。我们应该在编程期间将整数转换为货币,而不是等待运行时转换。21、充分利用连接条件(条件越多速度越快),在某些情况下,两个表之间的连接条件可能不止一个。这时候把连接条件完全写在WHERE子句中,可能会大大提高查询速度。示例:SELECTSUM(A.AMOUNT)FROMACCOUNTA,CARDBWHEREA.CARD_NO=B.CARD_NOSELECTSUM(A.AMOUNT)FROMACCOUNTA,CARDBWHEREA.CARD_NO=B.CARD_NOANDA.ACCOUNT_NO=B.ACCOUNT_NO第二句会比第一句执行得快很多。