本节书摘来自华章出版社《MySQL DBA修炼之道》一书中的第3章,第3.4节,作者:陈晓勇,更多章节内容可以访问云栖社区“华章计算机”公众号查看
3.4 PHP开发
3.4.1 概述
一般的流行语言,如PHP、C、Perl、Java都对MySQL提供了完善支持,这其中PHP是最常用的使用MySQL数据库的语言,互联网普遍使用的是LAMP/LNMP架构,这里的P可以理解为就是PHP,可以说PHP的应用范围相当广泛,尤其是在Web程序的开发上,比如,我们熟知的Facebook,就是PHP、MySQL的重度使用者。作为互联网开发者,我们有必要熟悉MySQL在各种语言环境下的使用,尤其是PHP。
以下简要介绍PHP与MySQL开发,PHP(全称为Hypertext Preprocessor,即超文本预处理器)是一种开源的通用计算机脚本语言,它是服务器端的解释语言,可以嵌入到HTML页面中,这些代码在每次页面访问时都将被执行。PHP代码将在Web服务器中被解释并且生成HTML或访问者看到的其他输出界面。
如果想要学习PHP+MySQL开发,那么首先需要搭建一个适合练习的开发环境。对于操作系统,建议使用Linux;Web服务器建议使用Apache或Nginx;数据库当然是MySQL了。网上有很多搭建LAMP(Linux、Apache、MySQL、PHP)环境的文档,大家可以搜索查看。
也有一些集成的自动安装包,如XAMPP,可以一键安装帮你部署好所有环境,但还是建议手动部署下环境,从而对Web服务器的配置文件、PHP的配置文件有一定的了解。
3.4.2 客户端访问过程
下面简单介绍下传统网站的访问数据流。在客户端(用户)与数据库服务器之间往往还会涉及Web服务器和负载均衡设备。作为一个开发者,需要清楚数据是如何在客户端和服务器之间进行传递的。下面简单说明下客户端访问数据库服务器中间经过的环节,这里以Windows PC浏览器为例进行说明,并解释一些软硬件基础概念。
1.访问DNS服务
用户在浏览器的地址栏输入网址域名,浏览器会查询这个域名与IP的对应关系是否已经存在于本机的host文件中,如果没有,则会把请求发送给本机指定的域名系统(Domain Name System,DNS)服务器。
什么是域名系统服务器呢?
计算机世界是以IP地址来定位服务器或PC的,DNS这项服务的目的就是将域名翻译成IP,使用户可以更方便地访问互联网。DNS服务器有一定的层级,如果某个DNS服务器不知道如何翻译,就会问另外一个,再不知道,再问下一个,这样就会有一个递归的过程。幸运的是,DNS可以缓存查询结果,这样我们就不需要经历重复冗长的过程去查找一个域名映射的IP地址。
DNS系统中,常见的资源记录类型有如下两种。
主机记录(A记录):用于名称解析的重要记录,它将特定的主机名映射到对应主机的IP地址上。
别名记录(CNAME记录):用于将某个别名指向到某个A记录上,这样的好处是修改IP的时候改A记录就可以了,对于有大量子域名的网站可以简化操作、统一维护域名指向。
DNS查询有两种方式:递归和迭代。DNS客户端设置使用的DNS服务器一般都是递归服务器,它负责全权处理客户端的DNS查询请求,直到返回最终结果。而DNS服务器之间一般采用迭代查询的方式。
下面以查询 zh.wikipedia.org 为例。
客户端发送查询报文“query zh.wikipedia.org”至DNS服务器,DNS服务器首先检查自身缓存,如果存在记录则直接返回结果。
如果记录老化或不存在,则DNS服务器向根域名服务器发送查询报文“query zh.wikipedia.org”,根域名服务器返回“.org”域的权威域名服务器地址。
DNS服务器向“.org”域的权威域名服务器发送查询报文“query zh.wikipedia.org”,得到“.wikipedia.org”域的权威域名服务器地址。
DNS服务器向“.wikipedia.org”域的权威域名服务器发送查询报文“query zh.wikipedia.org”,得到主机zh的A记录,存入自身缓存并返回给客户端。
DNS系统还有一个很重要的概念:TTL(Time To Live的缩写),简单地说,它表示的是一条域名解析记录在DNS服务器上的缓存时间。当一个递归域名服务器查询权威域名服务器获取某个域名的映射时,它会将该记录缓存上一定的时间,这个时间就是TTL指定的时间(以秒为单位)。如果在一台Linux机器上反复运行命令“dig www.mysql.com”,就会发现这个缓存时间在减少,为什么呢?因为在你的DNS缓存中,这笔记录能够保存的时间开始倒计数,如果TTL没有归零,缓存服务器会简单地用已缓存的记录答复查询请求。若这个数字归零后,下次再有人重新搜寻这笔记录时,你的DNS就需要从权威域名服务器重新获取记录。也就是说,如果更改了域名的指向,那么最长需要TTL时间才会完全
生效。
了解DNS系统不仅仅是运维团队的事情,研发人员也有必要清楚其大概的机制。我们在部署程序或设计迁移方案的时候,需要清楚是否要申请域名、创建新的CNAME记录,是否需要修改TTL生效时间,是否需要修改A记录。另外需要注意的是,虽然有TTL的机制,但由于国内移动网络的特殊性,DNS的修改可能长期不能生效,由于其特殊的分布式数据库的设计,如果遭到域名污染,往往也会造成巨大的影响,这种问题很难解决。
2.经过负载均衡软硬件设备
经过负载均衡软硬件设备如F5、Haproxy、LVS后,再把请求转发给后端的网络服务。
负载均衡(Load Balance),即将负载(工作任务)进行平衡、分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其他关键任务服务器等,从而共同完成工作任务。当后端的一台服务器宕机或过载,负载均衡软硬件设备将不再转发流量到这台服务器,转而发送到备用的服务器上,从而实现自动故障冗余切换。
最早的负载均衡技术是通过DNS来实现的,在DNS中为多个地址配置同一个名字,因而查询这个名字的客户机将得到其中的一个地址,从而使得不同的客户访问不同的服务器,达到负载均衡的目的。DNS负载均衡是一种简单而且有效的方法,但是它不能区分服务器的差异,也不能反映服务器的当前运行状态,对于高并发、大流量的请求,很容易导致负载并不均衡。现实中,它可能作为更上层的负载均衡存在,完成粗粒度的流量调度任务,比如在机房之间使用DNS负载均衡,在机房内部使用其他负载均衡方式。
F5负载均衡器是应用交付网络的全球领导者F5 Networks公司提供的一个负载均衡器专用设备,一般需要配置双机故障冗余切换。F5主要应用于传统行业内,如电信、移动、银行等,也有许多互联网公司使用F5设备,虽然F5设备比较昂贵,但在一定规模下,它可以降低企业的成本,代替系统管理员、工程师管理各种资源。互联网公司用得比较多的是F5 BIG-IP LTM(本地流量管理器),由于国内网络的复杂性,也有使用BIG-IP广域网流量管理器(BIG-IP GTM)的。F5设备除了负载均衡外,还有一些其他的功能,如利用压缩技术降低带宽支出、减少连接数等。
F5等硬件设备毕竟是商业化的产品,比较昂贵,在一定规模下使用可以获得比较好的投资回报率,但在公司初创时,或者公司已经流量很大的时候,F5设备的成本优势则并不明显,目前很多公司的F5设备已经逐步被LVS等其他软件替代,所以,对于互联网公司,一般建议使用开源软件实现负载均衡,常用的有LVS、Haproxy等,它们和其他技术配合使用可以实现很好的扩展性,无论是在流量很小还是流量很大的情况下,都能够满足需要。网上有很多关于LVS、Haproxy的资料,这里不再赘述。
3.经过反向代理服务
反向代理是代理服务器的一种,比如Squid、Vanish等。它根据客户端的请求,从后端的服务器上获取资源,然后再将这些资源返回给客户端。常用的代理服务为Squid,它可以作为缓存服务器,可以过滤流量保证网络安全,也可以作为代理服务器链中的一环,向上级代理转发数据或直接连接互联网,一些网站往往在前端增加Squid反向代理加速响应、提高吞吐量。Squid可以缓存内容,特别是一些静态的数据,比如图片和文件,如果反向代理靠近用户的网络,那么用户就会得到延时很低的高质量访问,这正是CDN技术的核心。
4.到达Web服务器
Web服务器包括Nginx、Apache、Lighttpd、Tomcat、Resin等。
Apache HTTP Server(简称Apache)是Apache软件基金会的一个开放源代码的网页服务器,是使用最广泛、最流行Web服务器端软件之一。Apache功能最完备,但占用的资源比较多,支持的连接数也比Nginx少,所以目前在互联网界,已经被Nginx抢了风头。
Nginx(发音同engine x)是一款由俄罗斯程序员Igor Sysoev所开发的轻量级的网页服务器,它也可以用作反向代理、负载均衡器,但更常见的功能是Web服务器。它是一款面向性能设计的HTTP服务器,以事件驱动的方式编写,很注重效率,所以在许多评测中,相比于Apache都有更高的性能,能支撑更多的并发请求。
在常见的网络架构中,Nginx往往配合php-fpm使用,Nginx负责处理静态请求,把PHP等动态请求抛给后端的php-fpm处理。或者Nginx处理前端的静态请求,把Apache放在后端处理一些动态请求。所以各种Web服务器之间可能也有一定的层级关系或功能分工,这点需要了解清楚。
5.调用应用服务器
应用程序服务器是通过很多协议来为应用程序提供商业逻辑的服务器。
根据我们的定义,作为应用程序服务器,它将通过各种协议(包括HTTP),把商业逻辑暴露给客户端应用程序。Web服务器主要是处理向浏览器发送HTML以供浏览,而应用程序服务器则提供访问商业逻辑的途径以供客户端应用程序使用。
这里所说的应用服务器更多地属于内部调用的范畴。一般Web服务器可以高效地处理简单的响应请求,但如果有复杂的商业逻辑的话,把这些业务逻辑放到独立的应用服务器上,然后通过调用的方式来获取信息会更安全、性能更高、开发也更方便。
6.访问数据库
客户端不直接和数据库打交道,如果处理逻辑需要访问数据,则由应用服务器或Web服务器访问数据库,获取数据。
以上架构是比较普通的三层/四层架构,架构中也可能有一个缓存(Cache)服务,以减轻数据库的压力。Web服务器、应用服务器到数据库服务器中间可能存在数据中间件,但更常规的做法是通过在Web服务器、应用服务程序配置文件里指定IP或内网域名来配置数据库路由。
生产环境里,如果出现了性能问题,研发人员往往第一时间就会怀疑是数据库出现了性能问题,但事实往往并非如此,从上面的叙述可知,用户的访问请求经过了许多环节,有DNS、负载均衡设备、Web服务器,而且长距离网络中的数据传输物理上还需要经过许多设备,如路由器、交换机等,由于国内网络的特殊性和复杂性,有时会碰到网络丢包,丢包很可能导致性能问题。所以,如果出现了性能问题或访问异常,研发、运维人员就需要仔细甄别到底是哪个环节出现了问题,配合各种监控和日志记录,是有可能快速定位到问题症结所在的。
![604029a11280bfd9c8bc4b97899ff4b8773bf4fa](https://i-blog.csdnimg.cn/blog_migrate/388ed3057707f7b429a1e6aca8020732.png)
3.4.3 开发工具
这里主要介绍一款常用的基于Web的管理工具phpMyAdmin。
phpMyAdmin 是一个以PHP为基础的MySQL的数据库管理工具。让管理者可以通过Web接口操作数据库,也就是于远端管理MySQL数据库。
研发人员并不像DBA那样经常通过命令行来操作数据库,所以往往会不熟悉命令行,借由这套图形工具,可以方便地建立、修改、删除数据库及表、浏览数据、插入数据、删除数据。
phpMyAdmin也是很好的学习工具,可以生成SQL语句,验证语法,也可以生成常用的PHP语法。对于研发人员,若要求熟练使用命令行操作数据库,那么这个要求确实高了些,他们应该把更主要的精力放在程序代码的编写上,所以,建议熟悉此类图形工具。
需要注意的是,未经配置的phpMyAdmin不安全,容易受到攻击,建议只将其部署在开发和测试环境中。在生产环境中,如果需要部署phpMyAdmin,那么一定要先经过安全评估,确认没有安全漏洞,而这往往是需要通过改造phpMyAdmin来实现的。
还有一些客户端工具,如Workbench、toad for MySQL、SQLyog、Navicat for MySQL。这其中,SQLyog功能最强大,但是,它是收费软件,Workbench出自官方,对比以前的版本功能已经有了长足的进步,而且也一直在努力改进中,建议大家优先使用这个工具。
3.4.4 操作数据
如果想要做Web开发,那么读者需要熟悉HTML、XML、JavaScript、Ajax等知识。关于这些知识和PHP的基本语法和特性这里不做介绍,本章主要讲述PHP相对于数据的一些常用操作。如果需要详细了解PHP开发知识,可以参考官方文档或《PHP与MySQL Web开发》一书。
PHP提供了3种API可用于访问MySQL,分别是MySQL扩展、MySQLi扩展、PDO扩展。官方的建议是使用MySQLi或PDO,因为MySQL扩展在未来可能被废弃掉。MySQLi只支持MySQL数据库,如果有跨数据库平台的需要,那么就要使用PDO了。由于目前MySQL扩展仍然有广泛的应用,以下示例仍以MySQL扩展为例,同时也将列出MySQLi的代码供读者参考。
1. PHP连接数据库
在访问并处理数据库中的数据之前,必须先创建到达数据库的连接。
PHP提供了两个连接MySQL的函数mysql_pconnect() 和 mysql_connect()。
mysql_connect() 函数的语法如下。
mysql_connect(servername,username,password);
涉及的参数详见表3-14。
![34c109f158142ee7fa599408ba321b5609a21f91](https://i-blog.csdnimg.cn/blog_migrate/94e0c7e7fa758d730c886ea01922ddf7.png)
脚本一结束,就会关闭连接。如需提前关闭连接,请使用mysql_close()函数。
2.选择数据库
在执行语句前,需要先选择数据库。通过mysql_select_db()函数可选取数据库,它的语法如下。
使用MySQLi连接数据库,语句如下。
![1343f3e46865c410d89458d8ecadc2566d243f50](https://i-blog.csdnimg.cn/blog_migrate/48f1d2320a0fb0e50f02d98a08d910af.png)
3.查询数据
先使用mysql_query()函数向 MySQL 发送查询或命令。然后使用mysql_fetch_array函数返回数据。
示例test_select.php语句如下。
上面这个例子在$result变量中存放了由mysql_query()函数返回的数据。接下来,使用mysql_fetch_array()函数以数组的形式从记录集返回第一行。随后对于mysql_fetch_array()函数的每个调用都会返回记录集中的下一行。while loop语句会循环记录集中的所有记录。为了输出每行的值,这里使用了PHP的$row变量($row['dept_no']和$row['dept_name'])。
4.使用MySQLi查询数据
首先新建连接(new mysqli),然后预处理查询语句(使用prepare方法),执行这个语句(使用execute方法),最后,把结果集的列绑定到两个变量(使用bind_result方法),并获取实际数据(使用fetch方法),打印输出。
示例test_select2.php语句如下。
<html>
<head>
<title>select table</title>
</head>
<body>
<?php
$host="127.0.0.1";
$port=3306;
$socket="";
$user="garychen";
$password="garychen";
$dbname="employees";
$con = new mysqli($host, $user, $password, $dbname, $port, $socket)
or die ('Could not connect to the database server' . mysqli_connect_error());
echo 'connect to employees database successfully';
echo "<br />";
echo "select departments table";
echo "<br />";
$query = "select * from departments";
if ($stmt = $con->prepare($query)) {
$stmt->execute();
$stmt->bind_result($field1, $field2);
while ($stmt->fetch()) {
printf("%s, %s\n", $field1, $field2);
echo "<br />";
}
$stmt->close();
}
$con->close();
?>
</body>
</html>
5.插入记录
类似SELECT查询,先连接数据库(使用mysql_connect函数),然后选择数据库(使用mysql_select_db函数),最后发送INSERT语句给MySQL(使用mysql_query函数)。
示例test_insert.php语句如下。
<html>
<head>
<title>Insert records</title>
</head>
<body>
<?php
$con = mysql_connect("127.0.0.1","garychen","garychen");
if (!$con)
{
die('Could not connect: ' . mysql_error());
}
mysql_select_db("employees", $con);
$sql="INSERT INTO employees (emp_no, birth_date, first_name,last_name,gender,hire_date)
VALUES (500000,'1990-08-01','Peter', 'wang', 'M','2011-11-11')";
if (!mysql_query($sql,$con))
{
die('Error: ' . mysql_error());
}
echo "1 record added";
mysql_close($con);
?>
</body>
</html>
6. 使用MySQLi插入记录
新建连接(new mysqli),然后使用mysqli_query方法操作数据库,执行查询。
示例test_insert2.php语句如下。
<html>
<head>
<title>insert records</title>
</head>
<body>
<?php
$host="127.0.0.1";
$port=3306;
$socket="";
$user="garychen";
$password="garychen";
$dbname="employees";
$con = new mysqli($host, $user, $password, $dbname, $port, $socket)
or die ('Could not connect to the database server' . mysqli_connect_error());
echo 'connect to employees database successfully';
echo "<br />";
$sql="INSERT INTO employees (emp_no, birth_date, first_name,last_name,gender,hire_date)
VALUES (500003,'1990-09-01','Peter', 'zhang', 'M','2011-12-12')";
if (!mysqli_query($con,$sql))
{
die('Error: ' . mysqli_error($con));
}
echo "1 record added";
$con->close();
?>
</body>
</html>
3.4.5 PHP数据库开发建议
对于开发人员来说,了解数据流和生产环境的物理部署是很重要的,开发人员不应该局限于自己的领域。
能够熟练查阅MySQL官方文档,虽然研发人员不必熟悉MySQL的每一个细节,但要善于查找资料,找到答案,一般我们经历的问题都是可以从文档或网络中找到答案的。
注意安全问题,防止SQL注入、跨站脚本攻击等恶意攻击。详细内容可参考4.2节“权限机制和安全”。
阅读源码,了解它们是如何访问数据的。一些网站,如sourceforge提供了许多优秀的源码。
注重用户体验,数据库的访问优化,往往和用户体验相关。如果要访问数据库或数据接口,优化的方式不外乎两个,或者减少对数据的访问,或者让查询执行得更快。比如一些大页面只改动了几行字,那么查询时可能就只需要检索小部分数据,而不是重载整个页面查询很多数据。比如地图,只需要加载部分块区域,而不是整个图重新绘制。比如翻页,传统的一些算法效率往往很差,影响用户体验。
要有性能分析器,如果想要开发高质量的程序,那么需要对自己的程序进行分析,特别是对于数据库访问的分析,比如记录数据库性能访问日志。一些工具也有助于分析网站的响应,如firebug。