协会的域名一直只有一个子域名,不用好好利用就太浪费了。
现向本协会会员免费提供yimatech.org的子域名一个(仅提供域名,不提供空间),在本日志回复中进行申请,申请格式:
子域名:xxx.yimatech.org
指向IP或转发地址:xxx.xxx.xxx.xxx
PS: 请不要申请与自己无关的子域名。
协会的域名一直只有一个子域名,不用好好利用就太浪费了。
现向本协会会员免费提供yimatech.org的子域名一个(仅提供域名,不提供空间),在本日志回复中进行申请,申请格式:
子域名:xxx.yimatech.org
指向IP或转发地址:xxx.xxx.xxx.xxx
PS: 请不要申请与自己无关的子域名。
以堆栈溢出为代表的缓冲区溢出已成为最为普遍的安全漏洞。由此引发的安全问题比比皆是。早在 1988 年,美国康奈尔大学的计算机科学系研究生莫里斯 (Morris) 利用 UNIX fingered 程序的溢出漏洞,写了一段恶意程序并传播到其他机器上,结果造成 6000 台 Internet 上的服务器瘫痪,占当时总数的 10%。各种操作系统上出现的溢出漏洞也数不胜数。为了尽可能避免缓冲区溢出漏洞被攻击者利用,现今的编译器设计者已经开始在编译器层面上对堆栈进行保护。现在已经有了好几种编译器堆栈保护的实现,其中最著名的是 StackGuard 和 Stack-smashing Protection (SSP,又名 ProPolice)。
编译器堆栈保护原理
我们知道攻击者利用堆栈溢出漏洞时,通常会破坏当前的函数栈。例如,攻击者利用清单 1 中的函数的堆栈溢出漏洞时,典型的情况是攻击者会试图让程序往 name 数组中写超过数组长度的数据,直到函数栈中的返回地址被覆盖,使该函数返回时跳转至攻击者注入的恶意代码或 shellcode 处执行(关于溢出攻击的原理参见《Linux 下缓冲区溢出攻击的原理及对策》)。溢出攻击后,函数栈变成了图 2 所示的情形,与溢出前(图 1)比较可以看出原本堆栈中的 EBP,返回地址已经被溢出字符串覆盖,即函数栈已经被破坏。
清单 1. 可能存在溢出漏洞的代码
|
int vulFunc() { char name[10]; //… return 0; } |
图 2. 溢出后的函数栈
当我们登录QQ邮箱、Gmail、支付宝等网站时,我们不难发现,这些网站启用的是https协议。
什么是HTTPS协议
HTTPS(全称:Hypertext Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。 它是一个URI scheme(抽象标识符体系),句法类同http:体系。用于安全的HTTP数据传输。https:URL表明它使用了HTTP,但HTTPS存在不同于HTTP的默认端口及一个加密/身份验证层(在HTTP与TCP之间)。这个系统的最初研发由网景公司进行,提供了身份验证与加密通讯方法,现在它被广泛用于万维网上安全敏感的通讯,例如交易支付方面。
要想让自己的网站也用https协议,需要两个条件。第一是Web Server具有ssl模块。第二是我们必须要有一个证书。
Web Server的SSL模块就不谈了,这里谈谈怎么用你自己的机器生成一套https证书。
Linux下制作HTTPS证书
首先要安装OpenSSL。
Debian系列的Linux可以通过“apt-get install openssl”安装OpenSSL
RedHat系列的Linux可以通过“yum install openssl”安装它。 装好之后,我们就开始做证书了。 首先用这个命令生成一个key:
openssl genrsa -des3 -out server.key 1024
这个key的文件名是server.key当然,你可以改成其他的名字
输入这个命令之后会提示“Enter pass phrase for server.key”这时候你得输入一个字符串,这个字符串就是key文件的密码,你得牢记。
接下来就是生成csr文件.csr文件主要是包含证书的信息,比如机构。
openssl req -new -key server.key -out server.csr
执行这一步首先就会要你输入key文件的密码。输入完毕之后接下来会输入一系列的信息。城市、机构、名称、当然还有域名。最后还会要你输入一个密码。
最后一个步骤就是生成证书啦!
openssl x509 -req -days 1000 -in server.csr -signkey server.key -out server.cert
这一步比较简单,只要输入生成key文件的密码就行了。。。
好了,证书做好了,把证书放到对应的位置,修改一下配置Apache的文件。重启Apache就可以了。重启的时候要输入创建key文件的密码哦。
存在的问题
这样一来你的网站就可以使用https协议了。不过不要高兴得太早,当你以https协议访问你的网站的时候,浏览器会报错。报错的大致内容就是说你这个证书是不可信任的。为什么不可信任呢?你想想看,假如你的学士学位证不是教育机构办法的,而是你自己颁发给你自己的。那么你的学位证可信吗?当然不可信。同样,你的https证书是你自己做的,也不可信。
要想获得可信的https证书唯一的办法就是到专门提供ssl证书的机构去申请一个。话说申请证书证书,通常是要收费的,而且价格不菲。不过StartSSL提供免费的证书。但是在StartSSL上申请这个证书步骤比较多。如果你只是想和我一样研究https协议以及Web Server服务器。那么就自己做一个算了吧!
例1 宏定义如下 #define CPSR_XXX(fields) CPSR ## fields #define SPSR_XXX(fields) SPSR ## fields #define all_fields _fsxc #define CPSR_XXXX(f) CPSR_XXX(f) #define SPSR_XXXX(f) SPSR_XXX(f) 代码中的CPSR_XXXX(all_fields)展开过程 1 CPSR_XXXX(all_fields) 2 CPSR_XXX(all_fields) -> CPSR_XXX(_fsxc) 3 CPSR ## _fsxc 4 CPSR_fsxc 代码中的CPSR_XXX(all_fields)展开过程 1 CPSR_XXX(all_fields) 2 CPSR ## all_fields 3 CPSRall_fields 例2 宏定义 #include <stdint.h> #define STR_(x) #x #define STR(x) STR_(x) 代码STR(INT_MAX)展开过程 1 STR(INT_MAX) 2 STR_(INT_MAX) -> STR_(0x7FFFFFFF) 3 #0x7FFFFFFF 4 "0x7FFFFFFF" 代码STR_(INT_MAX)展开过程 1 STR_(INT_MAX) 2 #INT_MAX 3 "INT_MAX"
原理:
碰到#define就记录下宏名和宏值
在代码行里碰到标识符是宏的就展开,一直展开到没有宏为止
但是展开过程中,如果展开的串里面有#和##这些宏运算符,就比较特别了,
在#之后的和##两边的标识符直接参与宏计算,不再展开
1. 获取dom4j包,DOM4J下载(SourceForge): http://sourceforge.net/projects/dom4j。
2. 解压后有很多个包,常用的只有两个,如果仅操作XML文档的话把dom4j-1.6.1.jar加入工程就可以了,如果需要使用XPath的话还需要加入包jaxen-1.1-beta-7.jar.
DOM实例的应用
1. Parsing XML(读写XML文档)主要依赖于org.dom4j.io包,其中提供了DOMReader和SAXReader两种不同的方式,但是使用是一样的。
//读取文件资源,已文件名为参数。
public Document read(String fileName) throws MalformedURLException, DocumentException {
SAXReader reader = new SAXReader();
Document document = reader.read(new File(fileName));
return document;
}
//读取URL资源
public Document read(Url url) throws DocumentException {
SAXReader reader = new SAXReader();
Document document = reader.read(url);
return document;
}
2. 读取xml形式的文本
String text = " <person> <name>James</name> </person> "; Document document = DocumentHelper.parseText(text);
3. 创建xml文档
/*
<root>
<author name=”James” location=”UK”> James Strachan </author>
<author name=” Bob” location=” US”> Bob McWhirter</author>
</root>
*/
public Document createDocument() {
Document document = DocumentHelper.createDocument();
Element root = document.addElement( "root" );
Element author1 = root.addElement( "author" )
.addAttribute( "name", "James" )
.addAttribute( "location", "UK" )
.addText( "James Strachan" );
Element author2 = root.addElement( "author" )
.addAttribute( "name", "Bob" )
.addAttribute( "location", "US" )
.addText( "Bob McWhirter" );
return document;
}
4. 把xml写入文件(writing a document to a file)
FileWriter out = new FileWriter( "foo.xml" );
document.write(out);
//文档中全为英文,不设置编码,直接写入的形式.
XMLWriter writer = new XMLWriter(new FileWriter("output.xml"));
writer.write(document);
writer.close();
//文档中含有中文,设置编码格式写入的形式.
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("GBK"); // 指定XML编码
XMLWriter writer = new XMLWriter(new FileWriter("output.xml"),format);
writer.write(document);
writer.close();
5. 把document 输出为字符串
String text = document.asXML();
6. 获取文档的根结点
Element root = document.getRootElement();
7. 取得某节点的单个子节点.
Element element = root.element(“node_name”); //node_name是节点名
8. 遍历节点
public void bar(Document document) throws DocumentException {
Element root = document.getRootElement();
// iterate through child elements of root
for ( Iterator i = root.elementIterator(); i.hasNext(); ) {
Element element = (Element) i.next();
// do something
}
// iterate through child elements of root with element name "foo"
for ( Iterator i = root.elementIterator( "foo" ); i.hasNext(); ) {
Element foo = (Element) i.next();
// do something
}
// iterate through attributes of root
for ( Iterator i = root.attributeIterator(); i.hasNext(); ) {
Attribute attribute = (Attribute) i.next();
// do something
}
}
9. 在某节点下添加子节点
Element author = root.addElement("author");
Element books = author.addElement(“books”);
Element book1 = books.addElement(“book”).setText(“The First Design Pattern”);
10. 设置节点文字
root.setText(“This is root”); // <root>This is root</root>
11. 删除某节点
root.remove(child); //child是被删除的节点
12. 获取某个节点的属性
Attribute attribute = root.attribute(“name”); // <root name=”Tony.Q”></root>
13. 取得属性的文字
attribute.getText() // Tony.Q
root.attributeValue("name"); //Tony.Q
14. 设置某节点的属性和文字.
root.addAttribute(“url”,”blog.qustx.com”); // <root url=” blog.qustx.com”></root>
15 .修改某节点的属性的文字
root.attribute(“url”).setText(“blog.yimatech.org”); // <root url=”blog.yimatech.org”></root>
16. 删除某属性
Attribute attribute=root.attribute("url"); //<root url=” blog.qustx.com”></root>
root.remove(attribute); // <root></root>
实例应用:
1. 通过提供的数据。
2. 创建对应的XML文档。
public class Book {
private String name;
private String author;
private Double cost;
public Book()
{
super();
}
public Book(String name, String author, Double cost) {
super();
this.name = name;
this.author = author;
this.cost = cost;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Double getCost() {
return cost;
}
public void setCost(Double cost) {
this.cost = cost;
}
}
public class Main {
private static List list = new ArrayList();
static {
list.add(new Book("海底捞你学不会","黄铁鹰",Double.valueOf(24.20)));
list.add(new Book("卓有成效的管理者","德鲁克",Double.valueOf(19.50)));
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Document doc = DocumentHelper.createDocument();
Element root = doc.addElement("books");
for(int i=0;i<list.size();i++)
{
Book book = (Book)list.get(i);
root.addElement("book").addAttribute("author",book.getAuthor())
.addAttribute("cost", book.getCost().toString())
.setText(book.getName());
}
System.out.print(doc.asXML());
}
}
结果:
<?xml version="1.0" encoding="UTF-8"?> <books> <book author="黄铁鹰" cost="24.2">海底捞你学不会</book> <book author="德鲁克" cost="19.5">卓有成效的管理者</book> </books>
某移动省份经分项目的需求如下:
140亿张话单汇总,现在需要38分钟,每个月做一次,粗粒度的计算;
目前应对该需求,客户使用的硬件资源是:
4台superdone,2个EMC存储,总共需要100T的存储;
针对该客户的项目需求,使用Greenplum进行解决,所需要的硬件配置如下:
16台Segment服务器,4套EMC的存储,详细配置如下:
|
要素 |
型号/配置 |
数量 |
单位 |
|
Segment服务器 |
2×4核 Xeon 55xx CPU 48G RAM |
16 |
台 |
|
硬盘容量规格 |
SAS 15k rpm |
600 |
GB |
|
硬盘性能规格 |
SAS 15k rpm |
250 |
IOPS |
|
Segment服务器可用硬盘个数 |
每台 |
36 |
个 |
|
RAID 类型 |
5 |
5/6 |
可用率 |
|
Segment服务器数量 |
|
16 |
台 |
|
EMC CX4-960/VMX |
8个8Gbps FC口,8G缓存 |
4 |
套 |
|
每套EMC存储阵列的硬盘个数 |
|
144 |
个 |
|
每套EMC存储阵列的硬盘存储容量 |
86,400 |
GB |
|
|
合计存储性能 |
|
120,000 |
IOPS |
|
合计存储可用容量 |
|
288,000 |
GB |
|
Greenplum 数据仓库可用容量 |
|
100,800 |
GB |
|
Greenplum 数据仓库性能 |
(估) |
43.63636364 |
GB/s |
|
Greenplum 数据仓库性能 |
TPC-H性能(估) |
3950646.0 |
|
|
Greenplum 数据库性能 |
TPC-C性能(估) |
9097430.4 |
TpmC |
从系统架构来看,目前的商用服务器大体可以分为三类,即对称多处理器结构 (SMP : Symmetric Multi-Processor) ,非一致存储访问结构 (NUMA : Non-Uniform Memory Access) ,以及海量并行处理结构 (MPP : Massive Parallel Processing) 。它们的特征分别描述如下:
SMP(Symmetric Multi-Processor)
所谓对称多处理器结构,是指服务器中多个 CPU 对称工作,无主次或从属关系。各 CPU 共享相同的物理内存,每个 CPU 访问内存中的任何地址所需时间是相同的,因此 SMP 也被称为一致存储器访问结构 (UMA : Uniform Memory Access) 。对 SMP 服务器进行扩展的方式包括增加内存、使用更快的 CPU 、增加 CPU 、扩充 I/O( 槽口数与总线数 ) 以及添加更多的外部设备 ( 通常是磁盘存储 ) 。
SMP 服务器的主要特征是共享,系统中所有资源 (CPU 、内存、 I/O 等 ) 都是共享的。也正是由于这种特征,导致了 SMP 服务器的主要问题,那就是它的扩展能力非常有限。对于 SMP 服务器而言,每一个共享的环节都可能造成 SMP 服务器扩展时的瓶颈,而最受限制的则是内存。由于每个 CPU 必须通过相同的内存总线访问相同的内存资源,因此随着 CPU 数量的增加,内存访问冲突将迅速增加,最终会造成 CPU 资源的浪费,使 CPU 性能的有效性大大降低。实验证明, SMP 服务器 CPU 利用率最好的情况是 2 至 4 个 CPU 。
NUMA(Non-Uniform Memory Access)
由于 SMP 在扩展能力上的限制,人们开始探究如何进行有效地扩展从而构建大型系统的技术, NUMA 就是这种努力下的结果之一。利用 NUMA 技术,可以把几十个 CPU( 甚至上百个 CPU) 组合在一个服务器内。其 CPU 模块结构如图 2 所示:
NUMA 服务器的基本特征是具有多个 CPU 模块,每个 CPU 模块由多个 CPU( 如 4 个 ) 组成,并且具有独立的本地内存、 I/O 槽口等。由于其节点之间可以通过互联模块 ( 如称为 Crossbar Switch) 进行连接和信息交互,因此每个 CPU 可以访问整个系统的内存 ( 这是 NUMA 系统与 MPP 系统的重要差别 ) 。显然,访问本地内存的速度将远远高于访问远地内存 ( 系统内其它节点的内存 ) 的速度,这也是非一致存储访问 NUMA 的由来。由于这个特点,为了更好地发挥系统性能,开发应用程序时需要尽量减少不同 CPU 模块之间的信息交互。
利用 NUMA 技术,可以较好地解决原来 SMP 系统的扩展问题,在一个物理服务器内可以支持上百个 CPU 。比较典型的 NUMA 服务器的例子包括 HP 的 Superdome 、 SUN15K 、 IBMp690 等。
但 NUMA 技术同样有一定缺陷,由于访问远地内存的延时远远超过本地内存,因此当 CPU 数量增加时,系统性能无法线性增加。如 HP 公司发布 Superdome 服务器时,曾公布了它与 HP 其它 UNIX 服务器的相对性能值,结果发现, 64 路 CPU 的 Superdome (NUMA 结构 ) 的相对性能值是 20 ,而 8 路 N4000( 共享的 SMP 结构 ) 的相对性能值是 6.3 。从这个结果可以看到, 8 倍数量的 CPU 换来的只是 3 倍性能的提升。
MPP(Massive Parallel Processing)
和 NUMA 不同, MPP 提供了另外一种进行系统扩展的方式,它由多个 SMP 服务器通过一定的节点互联网络进行连接,协同工作,完成相同的任务,从用户的角度来看是一个服务器系统。其基本特征是由多个 SMP 服务器 ( 每个 SMP 服务器称节点 ) 通过节点互联网络连接而成,每个节点只访问自己的本地资源 ( 内存、存储等 ) ,是一种完全无共享 (Share Nothing) 结构,因而扩展能力最好,理论上其扩展无限制,目前的技术可实现 512 个节点互联,数千个 CPU 。目前业界对节点互联网络暂无标准,如 NCR 的 Bynet , IBM 的 SPSwitch ,它们都采用了不同的内部实现机制。但节点互联网仅供 MPP 服务器内部使用,对用户而言是透明的。
在 MPP 系统中,每个 SMP 节点也可以运行自己的操作系统、数据库等。但和 NUMA 不同的是,它不存在异地内存访问的问题。换言之,每个节点内的 CPU 不能访问另一个节点的内存。节点之间的信息交互是通过节点互联网络实现的,这个过程一般称为数据重分配 (Data Redistribution) 。
但是 MPP 服务器需要一种复杂的机制来调度和平衡各个节点的负载和并行处理过程。目前一些基于 MPP 技术的服务器往往通过系统级软件 ( 如数据库 ) 来屏蔽这种复杂性。举例来说, NCR 的 Teradata 就是基于 MPP 技术的一个关系数据库软件,基于此数据库来开发应用时,不管后台服务器由多少个节点组成,开发人员所面对的都是同一个数据库系统,而不需要考虑如何调度其中某几个节点的负载。
NUMA 与 MPP 的区别
从架构来看, NUMA 与 MPP 具有许多相似之处:它们都由多个节点组成,每个节点都具有自己的 CPU 、内存、 I/O ,节点之间都可以通过节点互联机制进行信息交互。那么它们的区别在哪里?通过分析下面 NUMA 和 MPP 服务器的内部架构和工作原理不难发现其差异所在。
首先是节点互联机制不同, NUMA 的节点互联机制是在同一个物理服务器内部实现的,当某个 CPU 需要进行远地内存访问时,它必须等待,这也是 NUMA 服务器无法实现 CPU 增加时性能线性扩展的主要原因。而 MPP 的节点互联机制是在不同的 SMP 服务器外部通过 I/O 实现的,每个节点只访问本地内存和存储,节点之间的信息交互与节点本身的处理是并行进行的。因此 MPP 在增加节点时性能基本上可以实现线性扩展。
其次是内存访问机制不同。在 NUMA 服务器内部,任何一个 CPU 可以访问整个系统的内存,但远地访问的性能远远低于本地内存访问,因此在开发应用程序时应该尽量避免远地内存访问。在 MPP 服务器中,每个节点只访问本地内存,不存在远地内存访问的问题。
数据仓库的选择
哪种服务器更加适应数据仓库环境?这需要从数据仓库环境本身的负载特征入手。众所周知,典型的数据仓库环境具有大量复杂的数据处理和综合分析,要求系统具有很高的 I/O 处理能力,并且存储系统需要提供足够的 I/O 带宽与之匹配。而一个典型的 OLTP 系统则以联机事务处理为主,每个交易所涉及的数据不多,要求系统具有很高的事务处理能力,能够在单位时间里处理尽量多的交易。显然这两种应用环境的负载特征完全不同。
从 NUMA 架构来看,它可以在一个物理服务器内集成许多 CPU ,使系统具有较高的事务处理能力,由于远地内存访问时延远长于本地内存访问,因此需要尽量减少不同 CPU 模块之间的数据交互。显然, NUMA 架构更适用于 OLTP 事务处理环境,当用于数据仓库环境时,由于大量复杂的数据处理必然导致大量的数据交互,将使 CPU 的利用率大大降低。
相对而言, MPP 服务器架构的并行处理能力更优越,更适合于复杂的数据综合分析与处理环境。当然,它需要借助于支持 MPP 技术的关系数据库系统来屏蔽节点之间负载平衡与调度的复杂性。另外,这种并行处理能力也与节点互联网络有很大的关系。显然,适应于数据仓库环境的 MPP 服务器,其节点互联网络的 I/O 性能应该非常突出,才能充分发挥整个系统的性能。
硬件堆栈概述
Segment处理数据库中大多数的数据库进程,因此特别注意采用好的配置尽可能获得最好的greenplum数据库系统性能。
Greenplum数据库的性能由一组segment服务中最慢的segment决定; 因此要确保基本的运行greenplum数据的硬件与操作系统在同一个性能级别,同样建议在greenplum数据系统中的所有的segment机器有一样的资源与配置;
下面的示图给了一个greenplum数据库segment机器硬件堆栈的实例,在一个机器上的cpu数据决定部署多少个greenplum数据库segment;这个实例显示一个有两个cpu或一个双核cpu机器,注意一个有效的cpu只有一个主segment实例。
每一个cpu应该对应一个逻辑磁盘,一个逻辑磁盘由一组通过I/O通道或磁盘控制器访问一批物理磁盘的文件系统组成。逻辑磁盘与文件系统由操作系统提供。大多数操作系统有提供逻辑磁盘驱动一组RAID物理磁盘的能力;
最优的磁盘阵列方式:
在查询期间,在segment机器上的所有主segment实例用同样的模式同时访问数据,意思是说它们用同样的方式访问同样数量的数据。原因是table上的数据均匀的分布到segment上,并且greenplum数据库的查询执行计划划分工作量到每一个segment上。
在一个segment机器上做磁盘阵列的目标是:一台segment机器有一致的访问模式;最大的segment并行数;顺序存取数据;其它要考虑的是:性能与容量需求;数据安全性需求;CPU的数量;网络配置有代表性的,用于greenplum数据库由一组带有多个网卡的硬件系统,greenplum数据库互联的性能由segment上的网卡的网络负载来提升。
为了区分一个机器上所有网卡的网络跨越负载,你必须配置机器上的每一块网卡,便于分配它自已的子网,一个子网分配一个常用的网络掩码和IP地址组件,举一个例子,由从192.168.1.X开始的IP地址和网络掩码255.255.255.0的接口将是同一个子网, 参考网卡提供商的文档和操作系统指南配置每一个网络接口。
为了达到最好的性能,greenplum建议segment机器上的每一个主segment配一个网卡。如果用镜像,一个主/镜像对将分享一个网卡,为了greenplum数据库追加额外的扩展网卡,master同样需要四个网卡。
在每一个greenplum数据库segment机器上,你将为每个网卡创建不同的机器名;举一个例子:如个一个机器有四个网卡,那么它将有四个相近的机器名,每一个机器名将映射到一个主segment实例,对master你也可以同样配置,然而当你初始化你的greenplum数据库时,只有一个master机器名使用。
使用这个配置,操作系统的TCP/IP协议将选择最好的路径,为了最大的并行性greenplum数据库自动均衡负载网络终点。
多交换配置
如果在你的greenplum数据中使用多个千兆网络交换机,你将在交换机之间均衡的分配子网的数量。在我们的实例配置中,如果我们有两个交换机,每个机器上的网卡1和2将用1号交换机,网卡3与4使用2号交换机,对于master机,绑定到网卡1的机器名是这个master的名字,因此,如果部署一个热备master,这个热备master将用不同的交换机与主master。
采购Segment机器硬件
CPU
为了达到最优的性能,考虑用双核的CPU。一个双核CPU本质上是在一个微处理芯片上的两个独立的处理器,当处理多线程的应用时,两个处理器能够剩过一个单核处理器,一个应用有多个软件线程,比如greenplum数据库,将运行得更快在双核的处理器上,因为操作系统能够分配单个线程在每一个处理器上,一个机器上CPU的数量决定部团到机器上的greenplum数据库segment实例的数量,推荐是一个主segment(或主/镜像对)一个CPU(或core)。
文件系统
每一个CPU应当与一个逻辑磁盘对应,在逻辑磁盘上,主数据与镜像数据应该放在不同的文件系统上,主数据文件系统可以使用磁盘最快的部分,如果在linux上使用XFS,多个segment实例在没有性能省失的情况下分享一个文件系统。
Greenplum数据库能够很好的运行在传统的UNIX文件系统上,比如BSD/UFS/FFS文件系统上,很多操作系统支持,在linux操作系统上,XFS是被推荐的,在solaris操作系统上,ZFS是被推荐的。
磁盘与I/O带宽
为了获得更大的带宽你将尽可能的获取更多的物理磁盘数量。考虑磁盘不仅仅使用时间速度(RPMs),而且要有高的持续不变的内部带宽(64M/s或更大)。
确认你选择的磁盘控制器能够包括磁盘带宽之和。举一个例子,如果你有8个有65M/s内部带宽的磁盘,为了获得磁盘最的性能你将需要能支持最少520M/s的磁盘控制器,举一个例子,一个SATA RAID卡能够对付800M/s;一个SCSI U320通道能对付320M/s。greenplum推荐SATA2硬件RAID控制器,如果用一个SCSI接口,为了获得更好的I/O应该考虑控制卡有多个I/O通道。
内存
通用的原则是内存越大越好。
网络
Greenplum推荐每一个segment机器至少两个千兆网卡,从网络带宽来说,你应当计划磁盘带宽与网络带宽之比为5:1的比率。为了得到更好的性能,考虑在一个segment机器上一个网卡一个主segment实例。

一个PHP程序执行了4个小时,经修改后再次执行只用了44秒的时间,这之间的差距是一个天上一个地下,让我们来见习这伟大的工程,我承认两次的程序都是本人书写的,完全为原创。
刚出来工作的时候对程序优化和mysql优化就像情窦初开的感觉,有点酸甜。也就那时,我常常认为执行少量的循环,少关联几张表,或者把关联拆分就可以达到优化的效果,但是却产生了一个PHP脚本程序本来几分钟可以执行完毕,却执行了4个小时,让我感到困惑,以下为程序的源代码:
$sql = "SELECT SUM(ps.amount - ps.sales) as num , product_id FROM `product_stock` AS ps
WHERE ps.status = 1 AND ps.type = 1 group by ps.product_id";
$rs = mysql_query($sql);
while($row = mysql_fetch_assoc($rs))
{
#获取五天内的订货量
$time = time()-5*24*60*60;
$sql = "SELECT sum(op.amount) as num FROM `order_product` as op
LEFT JOIN `order` as o ON op.order_id=o.order_id
WHERE o.status in (1,2,9,7,8,11,14) AND op.product_id='{$row['product_id']}'
AND UNIX_TIMESTAMP(o.create_date)>='{$time}' GROUP BY op.product_id";
$sale = mysql_fetch_assoc(mysql_query($sql));
$amount = $row['num'] - $sale['num'];
#更新其库存
$sql = "UPDATE products SET v_store='{$amount}' WHERE pid='{$row['product_id']}'";
mysql_query($sql);
......(后面还有很多执行语句)
}
通过该程序可以看出,循环体内太多执行语句,而且里面有mysql的查询语句,为了达到优化,那么必须对此语句进行性能分析,分析最常用的办法就是Explain.
EXPLAIN SELECT SUM(op.amount) AS num FROM `order_product` AS op LEFT JOIN `order` AS o ON op.order_id=o.order_id WHERE o.status IN (1,2,9,7,8,11,14) AND op.product_id='1141' AND UNIX_TIMESTAMP(o.create_date)>='1302495803' GROUP BY op.product_id
执行该语句,得出的结果:
select_type table type possible_keys key key_len ref rows Extra SIMPLE o range order_id,status status 1 (NULL) 34794 Using where; SIMPLE op ref order order 8 o.order_id,const 1
从以上分析可得出,取出该数据的时候并没有用到索引,通过该条数据可得出,执行了一次这样的查询mysql就查询了34794 条数据。那么如果商品的总量达到2000数据,那么2000*34794这样的数据是多么的庞大呀,我也不得不佩服自己的能力能达到这样的水平。所以为了提高性能,我把循环体里的获取五天内的订货量的功能这样实现:
$start_time = getMicroTime();
$productids = array();
$products = array();
$sql = "SELECT SUM(ps.amount - ps.sales) as num , product_id FROM `product_stock` AS ps
WHERE ps.status = 1 AND ps.type = 1 group by ps.product_id";
$rs = @mysql_query($sql);
if($rs)
{
while($row = mysql_fetch_assoc($rs))
{
$productids[$row['product_id']] = $row['product_id'];
$products[$row['product_id']]['stock'] = $row['num'];
$products[$row['product_id']]['product_id'] = $row['product_id'];
}
}
$time_1 = getMicroTime();
$query_time_1 = $time_1 - $start_time;
echo "Execute SQL :{$sql} Time is : ({$query_time_1}) ms \r\n";
$productids = '('.implode(',',$productids).')';
$time =(date('Y-m-d H:i:s',time()-5*24*60*60));
$sql = "SELECT sum(`op`.amount) as num,`op`.product_id FROM `order_product` as op
LEFT JOIN `order` as o ON op.order_id=o.order_id
WHERE o.status in (1,2,9,7,8,11,14)
AND op.product_id IN {$productids} AND o.create_date>='{$time}'
GROUP BY `order_product`.product_id";
$rs = $mysql_query($sql);
if($rs)
{
while($row = $mysql_fetch_assoc($rs))
{
$products[$row['product_id']]['order'] = $row['num'];
}
}
$time_2 = getMicroTime();
$query_time_2 = $time_2 - $time_1;
echo "Execute SQL :{$sql} Time is : ({$query_time_2}) ms \r\n";
......
转换成这样的代码后,执行的效率大大提高了,而且跟mysql的交互也仅仅只有两次而已。这就是我的杰作。
另外补充一点:
SELECT * FROM `order` as o WHERE UNIX_TIMESTAMP(o.create_date)>=' 1314756564'
和
SELECT * FROM `order` as o WHERE o.create_date>=' 2011-08-31 10:09:24'
其实是一样的功能,你会乐意选择哪个呢?
在这内容中我不会讲解那种告诉你怎么做,而是让你理解为什么要这样做,通常都是可以用测试数据来验证其可行性。
在用户登录的模块中,我们会常常碰到以下两种登录代码(PHP代码):
第一种方法:
$sql = “SELECT * FROM users WHERE uname=’$username’ AND upasssword=’$password’”;
$rs = mysql_query($sql);
if($rs)
{
$row = mysql_fetch_assoc($rs);
if(!empty($row))
{
//登录成功 do something
}
}
第二种方法:
$sql = “SELECT * FROM users WHERE uname=’$username’”;
$rs = mysql_query($sql);
if($rs)
{
$row = mysql_fetch_assoc($rs);
if($password == $row[‘upassword’])
{
//登录成功do something
}
}
在这两种方法中都能实现成功登录,我不会果断的断定那条语句执行效率高,我只会去分析到底该选择哪条语句才是最合适的,测试数据正好可以决定你该使用哪一条语句。我们来创建一张会员表,我们给uname字段加上索引。
create table users(
`uid` int not null auto_increment,
`uname` varchar(128) not null,
`upassword` varchar(128) not null,
primary key (`uid`),
key uname(`uname`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
插入几千条数据,然后更改代码,加入运行时间检测。
第一种方法:
function getMicroTime() {
list ($usec, $sec)= explode(' ', microtime());
return ((float) $usec + (float) $sec);
}
$start_time = getMicroTime();
$sql = “SELECT * FROM users WHERE uname=’$username’ AND upasssword=’$password’”;
$rs = mysql_query($sql);
$query_time = getMicroTime();
echo “The mysql excute time is :”.$query_time-$start_time;
if($rs)
{
$row = mysql_fetch_assoc($rs);
if(!empty($row))
{
//登录成功 do something
$login_time = getMicroTime();
echo “The Login time is:”. $login_time-$start_time;
}
}
第二种方法:
function getMicroTime() {
list ($usec, $sec)= explode(' ', microtime());
return ((float) $usec + (float) $sec);
}
$start_time = getMicroTime();
$sql = “SELECT * FROM users WHERE uname=’$username’”;
$rs = mysql_query($sql);
$query_time = getMicroTime();
echo “The mysql excute time is :”.$query_time-$start_time;
if($rs)
{
$row = mysql_fetch_assoc($rs);
if($password == $row[‘upassword’])
{
//登录成功 do something
$login_time = getMicroTime();
echo “The Login time is:”. $login_time-$start_time;
}
}
可以分别执行五次以上测试,得到结果,进行数据分析,决定其可行性.
为了得到mysql执行查询得到更精确的分析,可以使用以下语句来分析执行性能,如:
explain SELECT * FROM users WHERE uname=’$username’
当users表的数据成倍增长的时候,也许你会认为第二种方法的执行效率高,原因之一是查询语句使用了索引查询,mysql执行效率高,所以整体执行速度快。当我在users表在给upassword加入索引,效率又怎么样?当我把
select * from users where uname=’$username’ and upassword=’$password’
改成
select count(1) as num from users where uname=’$username’ and upassword=’$password’
效率又如何呢?
程序只是跟电脑交互语言,是死的,关键是如何应用,才是活的,这是一个思想,也是经验。程序也是一种艺术,优美的代码会给人一种亲切的感觉,但是过于优美而没有注重效率,代码也一样是失败品。