数据库是Web大多数应用开发的基础。如果你是用PHP,那么大多数据库用的是MySQL也是LAMP架构的重要部分。PHP看起来很简单,一个初学者也可以几个小时内就能开始写函数了。但是建立一个稳定、可靠的数据库确需要时间和经验。下面就是一些这样的经验,不仅仅是MYSQL,其他数据库也一样可以参考。

1.使用MyISAM而不是InnoDB
MySQL有很多的数据库引擎,单一般也就用MyISAM和InnoDB。
MyISAM 是默认使用的。但是除非你是建立一个非常简单的数据库或者只是实验性的,那么到大多数时候这个选择是错误的。MyISAM不支持外键的约束,这是保证数据完整性的精华所在啊。另外,MyISAM会在添加或者更新数据的时候将整个表锁住,这在以后的扩展性能上会有很大的问题。
解决办法很简单:使用InnoDB。
2.使用PHP的mysql方法
PHP从一开始就提供了MySQL的函数库。很多程序都依赖于mysql_connect、mysql_query、mysql_fetch_assoc等等,但是PHP手册中建议:
如果你使用的MySQL版本在4.1.3之后,那么强烈建议使用mysqli扩展。
mysqli,或者说MySQL的高级扩展,有一些优点:
有面向对象的接口
prepared statements(预处理语句,可以有效防止SQL-注入攻击,还能提高性能)
支持多种语句和事务
另外,如果你想支持多数据库那么应该考虑一下PDO。
3.不过滤用户输入
应该是:永远别相信用户的输入。用后端的PHP来校验过滤每一条输入的信息,不要相信Javascript。像下面这样的SQL语句很容易就会被攻击:
$username = $_POST["name"];
$password = $_POST["password"];
$sql = "SELECT userid FROM usertable WHERE username=?$username?AND password=?$password?;"; // run query...
这样的代码,如果用户输入”admin’;”那么,就相当于下面这条了:
SELECT userid FROM usertable WHERE username=?admin?;
这样入侵者就能不输入密码,就通过admin身份登录了。
4.不使用UTF-8
那些英美国家的用户,很少考虑语言的问题,这样就造成很多产品就不能在其他地方通用。还有一些GBK编码的,也会有很多的麻烦。
UTF-8解决了很多国际化的问题。虽然PHP6才能比较完美的解决这个问题,但是也不妨碍你将MySQL的字符集设置为UTF-8。
5.该用SQL的地方使用PHP
如果你刚接触MySQL,有时候解决问题的时候可能会先考虑使用你熟悉的语言来解决。这样就可能造成一些浪费和性能比较差的情况。比如:计算平均值的时候不适用MySQL原生的AVG()方法,而是用PHP将所有值循环一遍然后累加计算平均值。
另外还要注意SQL查询中的PHP循环。通常,在取得所有结果之后再用PHP来循环的效率更高。
一般在处理大量数据的时候使用强有力的数据库方法,更能提高效率。
6.不优化查询
99%的PHP性能问题都是数据库造成的,一条糟糕的SQL语句可能让你的整个程序都非常慢。MySQL的EXPLAIN statement,Query Profiler,many other tools的这些工具可以帮你找出那些调皮的SELECT。
7.使用错误的数据类型
MySQL提供一系列数字、字符串、时间等的数据类型。如果你想存储日期,那么就是用DATE或者DATETIME类型,使用整形或者字符串会让事情更加复杂。
有时候你想用自己定义的数据类型,例如,使用字符串存储序列化的PHP对象。数据库的添加可能很容易,但是这样的话,MySQL就会变得很笨重,而且以后可能导致一些问题。
8.在SELECT查询中使用*
不要使用*在表中返回所有的字段,这会非常的慢。你只需要取出你需要的数据字段。如果你需要取出所有的字段,那么可能你的表需要更改了。
9.索引不足或者过度索引
一般来说,应该索引出现在SELECT语句中WHERE后面所有的字段。
例如,假如我们的用户表有一个数字的ID(主键)和email地址。登录之后,MySQL应该通过email找到相应的ID。通过索引,MySQL可以通过搜索算法很快的定位email。如果没有索引,MySQL就需要检查每一项记录直到找到。
这样的话,你可能想给每一个字段都添加索引,但是这样做的后果就是在你更新或者添加的时候,索引就会重新做一遍,当数据量大的时候,就会有性能问题。所以,只在需要的字段做索引。
10.不备份
也许不常发生,但是数据库损毁,硬盘坏了、服务停止等等,这些都会对数据造成灾难性的破坏。所以你一定要确保自动备份数据或者保存副本。
11.另外:不考虑其他数据库
MySQL可能是PHP用的最多的数据库了,但是也不是唯一的选择。 PostgreSQL和Firebird也是竞争者,他们都开源,而且不被某些公司所控制。微软提供SQL Server Express,Oracle有10g Express,这些企业级的也有免费版。SQLite对于一些小型的或者嵌入式应用来说也是不错的选择。

原文地址:http://www.cnbeta.com/articles/169770.htm

posted @ 2012-01-30 11:27 阿C++ 阅读(6) 评论(0) 编辑

忘记“百度知道”是什么时候面世的了,从中学的时候就开始用了,为我找到了无数问题的答案。
后来其它互联网公司也出了同类产品,比如新浪的爱问,腾讯的问问,还有奇虎问答,天涯问答等等一大堆。但我都只觉得百度知道亲切。

最近发现百度知道的提问框耳目一新,我非常快捷地就发布了一个关于网络管理方面的问题,并且发现,百度根据我填写的问题,自动为我先择了问题的分类,而且在我输入问题的过程中,百度同时在分析我输入的文字,并在下方的页面空白处为我筛选出一些可能帮到我的别人已提出的已解决的问题。如图所示:

这个过程实在太享受了,我不知道其它的同类产品有没有做到这样,但我想说的是我在此感受到了百度用户体验团队的用心。

以前我做项目时,发布信息都是由用户来选择要发布的信息的分类,也不觉得有什么不妥,但是我今天发现,那实在太糟糕了。
首先,这不是用户想做的事情,他只是想提出问题并找到答案,他不愿意作额外的思考去归纳他的问题属于网站内容分类中的哪一类,这太伤脑筋了。
其次,对问题的分类归纳应该是网站系统自身的工作,不应该动不动就让用户来帮你钩这个选哪个。

所以,为了我们拥有更好的互联网,大家一起来为网站的用户体验而努力吧!

posted @ 2011-09-30 13:01 阿C++ 阅读(2331) 评论(25) 编辑

一般偷懒的时候,我们会说网络带宽是10兆,不要以为最大下载流量可以达到每秒下载10兆的文件。其实是10/8 =1.25兆。原因是因为带宽的单位和硬盘的单位是不一样的。

容易误解的技术概念:Kb是否等于KB

经常遇到这样一个疑惑,假设我们明明申请的100Mb带宽的光纤,可是当我下载本地网站的软件时,下载速度只能达到10MB左右的速度,而电信部门却说我的网络速度非常正常。原因在哪里呢?

细心的读者肯定会发现,我上面的两个数据中,单位不同,一个是Mb,另外一个是MB。具体的差别就是在这里了。我们在日常的书写中,经常会不注意上面的细节,而这两个单位,真正的含义是不同的。Mb(全称为Mbps)这是电信部门衡量网络带宽的单位,意思是兆比特位每秒(M与K是什么含义,相信不用再解释了吧),而MB(Mbytes)是电脑文件容量的单位兆字节。由于我们在查阅资料时,忽略了这一点微小的差别,造成了对Kb与KB的误解。

带宽单位详解:在我们的记忆中,我们恐怕最熟悉的就是当初用Modem接入互联网时,接入的速度仅仅为56Kbps。在这个单位中,bps是bit Per Second的缩写,翻译成中文就是比特位每秒,也就是表示一秒钟传输多少位(bit)的意思。那么,位与字节之间,又有什么联系呢?

存储单位详解:我们最常听到的一句话就是,80G的硬盘。G就是硬盘容量的单位,也是存储的单位。存储的最小单位是字节Byte,对于存储单位,有以下几个单位,GB、MB和KB,那么这三者之间的换算关系是:1GB=1024MB,1MB=1024KB,1KB=1024Bytes。

Kb与KB之间的关系:我们在电脑原理中知道,电脑的最小存储单位是字节Byte,一个字节,是由八位二进制位组成的。由此,我们可以这样认为,一个字节是由8个位组成的,或者说一个字节与八个位所占的空间是相同的。因为,当我们使用100Mb带宽的网络下载时,理论上的速度应该是100除以8等于12.5MB。

点评:看了以上的介绍,我们就会明白,网络带宽的单位与下载的单位,其实是不同的。因此,我们在阅读技术资料,或者写作时,必须仔细阅读每一个细节。

带宽1M,是1024K个bit,即16个64K bit
磁盘是1M,是1024K个byte ( 1M = 1024K = 1024*1024 byte =1024*1024 * 8bit )

由于网络的传输最早基于bit处理的,所以到现在还是以bit为单位,一般以nn Kb/s,或者nnMb/s表示,比如v.90的modem的理论最大传输速度是6Kb/s,
而磁盘是并行传输的,最早是以byte传输的,所以到现在还是以byte为单位,一般以nnMB/s表示,比如ultra scsi是40MB/s的传输速度。
注意:一个是以bit , 一个是byte! 简单来说他们就是有8倍的差异。

posted @ 2011-08-20 11:53 阿C++ 阅读(130) 评论(0) 编辑

自从进了这家贸易公司以来,就再没有接过私人项目来做。

近日见回一个高中同学,他是华软毕业的。不知道他学的具体是什么专业,反正是计算机类的啦,华软嘛。他现在做的是Web前端开发,也就是跟浏览器打交道,HTML、CSS、Javascript、Flash这些东东。据说接触此领域已经快两年,现在供职于某软件开发公司,公司主要给电信企业内部做Web应用系统。

就他的资历来说,我是不把这种层次放在眼内的,但是做人要谦虚啊,可能人家比你更历害呢,况且人家是你朋友。不过起码他是在真正的软件公司上班,而我只是在一个贸易公司里打滚,这点就已经显得他比我专业了很多了,而且他工资又高我那么多,真没脸啊。

于是他也挺照顾我,说有目项搞时预埋我,我心里也很感动,说不定可以一起学到很多软件项目知识呢。

过了两天,果然来了,说有个PHP项目搞,问我有没兴趣。

我当然有啦,问大概是怎么样的项目。

“用PHP开发一个系统,主要功能是智能建站,空间管理,和域名绑定”,这是原话。这时我并没有意识到,他的概念里,这就是所谓的很简单的需求了。

说实话,我并没有做这些系统的经验,完全没有,但是太概知道是个什么东西。按照他的话来看,应该是一个Web系统,包含一个可以通过拖曳和设置等非编程方式来建设网站的系统,包含一个用于销售和管理服务器空间的系统,包括一个域名代理注册管理系统(可以管理与本系统中的服务器空间的管理关系)。如此看来,这真是一个庞大的系统啊,对方想用来做什么业务基本上也知道了吧,他们想做一个类似新网、万网这样业务系统。哇,那投资一定很大吧。

于是我说没经验,没什么信心,但还是想试试的,想更多地去了解客户的需求,于是问题了一系列与了解对方需求有关的内容。

但他没有回答我问及的任何问题,而是说:“其实这是一个很小的项目”,这又是他的原话。

这时我懵了,又问了些问题,主要是问这项目是不是他的朋友想搞的,就是那种不懂技术又想搞个网站赚钱那种人,又在网上找了几个比较有名的智能建站系统发给他看,想问拿这些系统来改下界面风格可不可以交货。

他没回答我的问题,最后就只是说了一句:“我找个有经验的人就可以搞定了”。

这时,我完全懵了。哇,我这些年是不是白搞开发了啊。到底是我太菜了,还是对方的问题。

posted @ 2011-08-02 12:21 阿C++ 阅读(5459) 评论(59) 编辑
这是掌握cookie最后的一个障碍:

缺省情况下cookie只能被在同一个Web服务器上同一个路径下设置了该cookie的网页读取.

例如,如果在"http://a.abc.com/food/bananas/banana_puree.htm"有一段Javascript询问了用户的姓名,你可能需要在你的另一个网页例如主页中访问一个给定的名字.所以你必须设定该cookie的路径.路径"path"用于设置可以读取一个cookie的最顶层的目录.将cookie的路径设置为你的网页最顶层的目录可以让该该目录下的所有网页都能访问该cookie.方法:在你的cookie中加入path=/; 如果你只想让"food" 目录中的网页可以使用该cookie,则你加入path=/food;.

还有一点:有些网站有许多小的域名,例如一个网站可能还在"b.abc.com," "c.abc.com," 和"d.abc.com." 域名下有网页.缺省情况下只有"a.abc.com" 域下的网页可以读取该cookie.如果你向让"abc.com"下的所有机器都可以读取该cookie,我们必须在cookie中加入 "domain=abc.com" .

posted @ 2011-06-10 15:04 阿C++ 阅读(74) 评论(0) 编辑
1.session.save_handler = files

   
* 1. session_start()
        
1. session_start()是session机制的开始,它有一定概率开启垃圾回收,因为session是存放在文件中,
PHP自身的垃圾回收是无效的,SESSION的回收是要删文件的,这个概率是根据php
.ini的配置决定的,
但是有的系统是 session
.gc_probability = 0,这也就是说概率是0,而是通过cron脚本来实现垃圾回收。

            session
.gc_probability = 1
            session
.gc_divisor = 1000
            session
.gc_maxlifetime = 1440//过期时间 默认24分钟
            //概率是 session.gc_probability/session.gc_divisor 结果 1/1000,
            //不建议设置过小,因为session的垃圾回收,是需要检查每个文件是否过期的。

            session.save_path = //好像不同的系统默认不一样,有一种设置是 "N;/path"
            //这是随机分级存储,这个样的话,垃圾回收将不起作用,需要自己写脚本


        
2. session会判断当前是否有$_COOKIE[session_name()];session_name()返回保存session_id的COOKIE键值,
这个值可以从php
.ini找到

            session
.name = PHPSESSID //默认值PHPSESSID
            

        
3. 如果不存在会生成一个session_id,然后把生成的session_id作为COOKIE的值传递到客户端.
相当于执行了下面COOKIE 操作,注意的是,这一步执行了setcookie()操作,COOKIE是在header头中发送的,
这之前是不能有输出的,PHP有另外一个函数
session_regenerate_id() 如果使用这个函数,这之前也是不能有输出的。

               
setcookie(session_name(),
                         
session_id(),
                          session
.cookie_lifetime,//默认0
                          session.cookie_path,//默认'/'当前程序跟目录下都有效
                          session.cookie_domain,//默认为空
                          )

        
4. 如果存在那么session_id = $_COOKIE[session_name];
            然后去session
.save_path指定的文件夹里去找名字为'SESS_' . session_id()的文件.
            读取文件的内容反序列化,然后放到
$_SESSION中
   
* 2.$_SESSION赋值
      比如新添加一个值
$_SESSION['test'] = 'blah'; 那么这个$_SESSION只会维护在内存中,当脚本执行结束的时候,
用把
$_SESSION的值写入到session_id指定的文件夹中,然后关闭相关资源.      这个阶段有可能执行更改session_id的操作,
比如销毁一个旧的的session_id,生成一个全新的session_id
.一半用在自定义 session操作,角色的转换上,
比如Drupal
.Drupal的匿名用户有一个SESSION的,当它登录后需要换用新的session_id

       
if (isset($_COOKIE[session_name()])) {
         
setcookie(session_name(), '', time() - 42000, '/');//旧session cookie过期
        }
       
session_regenerate_id();//这一步会生成新的session_id
       //session_id()返回的是新的值


     
3.写入SESSION操作
      在脚本结束的时候会执行SESSION写入操作,把
$_SESSION中值写入到session_id命名的文件中,可能已经存在,
可能需要创建新的文件。
   
* 4. 销毁SESSION
      SESSION发出去的COOKIE一般属于即时COOKIE,保存在内存中,当浏览器关闭后,才会过期,假如需要人为强制过期,
比如 退出登录,而不是关闭浏览器,那么就需要在代码里销毁SESSION,方法有很多,
          o
1. setcookie(session_name(), session_id(), time() - 8000000, ..);//退出登录前执行
          o 2. usset($_SESSION);//这会删除所有的$_SESSION数据,刷新后,有COOKIE传过来,但是没有数据。
          o 3. session_destroy();//这个作用更彻底,删除$_SESSION 删除session文件,和session_id

      当不关闭浏览器的情况下,再次刷新,2和3都会有COOKIE传过来,但是找不到数据

2.session.save_handler = user

      用户自定义session处理机制,更加直观
   
* session_set_save_handler('open', 'close', 'read', 'write', 'destroy', 'gc');
1.session_start(),
      执行open(
$save_path, $session_name)打开session操作句柄
     
$save_path 在session.save_handler = files的情况下它就是session.save_path,
但是如果用户自定的话,这个两个参数都用不上,直接返回TRUE

      执行read(
$id)从中读取数据.//这个参数是自动传递的就是session_id(),可以通过这个值进行操作。
    * 2.脚本执行结束
      执行write(
$id, $sess_data) //两个参数,很简单
    * 3.假如用户需要session_destroy()
      先执行destroy
.在执行第2步

      一个实际例子:

     
//SESSION初始化的时候调用
      function open($save_path, $session_name)
      {
       
global $sess_save_path;
       
$sess_save_path = $save_path;
       
return(true);
      }

     
//关闭的时候调用
      function close()
      {
       
return(true);
      }

     
function read($id)
      {
       
global $sess_save_path;
       
$sess_file = "$sess_save_path/sess_$id";
       
return (string) @file_get_contents($sess_file);
      }
     
//脚本执行结束之前,执行写入操作
      function write($id, $sess_data)
      {
       
echo "sdfsf";
       
global $sess_save_path;

       
$sess_file = "$sess_save_path/sess_$id";
       
if ($fp = @fopen($sess_file, "w")) {
         
$return = fwrite($fp, $sess_data);
         
fclose($fp);
         
return $return;
        }
else {
         
return(false);
        }

      }

     
function destroy($id)
      {
       
global $sess_save_path;

       
$sess_file = "$sess_save_path/sess_$id";
       
return(@unlink($sess_file));
      }

     
function gc($maxlifetime)
      {
       
global $sess_save_path;

       
foreach (glob("$sess_save_path/sess_*") as $filename) {
         
if (filemtime($filename) + $maxlifetime < time()) {
            @
unlink($filename);
          }
        }
       
return true;
      }
posted @ 2011-06-10 14:08 阿C++ 阅读(335) 评论(0) 编辑
摘要: 整数类型:TINYINT、SAMLLINT、MEDIUMINT、INT、BIGINT--1字节、2、3.、4、8浮点数类型:FLOAT(m,d)、DOUBLE(m,d)==REAL-4字节、8定点数类型:DECIMAL(m,d)、NUMERIC-m+2字节、8位类型:BIT(m)-1-8字节各个类型的详细范围可以参考mysql文档数据类型小例:1.整数类型createtablet1(idint,id2int(4));insertt1select1,2;select*fromt1;+------+------+|id|id2|+------+------+|1|2|+------+------+阅读全文
posted @ 2011-06-09 15:35 阿C++ 阅读(70) 评论(0) 编辑
摘要: 为了让打印机的作用发挥得更充分一些,将其设置成共享打印机已经是十分平常的事情;尽管使用共享打印机给我们带来了方便,不过在访问共享打印机的过程中,我们时常会遇到一些莫名其妙的打印故障,这些故障严重影响了我们的共享打印效率。为了提高共享打印效率,我们需要对平时遇到的共享打印故障进行总结,以便日后能够快速地解决各种打印故障。现在本文就贡献一则共享打印故障的排除心得,希望能对大家有点用处。  共享打印机拒...阅读全文
posted @ 2010-10-22 15:39 阿C++ 阅读(1847) 评论(0) 编辑
摘要: package { import flash.events.Event; //导入事件类 public class CustomEvent extends Event { //声明自定义事件扩展自事件类成为其子类 public static const SENDFLOWER:String="sendFlower"; //声明静态常量作为事件类型1 public static const SEN...阅读全文
posted @ 2010-10-19 16:49 阿C++ 阅读(864) 评论(0) 编辑
摘要: win2008远程桌面端口默认是用的是3389端口,但是由于安全考虑,经常我们安装好系统后一般都会考虑把原来的3389端口更改为另外的端口。本文以改为端口为25608商品为例,讲解一下具体操作过程。打开注册表: 运行regedit。找到:[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\Wds\rdpwd\Td...阅读全文
posted @ 2010-09-03 12:31 阿C++ 阅读(2241) 评论(0) 编辑