获取sql语句getLastSql$res=Db::table('staff')->field('id,salary')->where('id','>',1)->group('salary')->having('salary>5000')->select();$sql=Db::table('staff')->getLastSql();halt($sql);select(false)$res=Db::table('staff')->field('id,salary')->where('id','>',1)->group('salary')->having('salary>5000')->select(false);halt($res);fetchSql$res=Db::table('staff')->field('id,salary')->where('id','>',1)->group('salary')->having('salary>5000')->fetchSql()->select();halt($res);buildSql$res=Db::table('staff')->field('id,salary')->where('id','>',1)->group('salary')->having('salary>5000')->buildSql();halt($res);监听sqlDb::listen(function($sql,$time,$explain){//记录SQLecho$sql.'['.$time.'s]';//查看性能分析结果dump($explain);});
publicfunctiongetLikeList($catalog_id,$size=20){$res=$this->field('id,title')->where('catalog_id',$catalog_id)->orderRaw("RAND()")//随机取->paginate($size);return$res;}可以直接使用orderRaw('RABD()');不限定条数
tp5给某个字段增加减少值减少:Db::name('song')->where('id',$song_id)->setDec('song_number',5);//给song_number减少5增加:Db::name('song')->where('id',$song_id)->setInc('song_number',5);//给song_number增加10tp6给某个字段增加减少值减少:$w['goodscar_id']=1;$A=Db::name('goodscar')->where($w)->dec('goods_number',2)->update();增加$w['goodscar_id']=1;$A=Db::name('goodscar')->where($w)->inc('goods_number',2)->update();
在TP5公共common.php文件里写<?php//计算某个类别所属的类别层数functiongetcatelayer($cateid,$flag=1){$cates=M('Gcategory');$ini['cate_id']=$cateid;$arr=$cates->where($ini)->find();if($arr['parent_id']!=0){$flag=getcatelayer($arr['parent_id'],$flag+1);}return$flag;}//类别递归(单层)functiongetone($arr,$parent=0,$lev=0){//迭代$task=array($parent);//任务数组$tree=array();//结果数组while(!empty($task)){$flg=false;foreach($arras$k=>$v){if($v['parent_id']==$parent){$tree[]=array('id'=>$v['cate_id'],'name'=>$v['cate_name'],'lev'=>$lev);array_push($task,$v['cate_id']);$parent=$v['cate_id'];$lev=$lev+1;unset($arr[$k]);$flg=true;}}if($flg==false){array_pop($task);$parent=end($task);$lev=$lev-1;}}return$tree;}//类别递归(多层)functiongetCates($arr,$pid=0){for($i=0;$i<count($arr);$i++){if($arr[$i]['parent_id']==$pid){$newArr[]=array("id"=>$arr[$i]['cate_id'],"name"=>$arr[$i]['cate_name'],'son'=>getCates($arr,$arr[$i]['cate_id']),);}}return$newArr;}//查询类别所属级functiongetlayer($cateid,$flg=1){$gcg=M("Gcategory");$ini["cate_id"]=$cateid;$cateArr=$gcg->where($ini)->find();if($cateArr['parent_id']!=0){$flg=getlayer($cateArr['parent_id'],$flg+1);}return$flg;}一般只用其中的递归单层就可以了控制器中<?php//分类管理namespaceapp\admin\Controller;usethink\Db;usethink\Controller;classCategoryControllerextendsController{/***分类列表页(递归)*/publicfunctioncategory_list(){header('content-type:text/html;charset=utf-8');$categoryArr=db::name('表名')->select();//调用递归函数$getall=getone($categoryArr);$this->assign("getall",$getall);return$this->fetch()}视图view中<divclass="conShow"><tableborder="1"cellspacing="0"cellpadding="0"><tr><tdwidth="100px"class="tdColortdC">id</td><tdwidth="150px"class="tdColor">汽车类型</td><tdwidth="180px"class="tdColor">操作</td></tr>{foreachname="getall"id="v"}<tr><td>{$v.id}</td><td>{php}echostr_repeat(' ',$v['lev']*2){/php}|-{$v['name']}</td><td><ahref="connoisseuradd.html"><imgclass="operation"src="/img/admin/update.png"></a><imgclass="operationdelban"src="/img/admin/delete.png"></td></tr>{/foreach}</table><divclass="paging">此处是分页</div></div>
前期准备新建控制器/application/index/controller/Index.php<?phpnamespaceapp\index\controller;//导入路由usethink\Route;classIndex{//创建demo方法,输出欢迎信息publicfunctiondemo($name='you',$course='php'){return'欢迎'.$name.'来到csdn学习'.$course.'开发技术~~';}}新建路由配置文件/application/route.php注意:route.php文件,仅当系统路由功能开启时,才有效<?phpusethink\Route;//创建规则[demo/],后面有二个可选参数,替换掉原来较长的URL地址Route::rule('demo/[:name]/[:course]','index/Index/demo');普通模式普通模式,又叫常规模式或传统模式,就是不开启路由功能时访问模式。普通模式:按PATH_INFO方式访问demo方法,按顺序传入参数name和course修改应用配置文件/application/config.php//关闭路由'url_route_on'=>false,//URL参数传递方式:按顺序解析'url_param_type'=>1,访问方式其实质就是:访问指定的方法,并传入正确的参数访问地址:http://www.zhansanjie.com/index/index/demo/tom/tp5混合模式(官方推荐方式)混合模式,又叫兼容模式。是指:虽然开启了路由功能,但并不强制使用。对同一方法(如demo),即可以用普通模式访问,也可以用路由模式访问修改路由配置//开启路由'url_route_on'=>true,//不强制使用路由'url_route_must'=>false,//URL参数传递方式:按顺序解析'url_param_type'=>1,访问方式http://www.zhansanjie.com/index/baby/python
在测试的时候发现此方法会删除重复的数据进行添加新的数据,也就是id会发生变化,如数据库有id为1,2的五条数据唯一索引name分别为xiaoming、xiaohua,如果我们在批量插入数据的时候,唯一索引对比相同,则会删除掉之前的旧数据添加新数据。也就是如果我们现在要插入一条name为xiaohua的数据,那么id为2的数据将会被删除创建id为3的name为xiaohua的数据。//平常我们会这样来写$user是model类我们也可以用Db::name('table_name')$user->insert($data);//这样写将会自动处理$user->insert($data,true)$user->insertAll($data,true);//对于批量插入也适用,当有重复时,则进行更新
首先说明如果是linux或者是Mac,需要给予权限才能操作以下方法建议,在网站稳定后再生成上传。1生成路由缓存phpthinkoptimize:route如果你的应用定义了大量的路由规则,那么建议在实际部署后生成路由缓存文件,可以免去路由注册的开销,从而改善路由的检测效率这里如果没有权限会报异常我这里是Mac需要加sudosudophpthinkoptimize:route成功以后会在runtime目录下生成一个route.php文件2生成类库映射文件sudophpthinkoptimize:autoload类库映射文件可以提高自动加载的性能成功以后会在runtime目录下生成classmap.php文件3生成数据表字段缓存如果你希望提高查询的性能,可以通过生成字段缓存来减少查询sudophpthinkoptimize:schema执行完毕,会在RUNTIME_PATH目录下面创建schema目录,然后在该目录下面按照database.table.php的文件命名生成数据表字段缓存文件。4开启请求缓存 如果你的数据实时性不是很大可以开启, //是否开启请求缓存true自动缓存支持设置请求缓存规则<br>'request_cache'=>true,这里默认是false,开始后性能有很大的提高5可以为应用或者模块生成配置缓存文件sudophpthinkoptimize:config默认生成应用的配置缓存文件,调用后会在runtime目录下面生成init.php文件,生成配置缓存文件后,应用目录下面的config.phpcommon.php以及tags.php不会被加载,被runtime/init.php取代。这里要注意在本地生成配置缓存时需要把数据库等重要的配置替换成服务器上的配置以后,在生成。
<?phpnamespaceapp\index\controller;usethink\Controller;usethink\Db;classCharuextendsController{publicfunctionindex(){date_default_timezone_set("PRC");//此处修改php.ini的内存,不然无法执行插入,因为数据量太大ini_set('memory_limit','1024M');echodate('Y-m-dH:i:s').'<br>';$start=time();$data=array();for($i=0;$i<1000000;$i++){array_push($data,['name'=>'测试','age'=>10]);}print_r(db('charu')->insertAll($data));$end=time();echo'<br>'.date('Y-m-dH:i:s').'<br>';$a=$end-$start;print_r('耗时分秒'.date('i:s',$a));}}遇到下面的错误就表示你php.ini文件设置的内存值不够放Fatalerror:Allowedmemorysizeof157286400bytesexhausted(triedtoallocate32bytes)in 第一种解决方案是:在PHP文件中加入ini_set(”memory_limit”,”80M”);当然80M可以根据自己的情况改动,也不要太大哦,否则系统其它资源会遇到问题哦,其中-1为不设限。我用的是第一种方法第二种解决方案是:修改php.ini配置文件,在php.ini中找到memory_limit=16M,将前面的分号去掉,改动值的大小,将memory_limit由8M改成16M(或更大),重启apache服务即可。
第一种方式$bank=array(12){["bank_id"]=>string(1)"9"["bank_type"]=>string(1)"1"["bank_operator_id"]=>string(1)"2"["bank_operator_name"]=>string(9)"唐新朋"["bank_operator_type"]=>string(1)"3"["bank_accept_id"]=>string(1)"1"["bank_accept_name"]=>string(9)"汪昌宁"["bank_accept_type"]=>string(1)"1"["bank_sum"]=>string(5)"50000"["bank_mark"]=>string(3)"500"["bank_confirm"]=>string(1)"0"["bank_create_time"]=>string(10)"1467113725"}$m->where($bank)->setField('bank_confirm','1');第二种方式$bank=array(12){["bank_id"]=>string(1)"9"["bank_type"]=>string(1)"1"["bank_operator_id"]=>string(1)"2"["bank_operator_name"]=>string(9)"唐新朋"["bank_operator_type"]=>string(1)"3"["bank_accept_id"]=>string(1)"1"["bank_accept_name"]=>string(9)"汪昌宁"["bank_accept_type"]=>string(1)"1"["bank_sum"]=>string(5)"50000"["bank_mark"]=>string(3)"500"["bank_confirm"]=>string(1)"0"["bank_create_time"]=>string(10)"1467113725"}$condition['bank_id']=$bank["bank_id"];$m->where($condition)->setField('bank_confirm','1');这两种方法都可以实现我想要的操作但是第一种方法是否会对服务器或数据库造成压力第二种是可以多写这一行代码$condition['bank_id']=$bank["bank_id"];纠结中(⊙﹏⊙)回复内容:第一种方式$bank=array(12){["bank_id"]=>string(1)"9"["bank_type"]=>string(1)"1"["bank_operator_id"]=>string(1)"2"["bank_operator_name"]=>string(9)"唐新朋"["bank_operator_type"]=>string(1)"3"["bank_accept_id"]=>string(1)"1"["bank_accept_name"]=>string(9)"汪昌宁"["bank_accept_type"]=>string(1)"1"["bank_sum"]=>string(5)"50000"["bank_mark"]=>string(3)"500"["bank_confirm"]=>string(1)"0"["bank_create_time"]=>string(10)"1467113725"}$m->where($bank)->setField('bank_confirm','1');第二种方式$bank=array(12){["bank_id"]=>string(1)"9"["bank_type"]=>string(1)"1"["bank_operator_id"]=>string(1)"2"["bank_operator_name"]=>string(9)"唐新朋"["bank_operator_type"]=>string(1)"3"["bank_accept_id"]=>string(1)"1"["bank_accept_name"]=>string(9)"汪昌宁"["bank_accept_type"]=>string(1)"1"["bank_sum"]=>string(5)"50000"["bank_mark"]=>string(3)"500"["bank_confirm"]=>string(1)"0"["bank_create_time"]=>string(10)"1467113725"}$condition['bank_id']=$bank["bank_id"];$m->where($condition)->setField('bank_confirm','1');这两种方法都可以实现我想要的操作但是第一种方法是否会对服务器或数据库造成压力第二种是可以多写这一行代码$condition['bank_id']=$bank["bank_id"];纠结中(⊙﹏⊙)谢谢邀请。不知道你查询中的bank_id是不是唯一的,如果是唯一的第二种条件就足够了。虽然你第一种条件很多查询起来精确度也高,我看都是string类型,如果都是有索引还好,如果有的没有索引,一个条件足够的话我建议还是第二种。谢邀,bank_id不出意外必然是唯一键或者主键,所以肯定$m->where(array('bank_id'=>$bank['bank_id']))->setField('bank_confirm','1');楼上已经正确回答了你的问题,bank_id是唯一键则单条件查询理论上查询条件越多,过滤数据越多,查询越快。但有一个主键或者索引字段即可,加个limit=1,效率就足够好了。
使用ThinkPHP开发中MySQL性能优化的最佳21条经验讲解,目前,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显。关于数据库的性能,这并不只是DBA才需要担心的事,而这更是我们程序员需要去关注的事情。当我们去设计数据库表结构,对操作数据库时(尤其是查表时的SQL语句),我们都需要注意数据操作的性能。这里,我们不会讲过多的SQL语句的优化,而只是针对MySQL这一Web应用最多的数据库。希望下面的这些优化技巧对你有用。1.为查询缓存优化你的查询大多数的MySQL服务器都开启了查询缓存。这是提高性最有效的方法之一,而且这是被MySQL的数据库引擎处理的。当有很多相同的查询被执行了多次的时候,这些查询结果会被放到一个缓存中,这样,后续的相同的查询就不用操作表而直接访问缓存结果了。这里最主要的问题是,对于程序员来说,这个事情是很容易被忽略的。因为,我们某些查询语句会让MySQL不使用缓存。请看下面的示例://查询缓存不开启$r=mysql_query("SELECTusernameFROMuserWHEREsignup_date>=CURDATE()");//开启查询缓存$today=date("Y-m-d");$r=mysql_query("SELECTusernameFROMuserWHEREsignup_date>='$today'");上面两条SQL语句的差别就是CURDATE(),MySQL的查询缓存对这个函数不起作用。所以,像NOW()和RAND()或是其它的诸如此类的SQL函数都不会开启查询缓存,因为这些函数的返回是会不定的易变的。所以,你所需要的就是用一个变量来代替MySQL的函数,从而开启缓存。2.EXPLAIN你的SELECT查询使用EXPLAIN关键字可以让你知道MySQL是如何处理你的SQL语句的。这可以帮你分析你的查询语句或是表结构的性能瓶颈。EXPLAIN的查询结果还会告诉你你的索引主键被如何利用的,你的数据表是如何被搜索和排序的……等等,等等。挑一个你的SELECT语句(推荐挑选那个最复杂的,有多表联接的),把关键字EXPLAIN加到前面。你可以使用phpmyadmin来做这个事。然后,你会看到一张表格。下面的这个示例中,我们忘记加上了group_id索引,并且有表联接:当我们为group_id字段加上索引后:我们可以看到,前一个结果显示搜索了7883行,而后一个只是搜索了两个表的9和16行。查看rows列可以让我们找到潜在的性能问题。3.当只要一行数据时使用LIMIT1当你查询表的有些时候,你已经知道结果只会有一条结果,但因为你可能需要去fetch游标,或是你也许会去检查返回的记录数。在这种情况下,加上LIMIT1可以增加性能。这样一样,MySQL数据库引擎会在找到一条数据后停止搜索,而不是继续往后查少下一条符合记录的数据。下面的示例,只是为了找一下是否有“中国”的用户,很明显,后面的会比前面的更有效率。(请注意,第一条中是Select*,第二条是Select1)//没有效率的:$r=mysql_query("SELECT*FROMuserWHEREcountry='China'");if(mysql_num_rows($r)>0){//...}//有效率的:$r=mysql_query("SELECT1FROMuserWHEREcountry='China'LIMIT1");if(mysql_num_rows($r)>0){//...}4.为搜索字段建索引索引并不一定就是给主键或是唯一的字段。如果在你的表中,有某个字段你总要会经常用来做搜索,那么,请为其建立索引吧。从上图你可以看到那个搜索字串“last_nameLIKE‘a%’”,一个是建了索引,一个是没有索引,性能差了4倍左右。另外,你应该也需要知道什么样的搜索是不能使用正常的索引的。例如,当你需要在一篇大的文章中搜索一个词时,如:“WHEREpost_contentLIKE‘%apple%’”,索引可能是没有意义的。你可能需要使用MySQL全文索引或是自己做一个索引(比如说:搜索关键词或是Tag什么的)5.在Join表的时候使用相当类型的例,并将其索引如果你的应用程序有很多JOIN查询,你应该确认两个表中Join的字段是被建过索引的。这样,MySQL内部会启动为你优化Join的SQL语句的机制。而且,这些被用来Join的字段,应该是相同的类型的。例如:如果你要把DECIMAL字段和一个INT字段Join在一起,MySQL就无法使用它们的索引。对于那些STRING类型,还需要有相同的字符集才行。(两个表的字符集有可能不一样)//在state中查找company$r=mysql_query("SELECTcompany_nameFROMusersLEFTJOINcompaniesON(users.state=companies.state)WHEREusers.id=$user_id");//两个state字段应该是被建过索引的,而且应该是相当的类型,相同的字符集。6.千万不要ORDERBYRAND()想打乱返回的数据行?随机挑一个数据?真不知道谁发明了这种用法,但很多新手很喜欢这样用。但你确不了解这样做有多么可怕的性能问题。如果你真的想把返回的数据行打乱了,你有N种方法可以达到这个目的。这样使用只让你的数据库的性能呈指数级的下降。这里的问题是:MySQL会不得不去执行RAND()函数(很耗CPU时间),而且这是为了每一行记录去记行,然后再对其排序。就算是你用了Limit1也无济于事(因为要排序)下面的示例是随机挑一条记录//千万不要这样做:$r=mysql_query("SELECTusernameFROMuserORDERBYRAND()LIMIT1");//这要会更好:$r=mysql_query("SELECTcount(*)FROMuser");$d=mysql_fetch_row($r);$rand=mt_rand(0,$d[0]-1);$r=mysql_query("SELECTusernameFROMuserLIMIT$rand,1");7.避免SELECT*从数据库里读出越多的数据,那么查询就会变得越慢。并且,如果你的数据库服务器和WEB服务器是两台独立的服务器的话,这还会增加网络传输的负载。所以,你应该养成一个需要什么就取什么的好的习惯。//不推荐$r=mysql_query("SELECT*FROMuserWHEREuser_id=1");$d=mysql_fetch_assoc($r);echo"Welcome{$d['username']}";//推荐$r=mysql_query("SELECTusernameFROMuserWHEREuser_id=1");$d=mysql_fetch_assoc($r);echo"Welcome{$d['username']}";8.永远为每张表设置一个ID我们应该为数据库里的每张表都设置一个ID做为其主键,而且最好的是一个INT型的(推荐使用UNSIGNED),并设置上自动增加的AUTO_INCREMENT标志。就算是你users表有一个主键叫“email”的字段,你也别让它成为主键。使用VARCHAR类型来当主键会使用得性能下降。另外,在你的程序中,你应该使用表的ID来构造你的数据结构。而且,在MySQL数据引擎下,还有一些操作需要使用主键,在这些情况下,主键的性能和设置变得非常重要,比如,集群,分区……在这里,只有一个情况是例外,那就是“关联表”的“外键”,也就是说,这个表的主键,通过若干个别的表的主键构成。我们把这个情况叫做“外键”。比如:有一个“学生表”有学生的ID,有一个“课程表”有课程ID,那么,“成绩表”就是“关联表”了,其关联了学生表和课程表,在成绩表中,学生ID和课程ID叫“外键”其共同组成主键。9.使用ENUM而不是VARCHARENUM类型是非常快和紧凑的。在实际上,其保存的是TINYINT,但其外表上显示为字符串。这样一来,用这个字段来做一些选项列表变得相当的完美。如果你有一个字段,比如“性别”,“国家”,“民族”,“状态”或“部门”,你知道这些字段的取值是有限而且固定的,那么,你应该使用ENUM而不是VARCHAR。MySQL也有一个“建议”(见第十条)告诉你怎么去重新组织你的表结构。当你有一个VARCHAR字段时,这个建议会告诉你把其改成ENUM类型。使用PROCEDUREANALYSE()你可以得到相关的建议。10.从PROCEDUREANALYSE()取得建议PROCEDUREANALYSE()会让MySQL帮你去分析你的字段和其实际的数据,并会给你一些有用的建议。只有表中有实际的数据,这些建议才会变得有用,因为要做一些大的决定是需要有数据作为基础的。例如,如果你创建了一个INT字段作为你的主键,然而并没有太多的数据,那么,PROCEDUREANALYSE()会建议你把这个字段的类型改成MEDIUMINT。或是你使用了一个VARCHAR字段,因为数据不多,你可能会得到一个让你把它改成ENUM的建议。这些建议,都是可能因为数据不够多,所以决策做得就不够准。在phpmyadmin里,你可以在查看表时,点击“Proposetablestructure”来查看这些建议一定要注意,这些只是建议,只有当你的表里的数据越来越多时,这些建议才会变得准确。一定要记住,你才是最终做决定的人。 6.千万不要ORDERBYRAND()想打乱返回的数据行?随机挑一个数据?真不知道谁发明了这种用法,但很多新手很喜欢这样用。但你确不了解这样做有多么可怕的性能问题。如果你真的想把返回的数据行打乱了,你有N种方法可以达到这个目的。这样使用只让你的数据库的性能呈指数级的下降。这里的问题是:MySQL会不得不去执行RAND()函数(很耗CPU时间),而且这是为了每一行记录去记行,然后再对其排序。就算是你用了Limit1也无济于事(因为要排序)下面的示例是随机挑一条记录//千万不要这样做:$r=mysql_query("SELECTusernameFROMuserORDERBYRAND()LIMIT1");//这要会更好:$r=mysql_query("SELECTcount(*)FROMuser");$d=mysql_fetch_row($r);$rand=mt_rand(0,$d[0]-1);$r=mysql_query("SELECTusernameFROMuserLIMIT$rand,1");7.避免SELECT*从数据库里读出越多的数据,那么查询就会变得越慢。并且,如果你的数据库服务器和WEB服务器是两台独立的服务器的话,这还会增加网络传输的负载。所以,你应该养成一个需要什么就取什么的好的习惯。//不推荐$r=mysql_query("SELECT*FROMuserWHEREuser_id=1");$d=mysql_fetch_assoc($r);echo"Welcome{$d['username']}";//推荐$r=mysql_query("SELECTusernameFROMuserWHEREuser_id=1");$d=mysql_fetch_assoc($r);echo"Welcome{$d['username']}";8.永远为每张表设置一个ID我们应该为数据库里的每张表都设置一个ID做为其主键,而且最好的是一个INT型的(推荐使用UNSIGNED),并设置上自动增加的AUTO_INCREMENT标志。就算是你users表有一个主键叫“email”的字段,你也别让它成为主键。使用VARCHAR类型来当主键会使用得性能下降。另外,在你的程序中,你应该使用表的ID来构造你的数据结构。而且,在MySQL数据引擎下,还有一些操作需要使用主键,在这些情况下,主键的性能和设置变得非常重要,比如,集群,分区……在这里,只有一个情况是例外,那就是“关联表”的“外键”,也就是说,这个表的主键,通过若干个别的表的主键构成。我们把这个情况叫做“外键”。比如:有一个“学生表”有学生的ID,有一个“课程表”有课程ID,那么,“成绩表”就是“关联表”了,其关联了学生表和课程表,在成绩表中,学生ID和课程ID叫“外键”其共同组成主键。9.使用ENUM而不是VARCHARENUM类型是非常快和紧凑的。在实际上,其保存的是TINYINT,但其外表上显示为字符串。这样一来,用这个字段来做一些选项列表变得相当的完美。如果你有一个字段,比如“性别”,“国家”,“民族”,“状态”或“部门”,你知道这些字段的取值是有限而且固定的,那么,你应该使用ENUM而不是VARCHAR。MySQL也有一个“建议”(见第十条)告诉你怎么去重新组织你的表结构。当你有一个VARCHAR字段时,这个建议会告诉你把其改成ENUM类型。使用PROCEDUREANALYSE()你可以得到相关的建议。10.从PROCEDUREANALYSE()取得建议PROCEDUREANALYSE()会让MySQL帮你去分析你的字段和其实际的数据,并会给你一些有用的建议。只有表中有实际的数据,这些建议才会变得有用,因为要做一些大的决定是需要有数据作为基础的。例如,如果你创建了一个INT字段作为你的主键,然而并没有太多的数据,那么,PROCEDUREANALYSE()会建议你把这个字段的类型改成MEDIUMINT。或是你使用了一个VARCHAR字段,因为数据不多,你可能会得到一个让你把它改成ENUM的建议。这些建议,都是可能因为数据不够多,所以决策做得就不够准。在phpmyadmin里,你可以在查看表时,点击“Proposetablestructure”来查看这些建议一定要注意,这些只是建议,只有当你的表里的数据越来越多时,这些建议才会变得准确。一定要记住,你才是最终做决定的人。16.垂直分割“垂直分割”是一种把数据库中的表按列变成几张表的方法,这样可以降低表的复杂度和字段的数目,从而达到优化的目的。(以前,在银行做过项目,见过一张表有100多个字段,很恐怖)示例一:在Users表中有一个字段是家庭地址,这个字段是可选字段,相比起,而且你在数据库操作的时候除了个人信息外,你并不需要经常读取或是改写这个字段。那么,为什么不把他放到另外一张表中呢?这样会让你的表有更好的性能,大家想想是不是,大量的时候,我对于用户表来说,只有用户ID,用户名,口令,用户角色等会被经常使用。小一点的表总是会有好的性能。示例二:你有一个叫“last_login”的字段,它会在每次用户登录时被更新。但是,每次更新时会导致该表的查询缓存被清空。所以,你可以把这个字段放到另一个表中,这样就不会影响你对用户ID,用户名,用户角色的不停地读取了,因为查询缓存会帮你增加很多性能。另外,你需要注意的是,这些被分出去的字段所形成的表,你不会经常性地去Join他们,不然的话,这样的性能会比不分割时还要差,而且,会是极数级的下降。17.拆分大的DELETE或INSERT语句如果你需要在一个在线的网站上去执行一个大的DELETE或INSERT查询,你需要非常小心,要避免你的操作让你的整个网站停止相应。因为这两个操作是会锁表的,表一锁住了,别的操作都进不来了。Apache会有很多的子进程或线程。所以,其工作起来相当有效率,而我们的服务器也不希望有太多的子进程,线程和数据库链接,这是极大的占服务器资源的事情,尤其是内存。如果你把你的表锁上一段时间,比如30秒钟,那么对于一个有很高访问量的站点来说,这30秒所积累的访问进程/线程,数据库链接,打开的文件数,可能不仅仅会让你泊WEB服务Crash,还可能会让你的整台服务器马上掛了。所以,如果你有一个大的处理,你定你一定把其拆分,使用LIMIT条件是一个好的方法。下面是一个示例:while(1){//每次只做1000条mysql_query("DELETEFROMlogsWHERElog_date<='2009-11-01'LIMIT1000");if(mysql_affected_rows()==0){//没得可删了,退出!break;}//每次都要休息一会儿usleep(50000);}18.越小的列会越快对于大多数的数据库引擎来说,硬盘操作可能是最重大的瓶颈。所以,把你的数据变得紧凑会对这种情况非常有帮助,因为这减少了对硬盘的访问。参看MySQL的文档StorageRequirements查看所有的数据类型。如果一个表只会有几列罢了(比如说字典表,配置表),那么,我们就没有理由使用INT来做主键,使用MEDIUMINT,SMALLINT或是更小的TINYINT会更经济一些。如果你不需要记录时间,使用DATE要比DATETIME好得多。当然,你也需要留够足够的扩展空间,不然,你日后来干这个事,你会死的很难看,参看Slashdot的例子(2009年11月06日),一个简单的ALTERTABLE语句花了3个多小时,因为里面有一千六百万条数据。19.选择正确的存储引擎在MySQL中有两个存储引擎MyISAM和InnoDB,每个引擎都有利有弊。酷壳以前文章《MySQL:InnoDB还是MyISAM?》讨论和这个事情。MyISAM适合于一些需要大量查询的应用,但其对于有大量写操作并不是很好。甚至你只是需要update一个字段,整个表都会被锁起来,而别的进程,就算是读进程都无法操作直到读操作完成。另外,MyISAM对于SELECTCOUNT(*)这类的计算是超快无比的。InnoDB的趋势会是一个非常复杂的存储引擎,对于一些小的应用,它会比MyISAM还慢。他是它支持“行锁”,于是在写操作比较多的时候,会更优秀。并且,他还支持更多的高级应用,比如:事务。下面是MySQL的手册target=”_blank”MyISAMStorageEngineInnoDBStorageEngine20.使用一个对象关系映射器(ObjectRelationalMapper)使用ORM(ObjectRelationalMapper),你能够获得可靠的性能增涨。一个ORM可以做的所有事情,也能被手动的编写出来。但是,这需要一个高级专家。ORM的最重要的是“LazyLoading”,也就是说,只有在需要的去取值的时候才会去真正的去做。但你也需要小心这种机制的副作用,因为这很有可能会因为要去创建很多很多小的查询反而会降低性能。ORM还可以把你的SQL语句打包成一个事务,这会比单独执行他们快得多得多。目前,个人最喜欢的PHP的ORM是:Doctrine。21.小心“永久链接”“永久链接”的目的是用来减少重新创建MySQL链接的次数。当一个链接被创建了,它会永远处在连接的状态,就算是数据库操作已经结束了。而且,自从我们的Apache开始重用它的子进程后——也就是说,下一次的HTTP请求会重用Apache的子进程,并重用相同的MySQL链接。PHP手册:mysql_pconnect()在理论上来说,这听起来非常的不错。但是从个人经验(也是大多数人的)上来说,这个功能制造出来的麻烦事更多。因为,你只有有限的链接数,内存问题,文件句柄数,等等。而且,Apache运行在极端并行的环境中,会创建很多很多的了进程。这就是为什么这种“永久链接”的机制工作地不好的原因。在你决定要使用“永久链接”之前,你需要好好地考虑一下你的整个系统的架构。