绝无经由的PL/SQL用户指南与参考--PL/SQL使用程序功能调优
业界普遍的声音认为:“MySQL是一个可靠的数据库系统,MySQL学习教程无论是在嵌入式或大型群集系统的部署中,还是在基于Web的应用程序领域。第十二章PL/SQL使用程序功能调优1、PL/SQL功能成绩的启事
应基于PL/SQL的使用程序实施效力低下时,一般是由于欠好的SQL话语、编程步骤,对PL/SQL基本把握糟或是乱花共享内存储器促进的。
•PL/SQL中欠好的SQL话语
PL/SQL编程看上往绝对照较复杂,因为它们的庞大内容都遮蔽在SQL话语中,SQL话语常常分管大批的事情。这乃是为什么欠好的SQL话语是实施效力低下的主要原因了。如若一个程序中包含良多欠好的SQL话语,那末,不管是PL/SQL话语写的有何其美都是于事无补的。
如其SQL话语减低了我们的程序速率的话,将要按底以下表中的步骤剖析一会儿它们的实行企图和功能,厥后重新编辑SQL话语。好比,查询优化器的展现便可能会扫除失落成绩,如没有需要的全表扫描。
一.EXPLAINPLAN话语
二.施用TKPROF的SQLTrace效能
三.OracleTrace效能
•糟的编程习惯
一般,糟的编程习惯也会给程序带回负面影响。这类情形下,即便是故意得的程序员写出的代码也大概妨害功能发扬。
至于给定的一项义务,不管是所选的程序言语有多么合适,编辑品德较差的子程序(好比,一个很慢的分门别类或检索函数)也许毁失落全部功能。假定有一个急需被使用程序频仍挪用的查询函数,如其这个函数不是使用哈希或二分法,而是间接使用线性查寻,就会年夜年夜影响效力。糟的程序指的是那些带有从未有过使用过的变量的,传送没有需要的参数的,把初始化或盘算放到用不着的轮回中实施的程序之类。
•内置函数的重复
PL/SQL供应了很多多少高度优化过的函数,如REPLACE、TRANSLATE、SUBSTR、INSTR、RPAD和LTRIM等。不必手工编撰我们自个儿的版本,因为内置函数已是很高效力的了。即或内置函数的效能远远凌驾我们的亟需,也不必手工兑现它们效能的子集。
•低效的流程统制话语
在盘算逻辑表达式值的时分,PL/SQL使用短路的盘算形式。且不说,万一了局能够被断定上去,PL/SQL就会停止余下的表达式盘算。好比,下部的OR表达式,应sal比1500小的时分,操纵符右边的值乃是TRUE,之以是PL/SQL就不会再盘算操纵符右边表达式的值:
IF(sal<1500)OR(commISNULL)THEN
...
ENDIF;
现下,思索下部的AND表达式:
IFcredit_ok(cust_id)AND(loan<5000)THEN
...
ENDIF;
在下面的函数中,布尔函数credit_ok总是被挪用。可是,如其我们向底下这么退换两个表达式的地位:
IF(loan<5000)ANDcredit_ok(cust_id)THEN
...
ENDIF;
那末,函数只有在表达式loan<5000的值为TRUE的时分才会被挪用,这类情况也合用于EXIT-WHEN话语。
•隐式的数据门类变更
运转时,PL/SQL能把机关化分歧的数据部类停顿隐式的变更。比如说,把PLS_INTEGER变量赋给一个NUMBER变量,由于它们的内涵体现情势纷歧样,之以是就会引发隐式地数据部类变更。
制止隐式的门类变更能够改善功能。如次面的例证,15是一个有标记的4字节数量字,在加法演算事前,PL/SQL务必把它转换成Oracle的数量字门类。可是,浮点数15.0使用22字节的Oracle数量字体现,之以是就没有需要展开变更。
DECLARE
nNUMBER;
cCHAR(五);
BEGIN
n:=n+15;--converted
n:=n+15.0;--notconverted
...
END;
这边再有一个事例:
DECLARE
cCHAR(五);
BEGIN
c:=25;--converted
c:=25;--notconverted
...
END;
•不得当的数量字门类宣言
数据门类NUMBER和它的子部类都是22字节,数据库格局的数量字,它们易于移栽而且能适应于分歧的长度与严密度。当我们亟需宣言一个整型变量时,将近使用PLS_INTEGER,它是最高效力的数量字门类。这是因为PLS_INTEGER所需的内存储器要比INTEGER和NUMBER门类要少。一样,PLS_INTEGER使用呆板演算,之以是它的演算速率要比BINARY_INTEGER、INTEGER或NUMBER快很多。
别的,INTEGER、NATURAL、NATURALN、POSITIVE、POSITIVEN和SIGNTYPE都是受束缚的子门类。之以是,它们的变量急需在运转时稽察严密度,这就会影响到效力。
•用不着的NOTNULL束缚
PL/SQL中,施用NOTNULL束缚也会汲取功能亏耗。如次例所示:
PROCEDUREcalc_mIS
mNUMBERNOTNULL:=零;
aNUMBER;
bNUMBER;
BEGIN
...
m:=a+b;
...
END;
因为m是受NOTNULL束缚的,表达式a+b的值就会赋给临时变量,厥后PL/SQL会对这个临时变量作判空测试。如其变量不是空,它的值就可以赋给m,要否则就会呈现非常。可是,如若m不是有束缚限定的话,了局值就会间接赋给m。更高效力的写法如次:
PROCEDUREcalc_mIS
mNUMBER;--noconstraint
aNUMBER;
bNUMBER;
BEGIN
...
m:=a+b;
IFmISNULLTHEN--enforceconstraintprogrammatically
...
ENDIF;
END;
注重,NATURALN和POSTIVEN都是NOTNULL,之以是它们也一样会影响功能。
•VARCHAR二变量的长度宣言
至于VARCHAR2部类,我们在内存储器使用和效力上亟需作出一个权衡。关于VARCHAR二(长度>=2000)变量,PL/SQL静态分派内存储器来存放实际值,但至于VARCHAR二(长度<2000)变量,PL/SQL能之前分派充足的内存储器。之以是,如其我们把一样一个500字节的值放进一个VARCHAR二(2000)和一个VARCHAR二(1999)变量中,后者会多占用1499个字节的内存储器。
•乱花PL/SQL程序中的共享内存储器
第一回挪用封装子程序时,全部包会被加载到共享内存储器池。之以是,以后挪用包内干系子程序时,就不再亟需读取磁盘了,这么会抓紧我们的代码会实行速率。可是,如其包从内存储器中打扫今后,我们在重新引述它的时分,就必需重新加载它。
我们能够经由准确地设立共享内存储器池巨细来改善功能。必定要准保共享内存储器有充足空间来存放被频仍施用的包,但空间也不必过年夜,免得浪费内存储器。
•护持包(PinnedPackages)
别的一个改善功能的步骤即是把频仍使用的包护持到共享内存储器池中。应一个包被护持上去后,它就不会被Oracle一般所接纳的最少日前施用(LRU)算法打扫。甭管池有多满或是我们会见包有多么频仍,包一直会被坚持在池中的。我们能够使用体系包DBMS_SHARED_POOL把包护持上去。
•可继续重用包
为了互助我们管理内存储器的使用,PL/SQL供应了编译唆使SERIALLY_REUSABLE,它能让我们把某些包标志为可继续重用的(seriallyreusable)。如其一个包的形态只在服务器呼唤工夫内所急需,那末我们就能够对这个包使用这个标志了(好比,一个对服务器的OCI挪用或是服务器对服务器RPC)。
关于这么的包所分派的内存储器会放到体系年夜局区(SGA)中,而不是分派到独力的用户所施用的用户年夜局区(UGA)。那样,包的事情区就能够被反复使用。应服务器挪用结束的时分,内存储器就会被偿还共享池。次次包被重用时,它的大众变量就会被初始化作它们的默许值或NULL。
一个包所需的事情区最年夜个数乃是今朝使用这个包的用户数,这个数量字一样平常要小于登任命户数。SGA内存储器的增加量要年夜于UGA内存储器的减缩量。并且,如其Oracle要接纳SGA内存储器的话,它就会把没施用的事情区展开过期处理。
关于没包体的包来讲,我们能够使用在包申明中施用下边语法编辑编译唆使:
PRAGMASERIALLY_REUSABLE;
至于有包体的包来讲,我们务必在申明和包体中编辑编译唆使。我们不克不及只在包体中编辑编译唆使。底下的例证练习训练了何故在一个继续重用包中使用一个大众变量:
CREATEPACKAGEpkg一IS
PRAGMASERIALLY_REUSABLE;
numNUMBER:=零;
PROCEDUREinit_pkg_state(nNUMBER);
PROCEDUREprint_pkg_state;
ENDpkg一;
/
CREATEPACKAGEBODYpkg一IS
PRAGMASERIALLY_REUSABLE;
PROCEDUREinit_pkg_state(nNUMBER)IS
BEGIN
pkg一.num:=n;
END;
PROCEDUREprint_pkg_stateIS
BEGIN
dbms_output.put_line(Num:||pkg一.num);
END;
ENDpkg一;
/
BEGIN
/*Initializepackagestate.*/
pkg一.init_pkg_state(四);
/*Onsameservercall,printpackagestate.*/
pkg一.print_pkg_state;--prints四
END;
/
--subsequentservercall
BEGIN
--thepackagepublicvariableisinitialized
--tothedefaultvalueautomatically
pkg一.print_pkg_state;--prints零
END;
2、断定PL/SQL的功能成绩
当我们开辟愈来愈年夜的PL/SQL使用程序时,便在所不免要碰到功能成绩。之以是,PL/SQL为我们供应了ProfilerAPI来辨析运转时举措并互助我们识别功能瓶颈。PL/SQL也供应了一个TraceAPI用于追踪服务器真个程序实施。我们能够施用Trace来追踪子程序或非常的实施。
1、ProfilerAPI:DBMS_PROFILER包
ProfilerAPI由PL/SQL包DBMS_PROFILER兑现,它供应了征集并保存运转时的统计信息。这些信息会被封存在数据表中,供我们查询。好比,我们能够知道PL/SQL每行和每一个儿程序实施所花销的工夫是非。
要使用Profiler,先开启一本性能测评对话,充实地运作我们的使用程序以便到达充足的代码掩盖率,厥后把搜集到的信息保存在数据库中,停止功能估测对话。详细办法如次:
一.挪用DBMS_PROFILER包中的start_profiler历程,把一个解释与功能估测对话关联。
二.运作要被估测的使用程序。
三.反复挪用过程flush_data把搜集到的数据保存上去并释放内存储器。
四.挪用stop_profiler历程停止对话。
Profiler会追踪程序的实施,盘算每行和每一个儿程序所开支的工夫。我们能够用搜集到的数据互助改善功能。好比,我们能够会合处理那些运作慢的子程序。
•剖析搜集到的功能数据
下一步要判别出为啥实行某些代码段或会见某些数据布局要用度大批的工夫。依附查询出来的功能数据来找出成绩点。把成绩会合到那些损耗工夫长的子程序和包,只管的优化SQL话语、轮回和递回函数等。
•施用追踪数据改善程序功能
使用我们的剖析了局重新编撰那些实施效力低下的算法。好比,在急忙暴胀的数据中,我们大概要亟需使用二分法来顶替线性征采。
2、TraceAPI:包DBMS_TRACE
在年夜而庞大的使用程序中,很难追踪子程序的挪用。如其施用追踪API,我们就可以看到子程序的实施按次。追踪API是由包DBMS_TRACE兑现的,并供应了追踪子程序或非常的服务。
要使用追踪,先要开启一个追踪对话,运作程序,厥后停止追踪对话。应程序实行时,追踪数据就会把汇集并保存到数据库中。在一个对话中,我们能够接纳如次办法来实施追踪操纵:
一.可选办法,决定要追踪的某个特定的子程序。
二.挪用DBMS_TRACE包中的set_plsql_trace开启追踪。
三.运作要追踪的使用程序。
四.挪用历程clear_plsql_trace来停止追踪。
•统制追踪
追踪特年夜型使用程序大概会打造出大批的难以管理的数据。在开启追踪事前,我们能够弃取是否是限定要汇集的数据量。
别的,还能够决定追踪级别。好比,如若我们能够决定追踪一切的子程序和非常或是只追踪选出的子程序和非常。
3、PL/SQL功能优化特性
我们能够使用下边的PL/SQL特性和步骤来优化使用程序:
一.使用外地静态SQL优化PL/SQL
二.使用批量绑定优化PL/SQL
三.施用NOCOPY编译器展现优化PL/SQL
四.使用RETURNING子句优化PL/SQL
五.施用内部程序优化PL/SQL
六.使用工具部类和聚合优化PL/SQL
这些大略易用的特性能够明显的增高使用程序的实施速率。
1、使用外地静态SQL优化PL/SQL
有点程序务需要实行一些只有在运转时才力断定上去的SQL话语,这些话语被称为静态SQL话语。新近,要实行静态SQL话语就必需使用包DBMS_SQL。现下,我们能够在PL/SQL中间接使用被称为外乡静态SQL的接口来实施各类静态SQL话语。
外乡静态SQL更复杂利用,并且实行速率也要比DBMS_SQL包快。不才面的例证中,我们宣言一个游标变量,厥后把它与一个能回到数据表emp纪要的静态的SELECT话语关联下床:
DECLARE
TYPEempcurtypISREFCURSOR;
emp_cvempcurtyp;
my_enameVARCHAR二(15);
my_salNUMBER:=1000;
BEGIN
OPENemp_cvFORSELECTename,salFROMempWHEREsal>:s
USINGmy_sal;
...
END;
2、使用批量绑定优化PL/SQL
应SQL在聚合的轮回内实行时,PL/SQL和SQL动员机间的频仍切换就会影响到实施速率。好比,下头的UPDATE话语就在FOR话语中不住发送到SQL动员机:
DECLARE
TYPEnumlistISVARRAY(20)OFNUMBER;
deptsnumlist:=numlist(十,30,70,...);--departmentnumbers
BEGIN
...
FORiINdepts.FIRST..depts.LASTLOOP
...
UPDATEempSETsal=sal*1.10WHEREdeptno=depts(i);
ENDLOOP;
END;
在这么的情形下,如若SQL话语影响到四行或更多行数据时,使用批量绑定就会明显地增高功能。好比,底下的UPDATE话语能够一次就把全部嵌套表的数据发送到SQL动员机中:
FORALLiINdepts.FIRST..depts.LAST
UPDATEempSETsal=sal*1.10WHEREdeptno=depts(i);
要想尽最年夜也许地拔高功能,我们就必要像下部这么编写程序:
一.如其一条INSERT、UPDATE或DELETE话语在轮回内实施,并且摘引到会集中的元素,那末,就把它放到FORALL话语中往。
二.如其SELECTINTO、FETCHEINTO或RETURNINGINTO子句引述了一个聚合,那就共同BULKCOLLECT子句一行使用。
三.如若也许的话,尽量在使用程序和服务器其间使用主数组传送聚合。
四.如若DML操纵失利时且不是很严重的成绩,就能够在FORALL话语中使用SAVEEXCEPTIONS,厥后在以后的轮回中施用%BULK_EXCEPTIONS属性报告或驱除不对。
不必疏忽这些小成绩,因为它们能够互助我们剖析流程统制和程序的依附性。
3、施用NOCOPY编译器提示优化PL/SQL
默许情形下,OUT和INOUT情势的参数都是按值传接的。来说,一个INOUT实参会把它的复本拷贝到对应的形参中。厥后,如其程序实行正确的话,这个值又会重新赋给OUT和INOUT的实参。
但实参是聚合、纪要和工具典范这么的年夜的数据布局时,天生一个复本会极年夜地减低实施效力并泯灭大批内存储器的。为明白决这个成绩,我们能够使用编译器展现NOCOPY,它能让编译器把OUT和INOUT参数按摘引传送。下例中,我们就可以让编译器按摘引传送INOUT参数my_unit:
DECLARE
TYPEplatoonISVARRAY(200)OFsoldier;
PROCEDUREreorganize(my_unitINOUTNOCOPYplatoon)IS...
BEGIN
...
END;
END;
4、使用RETURNING子句优化PL/SQL
一般,使用程序亟需得到SQL操纵所影响到的行信息。INSERT、UPDATE和DELETE话语都能够包含一个RETURNING子句,这么就可以返来处理过的字段信息。也就不要在INSERT、UPDATE今后或DELETE头里使用SELECT来查询影响到的数据。这么也可以减小收集流量,缩水CPU工夫,急需更小量的游标和服务器内存储器需求。
不才面的事例中,我们就在更新雇职人为的与此同时,把现阶段雇员的称号和新的薪资赋给PL/SQL变量:
PROCEDUREupdate_salary(emp_idNUMBER)IS
"name"VARCHAR二(15);
new_salNUMBER;
BEGIN
UPDATEemp
SETsal=sal*1.1
WHEREempno=emp_idRETURNINGename,salINTO"name",new_sal;
--Nowdocomputationsinvolvingnameandnew_sal
END;
5、使用内部程序优化PL/SQL
PL/SQL供应了挪用其他言语编撰的程序的接口。PL/SQL能够从程序中挪用其他言语所编辑的基准库。这就可以够增高可重用性、高效力性和程序的模块化。
PL/SQL是特地用以停顿SQL事件处置的。有点义务在像C这么的低阶言语中处理起来会越发无效。
为了增高实施速率,我们能够用C言语重新编辑受盘算量限定的程序。除此之外,我们还能够把这么的程序从客户端移栽到服务器端,这么能够减小收集流量,更无效天时用资本。
好比,我们用C言语写一个使用图形工具门类的步骤,把它打包到静态链接库(DLL)中,并在PL/SQL中挂号,厥后我们就可以从使用程序中挪用它。运转时,库会主动态地加载,为了保险起见,它会在一个单独的地点空间运作。
6、施用工具部类和聚合优化PL/SQL
会集门类和工具门类在对实在全球中的实业展开数据建模时能互助我们进步效力。庞大的实业和干系会被间接投射到工具门类中。并且,一个构建很好的工具模子可以化除多表连接,减小往返来去之类,故此改善使用程序功能。
客户端程序,包含PL/SQL程序,能够宣言工具和聚合,把它们作为参数传接,寄存在数据库中,检索之类。一样,工具部类还能够把数据操纵停顿打包,把数据保护代码从SQL剧本中移出,把PL/SQL块放进步骤中。
工具和会集在贮存和检索方面越发高效力,因为它们是作为一个通体展开操纵的。一样,工具门类还能和数据库调剂在一同,使用Oracle本身所供应的易扩缩性和功能改善等长处。
7、编译外地实行的PL/SQL代码
我们能够把PL/SQL过程编译利润地代码放到共享库中,这么就可以拔高它的实施速率。过程还能够被转换成C代码,厥后用普一般通的C编译器编译,连接到Oracle过程当中。我们能够在Oracle供应的包和我们自各儿编辑的过程当中使用这项手艺。这么编译出来的过程能够在各类服务器情况中事情。因为这项手艺对从PL/SQL中挪用的SQL话语进步效力其实不分明,之以是它一样平常使用在盘算度高而实行SQL工夫不久不多的PL/SQL过程上。
要增高一个或多个历程的实施速率,我们能够这么施用这项手艺:
一.更新makefile并为我们的体系键进得当的路子和其他值。makefile路子是$ORACLE_HOME/plsql/spnc_makefile.mk。
二.经由使用ALTERSYSTEM或ALTERsession命令,或经由更新初始化材料,设立参数PLSQL_COMPILER_FLAGS来包含值NATIVE。默许设置包含的值是INTERPRETED,我们务必把它从参数值中剔除。
三.使用下部几个步骤编译一个或多个历程:
一.施用ALTERPROCEDURE或ALTERPACKAGE命令重新编译过程或全部包。
二.剔除过程并重新创立。
三.施用CREATEORREPLACE重新编译过程。
四.运作SQL*Plus剧本创建一组Oracle体系包。
五.用带有PLSQL_COMPILER_FLAGS=NATIVE的初始化材料制造数据库。在制造数据库时,用UTLIRP剧本运作并编译Oracle体系包。
四.要断定我们所做的办法是否是无效,能够查询数据辞书来检察历程是否是是被编译为外地实施,查询用的视图是USER_STORED_SETTINGS、DBA_STORED_SETTINGS和ALL_STORED_SETTINGS。好比,要检察MY_PROC的形态,我们能够输出:
SELECTparam_value
FROMuser_stored_settings
WHEREparam_name=PLSQL_COMPILER_FLAGS
ANDobject_name=MY_PROC;
PARAM_VALUE字段值为NATIVE时,代表历程是被编译外地实行的,否则即是INTERPRETED。
历程编译后就会被转到共享库,它们会被主动地连接到Oracle过程中。我们不必要从头启动数据库,或是把共享库放到除此之外一个中央。我们能够在贮存过程其间反复挪用它们,不论它们是以默许形式(interpreted)编译,外地实行形式编译还是接纳两种混淆的编译形式。
因为PLSQL_COMPILER_FLAGS设立是封存在每一个历程的库单位里的,应被编译利润地实施的历程掉灵时,在重新编译的时分还会接纳之前的编译情势。
我们能够透过ALTERSYSTEM或ALTERSESSION命令,或透过设立初始化材料中的参数来统制PL/SQL外地编译的举措:
一.PLSQL_COMPILER_FLAGS
二.PLSQL_NATIVE_LIBRARY_DIR(因为保险原因,不克不及施用ALTERSESSION展开设立)
三.PLSQL_NATIVE_LIBRARY_SUBDIR_COUNT
四.PLSQL_NATIVE_MAKE_UTILITY
五.PLSQL_NATIVE_MAKE_FILE_NAME
编译外地实施的PL/SQL过程举例来讲:
CONNECTscott/tiger;
SETserveroutputON;
ALTERSESSIONSETplsql_native_library_dir=/home/orauser/lib;
ALTERSESSIONSETplsql_native_make_utility=gmake;
ALTERSESSIONSETplsql_native_make_file_name=/home/orauser/spnc_makefile.mk;
ALTERSESSIONSETplsql_compiler_flags=NATIVE;
CREATEORREPLACEPROCEDUREhello_native_compilationAS
BEGIN
dbms_output.put_line(helloworld);
SELECTSYSDATEFROMdual;
END;
历程编译时,我们能够看到各类被实行的编译和连接命令。厥后过程就当即能够被挪用,间接在Oracle过程当中被作为共享库间接运作
本文
我的非常网
javaException
DotnetException
OracleException
707-java.lang.ClassNotFoundException
708-java.lang.NoClassDefFoundError
709-org.springframework.beans.factory.BeanCreationException
710-org.springframework.orm.hibernate3.HibernateSystemException:idsforthisclassmustbemanuallyassignedbeforecallingsave
711-java.lang.ExceptionInInitializerError
712-og4j:WARNNoappenderscouldbefoundforloggerorg.apache.commons.digester.Digester.sax
713-org.hibernate.exception.GenericJDBCException:Cannotopenconnection
714-org.hibernate.exception.SQLGrammarException:Cannotopenconnection
715-ervletclassorg.springframework.web.servlet.DispatcherServletforservletdispatchcouldnotbeloadedbecausetherequestedclasswasnotfoundintheclasspath
716-javax.naming.AuthenticationException
717-java.sql.SQLException:ORA-00907
718-JAVA非常处置机制
719-服务器端抛出非常怎样处置
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/liangdiwei/archive/2009/12/22/5051849.aspx尽管DBaaS模式有缺点,但它还是适合某些客户群体,这为解决方案提供商提供了新的商机。鉴于云服务的增长,解决方案提供商除了拥抱这些技术还有什么选择呢?如果他们不这样做,他们就会冒着被竞争对手击败的风险。但他们不能只想到如何把DBaaS的利润率与企业内部系统相比较。 对于微软系列的东西除了一遍遍尝试还真没有太好的办法 作了些试验,发现使用CLR的存储过程或函数在达到一定的阀值的时候,系统性能会呈指数级下滑!这是非常危险的!只使用几个可能没有问题,当一旦大规模使用会造成严重的系统性能问题! 以前的DTS轻盈简单。但是现在的SSIS虽然功能强大了很多,但是总是让人感觉太麻烦。看看论坛中询问SSIS的贴子就知道。做的功能太强大了,往往会有很多用户不会用了 如果,某一版本可以提供强大的并发响应,但是没有Oracle的相应版本稳定,或者价格较贵,那么,它就是不适合的。 你可以简单地认为适合的就是好,不适合就是不好。 原理很简单,对要求长时间计算某一时间点的报表生成和防用户操作错误很有帮助。但是比起Oracle10g的闪回技术还是细粒度不够。可惜! 我们学到了什么?思考问题的时候从表的角度来思考问 语句级快照和事务级快照终于为SQLServer的并发性能带来了突破。个人感觉语句级快照大家应该应用。事务级快照,如果是高并发系统还要慎用。如果一个用户总是被提示修改不成功要求重试时,会杀人的!
页:
[1]