前言:这两天机器坏了,正在送修中,写个系列的大型网站架构的文章,希望对有志在互联网做出一番事业的站长朋友们一些帮助。
注意:这里的大型网站架构只包括高互动性高交互性的数据型大型网站,基于大家众所周知的原因,我们就不谈新闻类和一些依靠HTML静态化就可以实现的架构了,我们以高负载高数据交换高数据流动性的网站为例,比如海内,开心网等类似的web2.0系列架构。我们这里不讨论是PHP还是JSP或者.NET环境,我们从架构的方面去看问题,实现语言方面并不是问题,语言的优势在于实现而不是好坏,不论你选择任何语言,架构都是必须要面对的。
文入正题:
首先讨论一下大型网站需要注意和考虑的问题
A. 海量数据的处理。
众所周知,对于一些相对小的站点来说,数据量并不是很大,select和update就可以解决我们面对的问题,本身负载量不是很大,最多再加几个索引就可以搞定。对于大型网站,每天的数据量可能就上百万,如果一个设计不好的多对多关系,在前期是没有任何问题的,但是随着用户的增长,数据量会是几何级的增长的。在这个时候我们对于一个表的select和update的时候(还不说多表联合查询)的成本的非常高的。
B. 数据并发的处理
在一些时候,2.0的CTO都有个尚方宝剑,就是缓存。对于缓存,在高并发高处理的时候也是个大问题。在整个应用程序下,缓存是全局共享的,然而在我们进行修改的时候就,如果两个或者多个请求同时对缓存有更新的要求的情况下,应用程序会直接的死掉。这个时候,就需要一个好的数据并发处理策略以及缓存策略。
另外,就是数据库的死锁问题,也许平时我们感觉不到,死锁在高并发的情况下的出现的概率是非常高的,磁盘缓存就是一个大问题。
C. 文件存贮的问题
对于一些支持文件上传的2.0的站点,在庆幸硬盘容量越来越大的时候我们更多的应该考虑的是文件应该如何被存储并且被有效的索引。常见的方案是对文件按照日期和类型进行存贮。但是当文件量 是海量的数据的情况下,如果一块硬盘存贮了500个G的琐碎文件,那么维护的时候和使用的时候磁盘的Io就是一个巨大的问题,哪怕你的带宽足够,但是你的磁盘也未必响应过来。如果这个时候还涉及上传,磁盘很容易就over了。
也许用raid和专用存贮服务器能解决眼下的问题,但是还有个问题就是各地的访问问题,也许我们的服务器在北京,可能在云南或者新疆的访问速度如何解决?如果做分布式,那么我们的文件索引以及架构该如何规划。
所以我们不得不承认,文件存贮是个很不容易的问题
D. 数据关系的处理
我们可以很容易的规划出一个符合第三范式的数据库,里面布满了多对多关系,还能用GUID来替换INDENTIFY COLUMN 但是,多对多关系充斥的2.0时代,第三范式是第一个应该被抛弃的。必须有效的把多表联合查询降到最低。
E. 数据索引的问题
众所周知,索引是提高数据库效率查询的最方面最廉价最容易实现的方案。但是,在高UPDATE的情况下,update和delete付出的成本会高的无法想想,笔者遇到过一个情况,在更新一个聚焦索引的时候需要10分钟来完成,那么对于站点来说,这些基本上是不可忍受的。
索引和更新是一对天生的冤家,问题A,D,E这些是我们在做架构的时候不得不考虑的问题,并且也可能是花费时间最多的问题,
F. 分布式处理
对于2.0网站由于其高互动性,CDN实现的效果基本上为0,内容是实时更新的,我们常规的处理。为了保证各地的访问速度,我们就需要面对一个绝大的问题,就是如何有效的实现数据同步和更新,实现各地服务器的实时通讯有是一个不得不需要考虑的问题。
G. Ajax的利弊分析
成也AJAX,败也AJAX,AJAX成为了主流趋势,突然发现基于XMLHTTP的post和get是如此的容易。客户端get或者post到服务器数据,服务器接到数据请求之后返回来,这是一个很正常的AJAX请求。但是在AJAX处理的时候,如果我们使用一个抓包工具的话,对数据返回和处理是一目了然。对于一些计算量大的AJAX请求的话,我们可以构造一个发包机,很容易就可以把一个webserver干掉。
H. 数据安全性的分析
对于HTTP协议来说,数据包都是明文传输的,也许我们可以说我们可以用加密啊,但是对于G问题来说的话,加密的过程就可能是明文了(比如我们知道的QQ,可以很容易的判断他的加密,并有效的写一个跟他一样的加密和解密方法出来的)。当你站点流量不是很大的时候没有人会在乎你,但是当你流量上来之后,那么所谓的外挂,所谓的群发就会接踵而来(从qq一开始的群发可见端倪)。也许我们可以很的意的说,我们可以采用更高级别的判断甚至HTTPS来实现,注意,当你做这些处理的时候付出的将是海量的database,io以及CPU的成本。对于一些群发,基本上是不可能的。笔者已经可以实现对于百度空间和qq空间的群发了。大家愿意试试,实际上并不是很难。
I. 数据同步和集群的处理的问题
当我们的一台databaseserver不堪重负的时候,这个时候我们就需要做基于数据库的负载和集群了。而这个时候可能是最让人困扰的的问题了,数据基于网络传输根据数据库的设计的不同,数据延迟是很可怕的问题,也是不可避免的问题,这样的话,我们就需要通过另外的手段来保证在这延迟的几秒或者更长的几分钟时间内,实现有效的交互。比如数据散列,分割,内容处理等等问题
K.数据共享的渠道以及OPENAPI趋势
Openapi已经成为一个不可避免的趋势,从google,facebook,myspace到海内校内,都在考虑这个问题,它可以更有效的留住用户并激发用户的更多的兴趣以及让更多的人帮助你做最有效的开发。这个时候一个有效的数据共享平台,数据开放平台就成为必不可少的途径了,而在开放的接口的情况保证数据的安全性和性能,又是一个我们必须要认真思考的问题了。
当然还有更多需要考虑的问题,我这里就写一个最需要考虑的问题,欢迎补充。
下一篇文章将针对问题A,提出具体的解决方案和思路
注意:这里的大型网站架构只包括高互动性高交互性的数据型大型网站,基于大家众所周知的原因,我们就不谈新闻类和一些依靠HTML静态化就可以实现的架构了,我们以高负载高数据交换高数据流动性的网站为例,比如海内,开心网等类似的web2.0系列架构。我们这里不讨论是PHP还是JSP或者.NET环境,我们从架构的方面去看问题,实现语言方面并不是问题,语言的优势在于实现而不是好坏,不论你选择任何语言,架构都是必须要面对的。
文入正题:
首先讨论一下大型网站需要注意和考虑的问题
A. 海量数据的处理。
众所周知,对于一些相对小的站点来说,数据量并不是很大,select和update就可以解决我们面对的问题,本身负载量不是很大,最多再加几个索引就可以搞定。对于大型网站,每天的数据量可能就上百万,如果一个设计不好的多对多关系,在前期是没有任何问题的,但是随着用户的增长,数据量会是几何级的增长的。在这个时候我们对于一个表的select和update的时候(还不说多表联合查询)的成本的非常高的。
B. 数据并发的处理
在一些时候,2.0的CTO都有个尚方宝剑,就是缓存。对于缓存,在高并发高处理的时候也是个大问题。在整个应用程序下,缓存是全局共享的,然而在我们进行修改的时候就,如果两个或者多个请求同时对缓存有更新的要求的情况下,应用程序会直接的死掉。这个时候,就需要一个好的数据并发处理策略以及缓存策略。
另外,就是数据库的死锁问题,也许平时我们感觉不到,死锁在高并发的情况下的出现的概率是非常高的,磁盘缓存就是一个大问题。
C. 文件存贮的问题
对于一些支持文件上传的2.0的站点,在庆幸硬盘容量越来越大的时候我们更多的应该考虑的是文件应该如何被存储并且被有效的索引。常见的方案是对文件按照日期和类型进行存贮。但是当文件量 是海量的数据的情况下,如果一块硬盘存贮了500个G的琐碎文件,那么维护的时候和使用的时候磁盘的Io就是一个巨大的问题,哪怕你的带宽足够,但是你的磁盘也未必响应过来。如果这个时候还涉及上传,磁盘很容易就over了。
也许用raid和专用存贮服务器能解决眼下的问题,但是还有个问题就是各地的访问问题,也许我们的服务器在北京,可能在云南或者新疆的访问速度如何解决?如果做分布式,那么我们的文件索引以及架构该如何规划。
所以我们不得不承认,文件存贮是个很不容易的问题
D. 数据关系的处理
我们可以很容易的规划出一个符合第三范式的数据库,里面布满了多对多关系,还能用GUID来替换INDENTIFY COLUMN 但是,多对多关系充斥的2.0时代,第三范式是第一个应该被抛弃的。必须有效的把多表联合查询降到最低。
E. 数据索引的问题
众所周知,索引是提高数据库效率查询的最方面最廉价最容易实现的方案。但是,在高UPDATE的情况下,update和delete付出的成本会高的无法想想,笔者遇到过一个情况,在更新一个聚焦索引的时候需要10分钟来完成,那么对于站点来说,这些基本上是不可忍受的。
索引和更新是一对天生的冤家,问题A,D,E这些是我们在做架构的时候不得不考虑的问题,并且也可能是花费时间最多的问题,
F. 分布式处理
对于2.0网站由于其高互动性,CDN实现的效果基本上为0,内容是实时更新的,我们常规的处理。为了保证各地的访问速度,我们就需要面对一个绝大的问题,就是如何有效的实现数据同步和更新,实现各地服务器的实时通讯有是一个不得不需要考虑的问题。
G. Ajax的利弊分析
成也AJAX,败也AJAX,AJAX成为了主流趋势,突然发现基于XMLHTTP的post和get是如此的容易。客户端get或者post到服务器数据,服务器接到数据请求之后返回来,这是一个很正常的AJAX请求。但是在AJAX处理的时候,如果我们使用一个抓包工具的话,对数据返回和处理是一目了然。对于一些计算量大的AJAX请求的话,我们可以构造一个发包机,很容易就可以把一个webserver干掉。
H. 数据安全性的分析
对于HTTP协议来说,数据包都是明文传输的,也许我们可以说我们可以用加密啊,但是对于G问题来说的话,加密的过程就可能是明文了(比如我们知道的QQ,可以很容易的判断他的加密,并有效的写一个跟他一样的加密和解密方法出来的)。当你站点流量不是很大的时候没有人会在乎你,但是当你流量上来之后,那么所谓的外挂,所谓的群发就会接踵而来(从qq一开始的群发可见端倪)。也许我们可以很的意的说,我们可以采用更高级别的判断甚至HTTPS来实现,注意,当你做这些处理的时候付出的将是海量的database,io以及CPU的成本。对于一些群发,基本上是不可能的。笔者已经可以实现对于百度空间和qq空间的群发了。大家愿意试试,实际上并不是很难。
I. 数据同步和集群的处理的问题
当我们的一台databaseserver不堪重负的时候,这个时候我们就需要做基于数据库的负载和集群了。而这个时候可能是最让人困扰的的问题了,数据基于网络传输根据数据库的设计的不同,数据延迟是很可怕的问题,也是不可避免的问题,这样的话,我们就需要通过另外的手段来保证在这延迟的几秒或者更长的几分钟时间内,实现有效的交互。比如数据散列,分割,内容处理等等问题
K.数据共享的渠道以及OPENAPI趋势
Openapi已经成为一个不可避免的趋势,从google,facebook,myspace到海内校内,都在考虑这个问题,它可以更有效的留住用户并激发用户的更多的兴趣以及让更多的人帮助你做最有效的开发。这个时候一个有效的数据共享平台,数据开放平台就成为必不可少的途径了,而在开放的接口的情况保证数据的安全性和性能,又是一个我们必须要认真思考的问题了。
当然还有更多需要考虑的问题,我这里就写一个最需要考虑的问题,欢迎补充。
下一篇文章将针对问题A,提出具体的解决方案和思路
在配置多个SSL的虚拟主机的时候,很容易想当然的像配置普通HTTP虚拟主机一样,新建一个Virtualhost后reload服务器。可用浏览器访问的时候,却提示证书为已经存在的某个虚拟主机的SSL证书,造成配置失败。
网上查询得知,一个普通的SSL证书是独占服务器端口。也就是说,如果Apache服务器上的虚拟主机A使用了SSL_A,并监听端口443,那即使新配置了虚拟主机B,如果虚拟主机是按照配置的,那用户访问时候读取的仍然是SSL_A。这和普通虚拟空间可以通过设置域名来区别虚拟主机是不同的。
要解决这个问题,实现一个Apache服务器上提供多个SSL虚拟主机,可以:
使用多域名SSL证书,可以实现一个IP,一个443端口上多个SSL虚拟主机;
为所有SSL虚拟主机配置单独的端口。比如,默认的虚拟主机使用443,其他的使用8080或8081等,且每个SSL虚拟主机必须独占一个端口;
为Apache服务器配置多个IP,每个SSL虚拟主机独占IP。如果只有一张物理网卡,可以配置为网卡配置子接口;
使用mod_gnutls模块,创建多个SSL虚拟主机
总之,Apache的SSL虚拟主机配置需要保证每个SSL虚拟主机有独立的IP:Port组合,如果不能提供这个条件,那只好多域名SSL证书了。
网上查询得知,一个普通的SSL证书是独占服务器端口。也就是说,如果Apache服务器上的虚拟主机A使用了SSL_A,并监听端口443,那即使新配置了虚拟主机B,如果虚拟主机是按照配置的,那用户访问时候读取的仍然是SSL_A。这和普通虚拟空间可以通过设置域名来区别虚拟主机是不同的。
要解决这个问题,实现一个Apache服务器上提供多个SSL虚拟主机,可以:
使用多域名SSL证书,可以实现一个IP,一个443端口上多个SSL虚拟主机;
为所有SSL虚拟主机配置单独的端口。比如,默认的虚拟主机使用443,其他的使用8080或8081等,且每个SSL虚拟主机必须独占一个端口;
为Apache服务器配置多个IP,每个SSL虚拟主机独占IP。如果只有一张物理网卡,可以配置为网卡配置子接口;
使用mod_gnutls模块,创建多个SSL虚拟主机
总之,Apache的SSL虚拟主机配置需要保证每个SSL虚拟主机有独立的IP:Port组合,如果不能提供这个条件,那只好多域名SSL证书了。
MySQL环境变量设置,将%MySQL_HOME%下的MySQL Server 5.1\bin放到Path下。
MySQL的mysqldump工具,基本用法是:
shell> mysqldump [OPTIONS] database [tables]
通过执行mysqldump --help,得到当前mysqldump版本支持的选项表。
通过执行mysqldump –V,得到当前mysqldump版本。
几个常用的例子(在mysqldump Ver 10.13 Distrib 5.1.30, for Win32 (ia32)下测试通过)
1.导出整个数据库
mysqldump -u 用户名 -p 数据库名 > 导出的文件名
mysqldump -u root -p student >d:\student.sql
2.导出一个数据库结构
mysqldump -u root -p -d --add-drop-table student >d:\student_structure.sql
-d 没有数据 --add-drop-table 在每个create语句之前增加一个drop table
3.导出一个表
mysqldump -u 用户名 -p 数据库名 表名> 导出的文件名
mysqldump -u root -p schoolproject student>d:\schoolproject_student.sql
4.导入数据库
shell> mysqladmin –u root –p create target_db_name
shell> mysql –u root –p target_db_name < backup-file.sql
就是:shell> mysql 数据库名 < 文件名
或者使用source 命令
进入mysql数据库控制台,mysql -u root –p
mysql>use 数据库
然后使用source命令,后面参数为脚本文件(.sql文件)
mysql>source d:\student.sql
MySQL的mysqldump工具,基本用法是:
shell> mysqldump [OPTIONS] database [tables]
通过执行mysqldump --help,得到当前mysqldump版本支持的选项表。
通过执行mysqldump –V,得到当前mysqldump版本。
几个常用的例子(在mysqldump Ver 10.13 Distrib 5.1.30, for Win32 (ia32)下测试通过)
1.导出整个数据库
mysqldump -u 用户名 -p 数据库名 > 导出的文件名
mysqldump -u root -p student >d:\student.sql
2.导出一个数据库结构
mysqldump -u root -p -d --add-drop-table student >d:\student_structure.sql
-d 没有数据 --add-drop-table 在每个create语句之前增加一个drop table
3.导出一个表
mysqldump -u 用户名 -p 数据库名 表名> 导出的文件名
mysqldump -u root -p schoolproject student>d:\schoolproject_student.sql
4.导入数据库
shell> mysqladmin –u root –p create target_db_name
shell> mysql –u root –p target_db_name < backup-file.sql
就是:shell> mysql 数据库名 < 文件名
或者使用source 命令
进入mysql数据库控制台,mysql -u root –p
mysql>use 数据库
然后使用source命令,后面参数为脚本文件(.sql文件)
mysql>source d:\student.sql
Ubuntu是一套基于Debian的Linux系统,它追求的是“Just Work”,最新的7.10版本发布于2007年10月,不同于其他Linux发行版本,Ubuntu的所有版本都是免费的,包括企业版。
第一次安装Ubuntu,发现比较“奇怪”的一点是,在安装过程中,不像其他发布版本那样,要求设置root的密码,也就无法以root登录了。经过去网上查询才发现:Ubuntu默认是关闭root帐户的,这样做有如下优点:
起初Ubuntu团队希望安装尽可能的简单,不使用root,在安装期间的两个用户交互步骤可以省略。(科林·沃森)
更进一步, 如果在安装中使用root,用户将被要求永远记住他们选择的密码--即使他们很少使用到它。Root密码经常被对Unix安全模型不熟悉的用户忘记。(马特·齐默曼)
它防止了缺省登录时“我能做任何事情”--在较大的变化发生之前,你将被提示输入口令,这可以使你考虑你这样做的结果。 如果你作为root登录,你可以删除一些“没用的文件夹”并且不会意识到你正处于错误的目录,那时已经太晚了。它是在Unix下长时间使用“su-command-^D”练习的情况下,代替一直呆在root shell--除非你做严重的系统维护(那时你仍然可以使用 "sudo su")。(吉姆·奇塔姆 和 安德鲁·索巴拉)
Sudo 增加了运行命令的日志记录(在/var/log/auth.log)。如果你陷入困境,你总是可以返回并看见那些运行的命令。(安德鲁·Zbikowski)
开启root帐号的方法:
为了启用root 帐号(也就是设置一个口令)使用:
sudo passwd root
当你使用完毕后屏蔽root帐号使用:
sudo passwd -l root
这个将锁住root帐号。
如何在终端模式下切换到root身份?
sudo -s -H
Password: <在此输入密码>
allen:用root帐户怎么也登陆不了。原来默认是关闭的。汗一个...-_-!! 帐户已经启用 哈哈YES
第一次安装Ubuntu,发现比较“奇怪”的一点是,在安装过程中,不像其他发布版本那样,要求设置root的密码,也就无法以root登录了。经过去网上查询才发现:Ubuntu默认是关闭root帐户的,这样做有如下优点:
起初Ubuntu团队希望安装尽可能的简单,不使用root,在安装期间的两个用户交互步骤可以省略。(科林·沃森)
更进一步, 如果在安装中使用root,用户将被要求永远记住他们选择的密码--即使他们很少使用到它。Root密码经常被对Unix安全模型不熟悉的用户忘记。(马特·齐默曼)
它防止了缺省登录时“我能做任何事情”--在较大的变化发生之前,你将被提示输入口令,这可以使你考虑你这样做的结果。 如果你作为root登录,你可以删除一些“没用的文件夹”并且不会意识到你正处于错误的目录,那时已经太晚了。它是在Unix下长时间使用“su-command-^D”练习的情况下,代替一直呆在root shell--除非你做严重的系统维护(那时你仍然可以使用 "sudo su")。(吉姆·奇塔姆 和 安德鲁·索巴拉)
Sudo 增加了运行命令的日志记录(在/var/log/auth.log)。如果你陷入困境,你总是可以返回并看见那些运行的命令。(安德鲁·Zbikowski)
开启root帐号的方法:
为了启用root 帐号(也就是设置一个口令)使用:
sudo passwd root
当你使用完毕后屏蔽root帐号使用:
sudo passwd -l root
这个将锁住root帐号。
如何在终端模式下切换到root身份?
sudo -s -H
Password: <在此输入密码>
allen:用root帐户怎么也登陆不了。原来默认是关闭的。汗一个...-_-!! 帐户已经启用 哈哈YES
一、简介
如果你对Unix/Linux有所了解的话,你应该知道他们大都自带了C和C++的编译器,分别是GCC和G++。Unix在程序安装及Make等许多地方使用到了这些编译器。利用一些控制台命令,C++和PHP, 我将向你介绍怎样生成一个完整的C++程序例子,他可以在用PHP程序来执行,并能获得相应的输出结果。我将先生成C++程序代码,并编译它,谈后讨论我们将如果通过使用PHP的函数passthru来执行这个程序。从某种意义上来说,这边文章给我们提供一种通过Web页面来访问一般程序的方法。
为了能更好的理解这篇文章,你应该有一台运行着apache和最新版本php的unix/Linux服务器。同时也应该掌握C++, unix控制台命令,当然一些PHP的编程经验也是必需的。
二、编写一个C++程序
阅读全文
如果你对Unix/Linux有所了解的话,你应该知道他们大都自带了C和C++的编译器,分别是GCC和G++。Unix在程序安装及Make等许多地方使用到了这些编译器。利用一些控制台命令,C++和PHP, 我将向你介绍怎样生成一个完整的C++程序例子,他可以在用PHP程序来执行,并能获得相应的输出结果。我将先生成C++程序代码,并编译它,谈后讨论我们将如果通过使用PHP的函数passthru来执行这个程序。从某种意义上来说,这边文章给我们提供一种通过Web页面来访问一般程序的方法。
为了能更好的理解这篇文章,你应该有一台运行着apache和最新版本php的unix/Linux服务器。同时也应该掌握C++, unix控制台命令,当然一些PHP的编程经验也是必需的。
二、编写一个C++程序

同时在线访问量继续增大 对于1G内存的服务器明显感觉到吃力严重时甚至每天都会死机 或者时不时的服务器卡一下 这个问题曾经困扰了我半个多月MySQL使用是很具伸缩性的算法,因此你通常能用很少的内存运行或给MySQL更多的被存以得到更好的性能。
安装好mysql后,配制文件应该在/usr/local/mysql/share/mysql目录中,配制文件有几个,有my-huge.cnf my-medium.cnf my-large.cnf my-small.cnf,不同的流量的网站和不同配制的服务器环境,当然需要有不同的配制文件了。
阅读全文
安装好mysql后,配制文件应该在/usr/local/mysql/share/mysql目录中,配制文件有几个,有my-huge.cnf my-medium.cnf my-large.cnf my-small.cnf,不同的流量的网站和不同配制的服务器环境,当然需要有不同的配制文件了。

其他强制操作,优先操作如下:
mysql常用的hint
对于经常使用oracle的朋友可能知道,oracle的hint功能种类很多,对于优化sql语句提供了很多方法。同样,在mysql里,也有类似的hint功能。下面介绍一些常用的。
强制索引 FORCE INDEX
SELECT * FROM TABLE1 FORCE INDEX (FIELD1) …
以上的SQL语句只使用建立在FIELD1上的索引,而不使用其它字段上的索引。
忽略索引 IGNORE INDEX
SELECT * FROM TABLE1 IGNORE INDEX (FIELD1, FIELD2) …
在上面的SQL语句中,TABLE1表中FIELD1和FIELD2上的索引不被使用。
关闭查询缓冲 SQL_NO_CACHE
SELECT SQL_NO_CACHE field1, field2 FROM TABLE1;
有一些SQL语句需要实时地查询数据,或者并不经常使用(可能一天就执行一两次),这样就需要把缓冲关了,不管这条SQL语句是否被执行过,服务器都不会在缓冲区中查找,每次都会执行它。
强制查询缓冲 SQL_CACHE
SELECT SQL_CALHE * FROM TABLE1;
如果在my.ini中的query_cache_type设成2,这样只有在使用了SQL_CACHE后,才使用查询缓冲。
优先操作 HIGH_PRIORITY
HIGH_PRIORITY可以使用在select和insert操作中,让MYSQL知道,这个操作优先进行。
SELECT HIGH_PRIORITY * FROM TABLE1;
滞后操作 LOW_PRIORITY
LOW_PRIORITY可以使用在insert和update操作中,让mysql知道,这个操作滞后。
update LOW_PRIORITY table1 set field1= where field1= …
延时插入 INSERT DELAYED
INSERT DELAYED INTO table1 set field1= …
INSERT DELAYED INTO,是客户端提交数据给MySQL,MySQL返回OK状态给客户端。而这是并不是已经将数据插入表,而是存储在内存里面等待排队。当mysql有空余时,再插入。另一个重要的好处是,来自许多客户端的插入被集中在一起,并被编写入一个块。这比执行许多独立的插入要快很多。坏处是,不能返回自动递增的ID,以及系统崩溃时,MySQL还没有来得及插入数据的话,这些数据将会丢失。
强制连接顺序 STRAIGHT_JOIN
SELECT TABLE1.FIELD1, TABLE2.FIELD2 FROM TABLE1 STRAIGHT_JOIN TABLE2 WHERE …
由上面的SQL语句可知,通过STRAIGHT_JOIN强迫MySQL按TABLE1、TABLE2的顺序连接表。如果你认为按自己的顺序比MySQL推荐的顺序进行连接的效率高的话,就可以通过STRAIGHT_JOIN来确定连接顺序。
强制使用临时表 SQL_BUFFER_RESULT
SELECT SQL_BUFFER_RESULT * FROM TABLE1 WHERE …
当我们查询的结果集中的数据比较多时,可以通过SQL_BUFFER_RESULT.选项强制将结果集放到临时表中,这样就可以很快地释放MySQL的表锁(这样其它的SQL语句就可以对这些记录进行查询了),并且可以长时间地为客户端提供大记录集。
分组使用临时表 SQL_BIG_RESULT和SQL_SMALL_RESULT
SELECT SQL_BUFFER_RESULT FIELD1, COUNT(*) FROM TABLE1 GROUP BY FIELD1;
一般用于分组或DISTINCT关键字,这个选项通知MySQL,如果有必要,就将查询结果放到临时表中,甚至在临时表中进行排序。SQL_SMALL_RESULT比起SQL_BIG_RESULT差不多,很少使用。
mysql常用的hint
对于经常使用oracle的朋友可能知道,oracle的hint功能种类很多,对于优化sql语句提供了很多方法。同样,在mysql里,也有类似的hint功能。下面介绍一些常用的。
强制索引 FORCE INDEX
SELECT * FROM TABLE1 FORCE INDEX (FIELD1) …
以上的SQL语句只使用建立在FIELD1上的索引,而不使用其它字段上的索引。
忽略索引 IGNORE INDEX
SELECT * FROM TABLE1 IGNORE INDEX (FIELD1, FIELD2) …
在上面的SQL语句中,TABLE1表中FIELD1和FIELD2上的索引不被使用。
关闭查询缓冲 SQL_NO_CACHE
SELECT SQL_NO_CACHE field1, field2 FROM TABLE1;
有一些SQL语句需要实时地查询数据,或者并不经常使用(可能一天就执行一两次),这样就需要把缓冲关了,不管这条SQL语句是否被执行过,服务器都不会在缓冲区中查找,每次都会执行它。
强制查询缓冲 SQL_CACHE
SELECT SQL_CALHE * FROM TABLE1;
如果在my.ini中的query_cache_type设成2,这样只有在使用了SQL_CACHE后,才使用查询缓冲。
优先操作 HIGH_PRIORITY
HIGH_PRIORITY可以使用在select和insert操作中,让MYSQL知道,这个操作优先进行。
SELECT HIGH_PRIORITY * FROM TABLE1;
滞后操作 LOW_PRIORITY
LOW_PRIORITY可以使用在insert和update操作中,让mysql知道,这个操作滞后。
update LOW_PRIORITY table1 set field1= where field1= …
延时插入 INSERT DELAYED
INSERT DELAYED INTO table1 set field1= …
INSERT DELAYED INTO,是客户端提交数据给MySQL,MySQL返回OK状态给客户端。而这是并不是已经将数据插入表,而是存储在内存里面等待排队。当mysql有空余时,再插入。另一个重要的好处是,来自许多客户端的插入被集中在一起,并被编写入一个块。这比执行许多独立的插入要快很多。坏处是,不能返回自动递增的ID,以及系统崩溃时,MySQL还没有来得及插入数据的话,这些数据将会丢失。
强制连接顺序 STRAIGHT_JOIN
SELECT TABLE1.FIELD1, TABLE2.FIELD2 FROM TABLE1 STRAIGHT_JOIN TABLE2 WHERE …
由上面的SQL语句可知,通过STRAIGHT_JOIN强迫MySQL按TABLE1、TABLE2的顺序连接表。如果你认为按自己的顺序比MySQL推荐的顺序进行连接的效率高的话,就可以通过STRAIGHT_JOIN来确定连接顺序。
强制使用临时表 SQL_BUFFER_RESULT
SELECT SQL_BUFFER_RESULT * FROM TABLE1 WHERE …
当我们查询的结果集中的数据比较多时,可以通过SQL_BUFFER_RESULT.选项强制将结果集放到临时表中,这样就可以很快地释放MySQL的表锁(这样其它的SQL语句就可以对这些记录进行查询了),并且可以长时间地为客户端提供大记录集。
分组使用临时表 SQL_BIG_RESULT和SQL_SMALL_RESULT
SELECT SQL_BUFFER_RESULT FIELD1, COUNT(*) FROM TABLE1 GROUP BY FIELD1;
一般用于分组或DISTINCT关键字,这个选项通知MySQL,如果有必要,就将查询结果放到临时表中,甚至在临时表中进行排序。SQL_SMALL_RESULT比起SQL_BIG_RESULT差不多,很少使用。
优化总结:
1.任何情况下SELECT COUNT(*) FROM xxx 是最优选择;
2.尽量减少SELECT COUNT(*) FROM xxx WHERE COL = ‘xxx’ 这种查询;
3.杜绝SELECT COUNT(COL) FROM tablename WHERE COL = ‘xxx’ 的出现。(其中COL非主键)
环境:
MySQL版本:5.0.45
OS:Windows XP SP3
数据表一:sphinx
+———-+——————+——+—–+———+—————-+
| Field | Type | Null | Key | Default | Extra |
+———-+——————+——+—–+———+—————-+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| til | varchar(100) | NO | | | |
| content | text | NO | | | |
| dataline | int(11) | NO | | | |
+———-+——————+——+—–+———+—————-+
记录数:1120100
查询一:
mysql> select count(*) as totalnum from sphinx;
+———-+
| totalnum |
+———-+
| 1120100 |
+———-+
1 row in set (0.00 sec)
查询二:
mysql> select count(*) as totalnum from sphinx where id>1000;
+———-+
| totalnum |
+———-+
| 1119100 |
+———-+
1 row in set (2.17 sec)
查询三:
mysql> select count(*) as totalnum from sphinx where id>1000;
+———-+
| totalnum |
+———-+
| 1119100 |
+———-+
1 row in set (0.61 sec)
查询四:
mysql> select count(*) as totalnum from sphinx where id>1000;
+———-+
| totalnum |
+———-+
| 1119100 |
+———-+
1 row in set (0.61 sec)
查询五:
mysql> select count(id) as totalnum from sphinx;
+———-+
| totalnum |
+———-+
| 1120100 |
+———-+
1 row in set (0.00 sec)
查询六:
mysql> select count(til) as totalnum from sphinx where id>1000;
+———-+
| totalnum |
+———-+
| 1119100 |
+———-+
1 row in set (1 min 38.61 sec)
查询七:
mysql> select count(id) as totalnum from sphinx where id>11000;
+———-+
| totalnum |
+———-+
| 1109100 |
+———-+
1 row in set (0.61 sec)
查询八:
mysql> select count(id) as totalnum from sphinx;
+———-+
| totalnum |
+———-+
| 1120100 |
+———-+
1 row in set (0.03 sec)
结论:
在 select count() 没有 where 条件的时候 select count(*) 和 select count(col) 所消耗的查询时间相差无几。
在 select count() 有 where 条件的时候 select count(col) 所消耗的查询时间 比 select count(*) 明显多出数量级的时间。
1.任何情况下SELECT COUNT(*) FROM xxx 是最优选择;
2.尽量减少SELECT COUNT(*) FROM xxx WHERE COL = ‘xxx’ 这种查询;
3.杜绝SELECT COUNT(COL) FROM tablename WHERE COL = ‘xxx’ 的出现。(其中COL非主键)
环境:
MySQL版本:5.0.45
OS:Windows XP SP3
数据表一:sphinx
+———-+——————+——+—–+———+—————-+
| Field | Type | Null | Key | Default | Extra |
+———-+——————+——+—–+———+—————-+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| til | varchar(100) | NO | | | |
| content | text | NO | | | |
| dataline | int(11) | NO | | | |
+———-+——————+——+—–+———+—————-+
记录数:1120100
查询一:
mysql> select count(*) as totalnum from sphinx;
+———-+
| totalnum |
+———-+
| 1120100 |
+———-+
1 row in set (0.00 sec)
查询二:
mysql> select count(*) as totalnum from sphinx where id>1000;
+———-+
| totalnum |
+———-+
| 1119100 |
+———-+
1 row in set (2.17 sec)
查询三:
mysql> select count(*) as totalnum from sphinx where id>1000;
+———-+
| totalnum |
+———-+
| 1119100 |
+———-+
1 row in set (0.61 sec)
查询四:
mysql> select count(*) as totalnum from sphinx where id>1000;
+———-+
| totalnum |
+———-+
| 1119100 |
+———-+
1 row in set (0.61 sec)
查询五:
mysql> select count(id) as totalnum from sphinx;
+———-+
| totalnum |
+———-+
| 1120100 |
+———-+
1 row in set (0.00 sec)
查询六:
mysql> select count(til) as totalnum from sphinx where id>1000;
+———-+
| totalnum |
+———-+
| 1119100 |
+———-+
1 row in set (1 min 38.61 sec)
查询七:
mysql> select count(id) as totalnum from sphinx where id>11000;
+———-+
| totalnum |
+———-+
| 1109100 |
+———-+
1 row in set (0.61 sec)
查询八:
mysql> select count(id) as totalnum from sphinx;
+———-+
| totalnum |
+———-+
| 1120100 |
+———-+
1 row in set (0.03 sec)
结论:
在 select count() 没有 where 条件的时候 select count(*) 和 select count(col) 所消耗的查询时间相差无几。
在 select count() 有 where 条件的时候 select count(col) 所消耗的查询时间 比 select count(*) 明显多出数量级的时间。
flash-php的RPC方案amfphp
时间:2009-03-04 17:13:18 来源:http://ilovelate.blog.163.com/blog/static/601420091212142713 作者:
flash里自己有个二进制的数据传输协议Amf, 幸好php里有个amfphp,那就很方便了, 协议会自动转换php和flash的数据类型,本来都是动态语言,啥都好办。
网址:http://www.amfphp.org/ 官方的,目前最新版本1.9 beta2
下载地址: http://sourceforge.net/project/showfiles.php?group_id=72483
另外要安装个可以提升性能的东东:http://www.teslacore.it/wiki/index.php?title=AMFEXT 目前版本0.8.7
将amfphp放到php项目目录下就可以使用了。
注意将Remote Service php 放到 amfphp\services 目录里面就可以使用了,很简单。
贴个session的例子:
flash端:
php端:
<?php
// Wade Arnold: 1/6/2008
// Example is designed to show how to use PHP sessions.
class Counter {
public function __construct() {
// Check if the session is available or create it.
if (!isset($_SESSION['count'])) {
$_SESSION['count'] = 0;
}
}
// Used to increment the session variable count.
public function increment() {
$_SESSION['count']++;
return $_SESSION['count'];
}
// used to destroy the session variable and start over.
public function unregister() {
unset($_SESSION['count']);
return true;
}
// remove the entire session from the server.
public function destroy() {
session_destroy();
return true;
}
}
?>
YY两句,看到热血三国的通讯方式竟然是amfphp的RPC通讯方式的, 这样的话编程模型跟普通的web项目开发就一模一样了, 现在只是说使用了flash界面而已,开发难度大幅度降低。
想想也是, 策略类的游戏,没必要使用客户端游戏的那种socket编程模型,开发起来复杂多了。 可能游戏玩法不一样引起的。如果是有实时战斗的玩法,那必须得socket编程了吧。
时间:2009-03-04 17:13:18 来源:http://ilovelate.blog.163.com/blog/static/601420091212142713 作者:
flash里自己有个二进制的数据传输协议Amf, 幸好php里有个amfphp,那就很方便了, 协议会自动转换php和flash的数据类型,本来都是动态语言,啥都好办。
网址:http://www.amfphp.org/ 官方的,目前最新版本1.9 beta2
下载地址: http://sourceforge.net/project/showfiles.php?group_id=72483
另外要安装个可以提升性能的东东:http://www.teslacore.it/wiki/index.php?title=AMFEXT 目前版本0.8.7
将amfphp放到php项目目录下就可以使用了。
注意将Remote Service php 放到 amfphp\services 目录里面就可以使用了,很简单。
贴个session的例子:
flash端:
// Wade Arnold: 1/6/2008
// Example is designed to show how to use PHP sessions.
package {
// required for flash file and output display
import flash.display.MovieClip;
import fl.events.*;
import flash.events.*;
// required to send/recieve data over AMF
import flash.net.NetConnection;
import flash.net.Responder;
// Flash CS3 Document Class.
public class Counter extends MovieClip {
private var gateway:String = "http://localhost/server/amfphp/gateway.php";
private var connection:NetConnection;
private var responder:Responder;
public function Counter() {
trace("AMFPHP Session Counter Example");
// Event listner for buttons
increment_btn.addEventListener(MouseEvent.CLICK, incrementCounter);
reset_btn.addEventListener(MouseEvent.CLICK, resetCounter);
destroy_btn.addEventListener(MouseEvent.CLICK, destroySession);
// Responder to handle data returned from AMFPHP.
responder = new Responder(onResult, onFault);
connection = new NetConnection;
// Gateway.php url for NetConnection
connection.connect(gateway);
}
// Method run when the "Increment Counter" button is clicked.
public function incrementCounter(e:MouseEvent):void {
// Send the data to the remote server.
connection.call("Counter.increment", responder);
}
// Method run when the "Rest Counter" button is clicked.
public function resetCounter(e:MouseEvent):void {
// Send the data to the remote server.
connection.call("Counter.unregister", responder);
}
// Method run when the "Destroy Session" button is clicked.
public function destroySession(e:MouseEvent):void {
// Send the data to the remote server.
connection.call("Counter.destroy", responder);
}
// Handle a successful AMF call. This method is defined by the responder.
private function onResult(result:Object):void {
response_txt.text = String(result);
}
// Handle an unsuccessfull AMF call. This is method is dedined by the responder.
private function onFault(fault:Object):void {
response_txt.text = String(fault.description);
}
}
}
// Example is designed to show how to use PHP sessions.
package {
// required for flash file and output display
import flash.display.MovieClip;
import fl.events.*;
import flash.events.*;
// required to send/recieve data over AMF
import flash.net.NetConnection;
import flash.net.Responder;
// Flash CS3 Document Class.
public class Counter extends MovieClip {
private var gateway:String = "http://localhost/server/amfphp/gateway.php";
private var connection:NetConnection;
private var responder:Responder;
public function Counter() {
trace("AMFPHP Session Counter Example");
// Event listner for buttons
increment_btn.addEventListener(MouseEvent.CLICK, incrementCounter);
reset_btn.addEventListener(MouseEvent.CLICK, resetCounter);
destroy_btn.addEventListener(MouseEvent.CLICK, destroySession);
// Responder to handle data returned from AMFPHP.
responder = new Responder(onResult, onFault);
connection = new NetConnection;
// Gateway.php url for NetConnection
connection.connect(gateway);
}
// Method run when the "Increment Counter" button is clicked.
public function incrementCounter(e:MouseEvent):void {
// Send the data to the remote server.
connection.call("Counter.increment", responder);
}
// Method run when the "Rest Counter" button is clicked.
public function resetCounter(e:MouseEvent):void {
// Send the data to the remote server.
connection.call("Counter.unregister", responder);
}
// Method run when the "Destroy Session" button is clicked.
public function destroySession(e:MouseEvent):void {
// Send the data to the remote server.
connection.call("Counter.destroy", responder);
}
// Handle a successful AMF call. This method is defined by the responder.
private function onResult(result:Object):void {
response_txt.text = String(result);
}
// Handle an unsuccessfull AMF call. This is method is dedined by the responder.
private function onFault(fault:Object):void {
response_txt.text = String(fault.description);
}
}
}
php端:
<?php
// Wade Arnold: 1/6/2008
// Example is designed to show how to use PHP sessions.
class Counter {
public function __construct() {
// Check if the session is available or create it.
if (!isset($_SESSION['count'])) {
$_SESSION['count'] = 0;
}
}
// Used to increment the session variable count.
public function increment() {
$_SESSION['count']++;
return $_SESSION['count'];
}
// used to destroy the session variable and start over.
public function unregister() {
unset($_SESSION['count']);
return true;
}
// remove the entire session from the server.
public function destroy() {
session_destroy();
return true;
}
}
?>
YY两句,看到热血三国的通讯方式竟然是amfphp的RPC通讯方式的, 这样的话编程模型跟普通的web项目开发就一模一样了, 现在只是说使用了flash界面而已,开发难度大幅度降低。
想想也是, 策略类的游戏,没必要使用客户端游戏的那种socket编程模型,开发起来复杂多了。 可能游戏玩法不一样引起的。如果是有实时战斗的玩法,那必须得socket编程了吧。
最近在工作中常常用ssh登录某个节点的服务器解析域名的时间非常长:
#time ssh
real 0m19.928s
user 0m0.008s
sys 0m0.001s
可以看到登录这个点服务器需要近20秒的时间;
然后用nslookup检查了这个域名ip地址:
报了;; connection timed out; no servers could be reached的错误;
用ping是可以解析,不过解析时间也很长;
这个问题的原因,目前还不是很清楚,怀疑是否是新启用的ip地址可能存在这方面的情况;
ssh登录缓慢的问题可以通过修改sshd_config文件中的GSSAPI options
# GSSAPI options
#GSSAPIAuthentication no
#GSSAPIAuthentication yes
#GSSAPICleanupCredentials yes
全部注释掉,然后重启sshd服务;
再测试登录
#time ssh
real 0m2.229s
user 0m0.006s
sys 0m0.002s
恢复正常!
#time ssh
real 0m19.928s
user 0m0.008s
sys 0m0.001s
可以看到登录这个点服务器需要近20秒的时间;
然后用nslookup检查了这个域名ip地址:
报了;; connection timed out; no servers could be reached的错误;
用ping是可以解析,不过解析时间也很长;
这个问题的原因,目前还不是很清楚,怀疑是否是新启用的ip地址可能存在这方面的情况;
ssh登录缓慢的问题可以通过修改sshd_config文件中的GSSAPI options
# GSSAPI options
#GSSAPIAuthentication no
#GSSAPIAuthentication yes
#GSSAPICleanupCredentials yes
全部注释掉,然后重启sshd服务;
再测试登录
#time ssh
real 0m2.229s
user 0m0.006s
sys 0m0.002s
恢复正常!
# prefork MPM
# StartServers: number of server processes to start
# MinSpareServers: minimum number of server processes which are kept spare
# MaxSpareServers: maximum number of server processes which are kept spare
# MaxClients: maximum number of server processes allowed to start
# MaxRequestsPerChild: maximum number of requests a server process serves
StartServers 10
MinSpareServers 10
MaxSpareServers 15
ServerLimit 2000
MaxClients 2000
MaxRequestsPerChild 10000
查看httpd进程数(即prefork模式下Apache能够处理的并发请求数):
Linux命令:
ps -ef | grep httpd | wc -l
返回结果示例:
1388
表示Apache能够处理1388个并发请求,这个值Apache可根据负载情况自动调整,我这组服务器中每台的峰值曾达到过2002。
查看Apache的并发请求数及其TCP连接状态:
Linux命令:
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’
(这条语句是从新浪互动社区事业部技术总监王老大那儿获得的,非常不错)
返回结果示例:
LAST_ACK 5
SYN_RECV 30
ESTABLISHED 1597
FIN_WAIT1 51
FIN_WAIT2 504
TIME_WAIT 1057
其中的SYN_RECV表示正在等待处理的请求数;ESTABLISHED表示正常数据传输状态;TIME_WAIT表示处理完毕,等待超时结束的请求数。
关于TCP状态的变迁,可以从下图形象地看出:
状态:描述
CLOSED:无连接是活动的或正在进行
LISTEN:服务器在等待进入呼叫
SYN_RECV:一个连接请求已经到达,等待确认
SYN_SENT:应用已经开始,打开一个连接
ESTABLISHED:正常数据传输状态
FIN_WAIT1:应用说它已经完成
FIN_WAIT2:另一边已同意释放
ITMED_WAIT:等待所有分组死掉
CLOSING:两边同时尝试关闭
TIME_WAIT:另一边已初始化一个释放
LAST_ACK:等待所有分组死掉
# StartServers: number of server processes to start
# MinSpareServers: minimum number of server processes which are kept spare
# MaxSpareServers: maximum number of server processes which are kept spare
# MaxClients: maximum number of server processes allowed to start
# MaxRequestsPerChild: maximum number of requests a server process serves
StartServers 10
MinSpareServers 10
MaxSpareServers 15
ServerLimit 2000
MaxClients 2000
MaxRequestsPerChild 10000
查看httpd进程数(即prefork模式下Apache能够处理的并发请求数):
Linux命令:
ps -ef | grep httpd | wc -l
返回结果示例:
1388
表示Apache能够处理1388个并发请求,这个值Apache可根据负载情况自动调整,我这组服务器中每台的峰值曾达到过2002。
查看Apache的并发请求数及其TCP连接状态:
Linux命令:
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’
(这条语句是从新浪互动社区事业部技术总监王老大那儿获得的,非常不错)
返回结果示例:
LAST_ACK 5
SYN_RECV 30
ESTABLISHED 1597
FIN_WAIT1 51
FIN_WAIT2 504
TIME_WAIT 1057
其中的SYN_RECV表示正在等待处理的请求数;ESTABLISHED表示正常数据传输状态;TIME_WAIT表示处理完毕,等待超时结束的请求数。
关于TCP状态的变迁,可以从下图形象地看出:
状态:描述
CLOSED:无连接是活动的或正在进行
LISTEN:服务器在等待进入呼叫
SYN_RECV:一个连接请求已经到达,等待确认
SYN_SENT:应用已经开始,打开一个连接
ESTABLISHED:正常数据传输状态
FIN_WAIT1:应用说它已经完成
FIN_WAIT2:另一边已同意释放
ITMED_WAIT:等待所有分组死掉
CLOSING:两边同时尝试关闭
TIME_WAIT:另一边已初始化一个释放
LAST_ACK:等待所有分组死掉
中文空格这里面有好几种:没有简单的解决问题的方式,比如半角全角空格,比如段落符都会显示为空白的,然后让人们误解它是空格,所以去掉空格之前一定要先确认自己的是不是空格,我下面分析一下这两种的解决办法。
(1)/[\s| ]+/这个就可以,注意|后面的跟的是全角空格
(2)mb中的正则替换也可以试试
另外注意使用Unicode的时候,加上正则表达式描述符u
但由于汉语中显示为空格模样的有很多个,比如说我下面碰到的这么一个,它的unicode编码为C2A0,使用下面的表达式就能解决问题。
这里有个细节: \xc2a0本来是一个Unicode字符,但是不能匹配多个,只能替换一个,【这里可能是因为字节的原因】后来突然找到这么一种写法,解决了问题,另外为了避免,中文空格和英文空格相混杂的情形,后面又加了个\s来清除。
其实在Unicode上的正则表达式确实不怎么好用。
阅读全文
(1)/[\s| ]+/这个就可以,注意|后面的跟的是全角空格
(2)mb中的正则替换也可以试试
另外注意使用Unicode的时候,加上正则表达式描述符u
但由于汉语中显示为空格模样的有很多个,比如说我下面碰到的这么一个,它的unicode编码为C2A0,使用下面的表达式就能解决问题。
$new['content'] = preg_replace('/^[(\xc2\xa0)|\s]+/', '', $new['content']);
这里有个细节: \xc2a0本来是一个Unicode字符,但是不能匹配多个,只能替换一个,【这里可能是因为字节的原因】后来突然找到这么一种写法,解决了问题,另外为了避免,中文空格和英文空格相混杂的情形,后面又加了个\s来清除。
其实在Unicode上的正则表达式确实不怎么好用。

mysql> select distinct(UserNumber),Score_TT from Tbl_Score where FYear = "2009" and FWeek = '53' order by Score_TT desc limit 0,10;
+-----------+--------+
| UserNumber | Score_TT |
+-----------+--------+
| 70213303* | 1800 |
| 78400557* | 1450 |
| 45598211* | 1410 |
| 7575629* | 1200 |
| 30394912* | 1148 |
| 98649685* | 1050 |
| 6653828* | 850 |
| 38200298* | 800 |
| 80496539* | 800 |
| 18064165* | 670 |
+-----------+--------+
10 rows in set (0.37 sec)
mysql> select count(distinct UserNumber),UserNumber,Score_TT from Tbl_Score where FYear = "2009" and FWeek = '53' group by UserNumber order by Score_TT desc limit 0,10;
+---------------------+-----------+--------+
| count(distinct UserNumber) | UserNumber | Score_TT |
+---------------------+-----------+--------+
| 1 | 70213303* | 1800 |
| 1 | 78400557* | 1450 |
| 1 | 45598211* | 1410 |
| 1 | 7575629* | 1200 |
| 1 | 30394912* | 1148 |
| 1 | 98649685* | 1050 |
| 1 | 6653828* | 850 |
| 1 | 80496539* | 800 |
| 1 | 38200298* | 800 |
| 1 | 18064165* | 670 |
+---------------------+-----------+--------+
来源:
在使用 mysql时,有时需要查询出某个字段不重复的记录,虽然mysql提供有distinct这个关键字来过滤掉多余的重复记录只保留一条,但往往只用它来返回不重复记录的条数,而不是用它来返回不重记录的所有值。其原因是distinct只能返回它的目标字段,而无法返回其它字段,这个问题让我困扰了很久,用distinct不能解决的话,我只有用二重循环查询来解决,而这样对于一个数据量非常大的站来说,无疑是会直接影响到效率的。所以我花了很多时间来研究这个问题,网上也查不到解决方案,期间把朋友拉来帮忙,结果是我们两人都郁闷了!
下面先来看看例子:
table
id name
1 a
2 b
3 c
4 c
5 b
库结构大概这样,这只是一个简单的例子,实际情况会复杂得多。
比如我想用一条语句查询得到name不重复的所有数据,那就必须使用distinct去掉多余的重复记录。
select distinct name from table
得到的结果是:
name
a
b
c
好像达到效果了,可是,我想要得到的是id值呢?改一下查询语句吧:
select distinct name, id from table
结果会是:
id name
1 a
2 b
3 c
4 c
5 b
distinct怎么没起作用?作用是起了的,不过他同时作用了两个字段,也就是必须得id与name都相同的才会被排除……
我们再改改查询语句:
select id, distinct name from table
很遗憾,除了错误信息你什么也得不到,distinct必须放在开头。难到不能把distinct放到where条件里?能,照样报错。。。。。。。
很麻烦吧?确实,费尽心思都没能解决这个问题。没办法,继续找人问。
拉住公司里一JAVA程序员,他给我演示了oracle里使用distinct之后,也没找到mysql里的解决方案,最后下班之前他建议我试试group by。
试了半天,也不行,最后在mysql手册里找到一个用法,用group_concat(distinct name)配合group by name实现了我所需要的功能,兴奋,天佑我也,赶快试试。
报错。。。。。。。。。。。。郁闷。。。。。。。连mysql手册也跟我过不去,先给了我希望,然后又把我推向失望,好狠哪。。。。
再仔细一查,group_concat函数是4.1支持,晕,我4.0的。没办法,升级,升完级一试,成功。。。。。。
终于搞定了,不过这样一来,又必须要求客户也升级了。
突然灵机一闪,既然可以使用group_concat函数,那其它函数能行吗?
赶紧用count函数一试,成功,我。。。。。。。想哭啊,费了这么多工夫。。。。。。。。原来就这么简单……
现在将完整语句放出:
select *, count(distinct name) from table group by name
结果:
id name count(distinct name)
1 a 1
2 b 1
3 c 1
最后一项是多余的,不用管就行了,目的达到。
唉,原来mysql这么笨,轻轻一下就把他骗过去了,郁闷也就我吧(对了,还有容容那家伙),现在拿出来希望大家不要被这问题折腾。
哦,对,再顺便说一句,group by 必须放在 order by 和 limit之前,不然会报错.
更郁闷的事情发生了,在准备提交时容容发现,有更简单的解决方法:
select id, name from table group by name
看来对mysql的了解还是太肤浅了,不怕被笑话,发出来让大家别犯同样的错误。
# echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
如果要恢复,只要:
# echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_all
即可,
挺方便,不要去专门使用ipchains或者iptables了。
或者以root进入Linux系统,然后编辑文件icmp_echo_ignore_all
# vi /proc/sys/net/ipv4/icmp_echo_ignore_all
将其值改为1后为禁止PING
将其值改为0后为解除禁止PING
[root@redhat7 root]# echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
如果要恢复,只要:
[root@redhat7 root]# echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_all
即可,挺方便,不要去专门使用ipchains或者iptables了。
在/etc/rc.d/rc.local文件里添加一行命令:echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
这样每次启动系统后都将自动运行,可以阻止你的系统响应任何从外部/内部来的ping请求,
就没有人能ping通你的机器并收到响应,可以大大增强站点和系统的安全性。
如果要恢复,只要:
# echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_all
即可,
挺方便,不要去专门使用ipchains或者iptables了。
或者以root进入Linux系统,然后编辑文件icmp_echo_ignore_all
# vi /proc/sys/net/ipv4/icmp_echo_ignore_all
将其值改为1后为禁止PING
将其值改为0后为解除禁止PING
[root@redhat7 root]# echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
如果要恢复,只要:
[root@redhat7 root]# echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_all
即可,挺方便,不要去专门使用ipchains或者iptables了。
在/etc/rc.d/rc.local文件里添加一行命令:echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
这样每次启动系统后都将自动运行,可以阻止你的系统响应任何从外部/内部来的ping请求,
就没有人能ping通你的机器并收到响应,可以大大增强站点和系统的安全性。
SELECT mail
FROM `t_import_mail`
WHERE 1
GROUP BY mail
HAVING count( 0 ) >1
LIMIT 0 , 30
select FQQ,FUserID,count(*) as ct from `Tbl_User_**` GROUP BY FQQ HAVING ct > 1;
//查出表某个字段产生重复值的所有记录
可以用group by 来查出不重复的记录,但是我想查出重复的记录该如何写sql语句呢?
先看这个,
select uid,email,count(*) as ct from `edm_user081217` GROUP BY email
然后看这个,就容易理解了
select uid,email,count(*) as ct from `edm_user081217` GROUP BY email HAVING ct > 1
以下是having和where的区别:
SELECT city FROM weather WHERE temp_lo = (SELECT max(temp_lo) FROM weather);
作用的对象不同。WHERE 子句作用于表和视图,HAVING 子句作用于组。
WHERE 在分组和聚集计算之前选取输入行(因此,它控制哪些行进入聚集计算), 而 HAVING 在分组和聚集之后选取分组的行。因此,WHERE 子句不能包含聚集函数; 因为试图用聚集函数判断那些行输入给聚集运算是没有意义的。相反,HAVING 子句总是包含聚集函数。(严格说来,你可以写不使用聚集的 HAVING 子句,但这样做只是白费劲。同样的条件可以更有效地用于 WHERE 阶段。)
在前面的例子里,我们可以在 WHERE 里应用城市名称限制,因为它不需要聚集。 这样比在 HAVING 里增加限制更加高效,因为我们避免了为那些未通过 WHERE 检查的行进行分组和聚集计算
综上所述:
having一般跟在group by之后,执行记录组选择的一部分来工作的。
where则是执行所有数据来工作的。
再者having可以用聚合函数,如having sum(qty)>1000
PRIMARY, INDEX, UNIQUE 这3种是一类
PRIMARY 主键。 就是 唯一 且 不能为空。
INDEX 索引,普通的
UNIQUE 唯一索引。 不允许有重复。
FULLTEXT 是全文索引,用于在一篇文章中,检索文本信息的。
举个例子来说,比如你在为某商场做一个会员卡的系统。
这个系统有一个会员表
有下列字段:
会员编号 INT
会员姓名 VARCHAR(10)
会员身份证号码 VARCHAR(18)
会员电话 VARCHAR(10)
会员住址 VARCHAR(50)
会员备注信息 TEXT
那么这个 会员编号,作为主键,使用 PRIMARY
会员姓名 如果要建索引的话,那么就是普通的 INDEX
会员身份证号码 如果要建索引的话,那么可以选择 UNIQUE (唯一的,不允许重复)
会员备注信息 , 如果需要建索引的话,可以选择 FULLTEXT,全文搜索。
不过 FULLTEXT 用于搜索很长一篇文章的时候,效果最好。
用在比较短的文本,如果就一两行字的,普通的 INDEX 也可以。
阅读全文
PRIMARY 主键。 就是 唯一 且 不能为空。
INDEX 索引,普通的
UNIQUE 唯一索引。 不允许有重复。
FULLTEXT 是全文索引,用于在一篇文章中,检索文本信息的。
举个例子来说,比如你在为某商场做一个会员卡的系统。
这个系统有一个会员表
有下列字段:
会员编号 INT
会员姓名 VARCHAR(10)
会员身份证号码 VARCHAR(18)
会员电话 VARCHAR(10)
会员住址 VARCHAR(50)
会员备注信息 TEXT
那么这个 会员编号,作为主键,使用 PRIMARY
会员姓名 如果要建索引的话,那么就是普通的 INDEX
会员身份证号码 如果要建索引的话,那么可以选择 UNIQUE (唯一的,不允许重复)
会员备注信息 , 如果需要建索引的话,可以选择 FULLTEXT,全文搜索。
不过 FULLTEXT 用于搜索很长一篇文章的时候,效果最好。
用在比较短的文本,如果就一两行字的,普通的 INDEX 也可以。
