phpcachemode的简单介绍

发布时间:2023-01-04

本文目录一览:

  1. php写文件 路径的格式
  2. php二进制流输出文件出错
  3. 几种常见的PHP超时处理方法
  4. 想做个 网站 ,求一段PHP编程代码,PHP的MYSQL缓存怎么实现? 最好举个例子。

php写文件 路径的格式

可以直接使用绝对路径。 如果是Windows可以直接写“C:/Cache/” 如果是Linux可以直接写“/Cache/”,需先改cache的权限为777。看看报什么错。在PHP的最前面加上error_reporting(E_ALL ~E_NOTICE);相对路径是相对fopen所在的PHP文件即a.php而言的。

$f = fopen("cache/$filename", 'ab');

如果a.php在根目录下的b文件夹:

$f = fopen("../cache/$filename", 'ab');

php二进制流输出文件出错

你要的内容太多了,只能简单的说下。 fopen()filename 指定的名字资源绑定到一个流上。如果 filename"scheme://..." 的格式,则被当成一个 URL,PHP 将搜索协议处理器(也被称为封装协议)来处理此模式。如果该协议尚未注册封装协议,PHP 将发出一条消息来帮助检查脚本中潜在的问题并将 filename 当成一个普通的文件名继续执行下去。 如果 PHP 认为 filename 指定的是一个已注册的协议,而该协议被注册为一个网络 URL,PHP 将检查并确认 allow_url_fopen 已被激活。如果关闭了,PHP 将发出一个警告,而 fopen 的调用则失败。 注意:对 context 的支持是 PHP 5.0.0 添加的。有关 context 的说明见参考 CLX, Stream Functions。 注意:自 PHP 4.3.2 起,对所有区别二进制和文本模式的平台默认模式都被设为二进制模式。如果在升级后脚本碰到问题,尝试暂时使用 't' 标记,直到所有的脚本都照以下所说的改为更具移植性以后。 mode 参数指定了所要求到该流的访问类型。可以是以下:

mode 说明
'r' 只读方式打开,将文件指针指向文件头。
'r+' 读写方式打开,将文件指针指向文件头。
'w' 写入方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
'w+' 读写方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
'a' 写入方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
'a+' 读写方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
'x' 创建并以写入方式打开,将文件指针指向文件头。如果文件已存在,则 fopen() 调用失败并返回 FALSE,并生成一条 E_WARNING 级别的错误信息。
'x+' 创建并以读写方式打开,将文件指针指向文件头。如果文件已存在,则 fopen() 调用失败并返回 FALSE,并生成一条 E_WARNING 级别的错误信息。
注意:不同的操作系统家族具有不同的行结束习惯。当写入一个文本文件并想插入一个新行时,需要使用符合操作系统的行结束符号。基于 Unix 的系统使用 \n 作为行结束字符,基于 Windows 的系统使用 \r\n 作为行结束字符,基于 Macintosh 的系统使用 \r 作为行结束字符。
如果写入文件时使用了错误的行结束符号,则其它应用程序打开这些文件时可能会表现得很怪异。Windows 下提供了一个文本转换标记('t')可以透明地将 \n 转换为 \r\n。与此对应还可以使用 'b' 来强制使用二进制模式,这样就不会转换数据。要使用这些标记,要么用 'b' 或者用 't' 作为。

几种常见的PHP超时处理方法

Web服务器超时处理

Apache

一般在性能很高的情况下,缺省所有超时配置都是30秒,但是在上传文件,或者网络速度很慢的情况下,那么可能触发超时操作。 目前 Apache + FastCGI + PHP-FPM 模式下有三个超时设置: FastCGI 超时设置: 修改的 FastCGI 连接配置,类似如下:

<IfModule mod_fastcgi.c>
    FastCgiExternalServer /home/forum/apache/apache_php/cgi-bin/php-cgi -socket /home/forum/php5/etc/php-fpm.sock
    ScriptAlias /fcgi-bin/ "/home/forum/apache/apache_php/cgi-bin/"
    AddHandler php-fastcgi .php
    Action php-fastcgi /fcgi-bin/php-cgi
    AddType application/x-httpd-php .php
</IfModule>

缺省配置是30s,如果需要定制自己的配置,需要修改配置,比如修改为100秒:

<IfModule mod_fastcgi.c>
    FastCgiExternalServer /home/forum/apache/apache_php/cgi-bin/php-cgi -socket /home/forum/php5/etc/php-fpm.sock -idle-timeout 100
    ScriptAlias /fcgi-bin/ "/home/forum/apache/apache_php/cgi-bin/"
    AddHandler php-fastcgi .php
    Action php-fastcgi /fcgi-bin/php-cgi
    AddType application/x-httpd-php .php
</IfModule>

如果超时会返回500错误,断开跟后端PHP服务的连接,同时记录一条Apache错误日志:

[Thu Jan 27 18:30:15 2011] [error] [client 10.81.41.110] FastCGI: comm with server "/home/forum/apache/apache_php/cgi-bin/php-cgi" aborted: idle timeout (30 sec)
[Thu Jan 27 18:30:15 2011] [error] [client 10.81.41.110] FastCGI: incomplete headers (0 bytes) received from server "/home/forum/apache/apache_php/cgi-bin/php-cgi"

其他 FastCGI 配置参数说明:

参数 说明
IdleTimeout 发呆时限
ProcessLifeTime 一个进程的最长生命周期,过期之后无条件 kill
MaxProcessCount 最大进程个数
DefaultMinClassProcessCount 每个程序启动的最小进程个数
DefaultMaxClassProcessCount 每个程序启动的最大进程个数
IPCConnectTimeout 程序响应超时时间
IPCCommTimeout 与程序通讯的最长时间
MaxRequestsPerProcess 每个进程最多完成处理个数,达成后自杀

Lighttpd

配置中,关于超时的参数有如下几个(篇幅考虑,只写读超时,写超时参数同理): 主要涉及选项:

server.max-keep-alive-idle = 5
server.max-read-idle = 60
server.read-timeout = 0
server.max-connection-idle = 360

示例配置:

# 每次keep-alive的最大请求数,默认值是16
server.max-keep-alive-requests = 100
# keep-alive的最长等待时间,单位是秒,默认值是5
server.max-keep-alive-idle = 1200
# lighttpd的work子进程数,默认值是0,单进程运行
server.max-worker = 2
# 限制用户在发送请求的过程中,最大的中间停顿时间(单位是秒)
# 如果用户在发送请求的过程中(没发完请求),中间停顿的时间太长,lighttpd会主动断开连接
# 默认值是60(秒)
server.max-read-idle = 1200
# 限制用户在接收应答的过程中,最大的中间停顿时间(单位是秒)
# 如果用户在接收应答的过程中(没接完),中间停顿的时间太长,lighttpd会主动断开连接
# 默认值是360(秒)
server.max-write-idle = 12000
# 读客户端请求的超时限制,单位是秒,配为0表示不作限制
# 设置小于max-read-idle时,read-timeout生效
server.read-timeout = 0
# 写应答页面给客户端的超时限制,单位是秒,配为0表示不作限制
# 设置小于max-write-idle时,write-timeout生效
server.write-timeout = 0
# 请求的处理时间上限,如果用了mod_proxy_core,那就是和后端的交互时间限制,单位是秒
server.max-connection-idle = 1200

说明: 对于一个 keep-alive 连接上的连续请求,发送第一个请求内容的最大间隔由参数 max-read-idle 决定,从第二个请求起,发送请求内容的最大间隔由参数 max-keep-alive-idle 决定。请求间的间隔超时也由 max-keep-alive-idle 决定。发送请求内容的总时间超时由参数 read-timeout 决定。Lighttpd 与后端交互数据的超时由 max-connection-idle 决定。

Nginx

配置:

http {
    # Fastcgi:(针对后端的fastcgi生效,fastcgi不属于proxy模式)
    fastcgi_connect_timeout 5; # 连接超时
    fastcgi_send_timeout 10;   # 写超时
    fastcgi_read_timeout 10;   # 读取超时
    # Proxy:(针对proxy/upstreams的生效)
    proxy_connect_timeout 15s; # 连接超时
    proxy_read_timeout 24s;    # 读超时
    proxy_send_timeout 10s;    # 写超时
}

说明: Nginx 的超时设置倒是非常清晰容易理解,上面超时针对不同工作模式,但是因为超时带来的问题是非常多的。

PHP本身超时处理

PHP-FPM

配置:

<?xml version="1.0"?>
<configuration>
    <!-- 设置php-cgi的进程数量 -->
    <value name="max_children">128</value>
    <!-- php-fpm 请求执行超时时间,0s为永不超时,否则设置一个 Ns 为超时的秒数 -->
    <value name="request_terminate_timeout">0s</value>
    <!-- php-fpm 请求慢日志超时时间 -->
    <value name="request_slowlog_timeout">0s</value>
</configuration>

说明: 在 php.ini 中,有一个参数 max_execution_time 可以设置 PHP 脚本的最大执行时间,但是,在 php-cgiphp-fpm)中,该参数不会起效。真正能够控制 PHP 脚本最大执行时间的是:

<value name="request_terminate_timeout">0s</value>

就是说如果是使用 mod_php5.so 的模式运行 max_execution_time 是会生效的,但是如果是 php-fpm 模式中运行时不生效的。

PHP

配置:php.ini 选项:

max_execution_time = 30

或者在代码里设置:

ini_set("max_execution_time", 30);
set_time_limit(30);

说明: 对当前会话生效,比如设置 0 一直不超时,但是如果 PHP 的 safe_mode 打开了,这些设置都会不生效。 效果一样,但是具体内容需要参考 php-fpm 部分内容,如果 php-fpm 中设置了 request_terminate_timeout 的话,那么 max_execution_time 就不生效。

后端接口访问超时

HTTP访问

一般我们访问HTTP方式很多,主要是:curlsocketfile_get_contents() 等方法。 如果碰到对方服务器一直没有响应的时候,我们就悲剧了,很容易把整个服务器搞死,所以在访问 HTTP 的时候也需要考虑超时的问题。

CURL 访问HTTP

curl_setopt($ch, opt) 可以设置一些超时的设置,主要包括:

  • CURLOPT_TIMEOUT:设置 cURL 允许执行的最长秒数。
  • CURLOPT_TIMEOUT_MS:设置 cURL 允许执行的最长毫秒数。(在 cURL 7.16.2 中被加入。从 PHP 5.2.3 起可使用。)
  • CURLOPT_CONNECTTIMEOUT:在发起连接前等待的时间,如果设置为 0,则无限等待。
  • CURLOPT_CONNECTTIMEOUT_MS:尝试连接等待的时间,以毫秒为单位。如果设置为 0,则无限等待。在 cURL 7.16.2 中被加入。从 PHP 5.2.3 开始可用。
  • CURLOPT_DNS_CACHE_TIMEOUT:设置在内存中保存 DNS 信息的时间,默认为 120 秒。 curl普通秒级超时:
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 60); // 只需要设置一个秒的数量就可以
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_USERAGENT, $defined_vars['HTTP_USER_AGENT']);

curl如果需要进行毫秒超时,需要增加:

curl_setopt($ch, CURLOPT_NOSIGNAL, true);

curl一个毫秒级超时的例子:

<?php
if (!isset($_GET['foo'])) {
    // Client
    $ch = curl_init('');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_NOSIGNAL, 1); // 注意,毫秒超时一定要设置这个
    curl_setopt($ch, CURLOPT_TIMEOUT_MS, 200); // 超时毫秒,cURL 7.16.2 中被加入。从 PHP 5.2.3 起可使用
    $data = curl_exec($ch);
    $curl_errno = curl_errno($ch);
    $curl_error = curl_error($ch);
    curl_close($ch);
    if ($curl_errno > 0) {
        echo "cURL Error ($curl_errno): $curl_error\n";
    } else {
        echo "Data received: $data\n";
    }
} else {
    // Server
    sleep(10);
    echo "Done.";
}
?>

其他一些技巧:

  1. 按照经验总结是:cURL 版本 >= libcurl/7.21.0 版本,毫秒级超时是一定生效的,切记。
  2. curl_multi 的毫秒级超时也有问题。单次访问是支持 ms 级超时的,curl_multi 并行调多个会不准。
流处理方式访问HTTP

除了 curl,我们还经常自己使用 fsockopen、或者是 file 操作函数来进行 HTTP 协议的处理,所以,我们对这块的超时处理也是必须的。 一般连接超时可以直接设置,但是流读取超时需要单独处理。 自己写代码处理:

$tmCurrent = gettimeofday();
$intUSGone = ($tmCurrent['sec'] - $tmStart['sec']) * 1000000 + ($tmCurrent['usec'] - $tmStart['usec']);
if ($intUSGone > $this->_intReadTimeoutUS) {
    return false;
}

或者使用内置流处理函数 stream_set_timeout()stream_get_meta_data() 处理:

<?php
// Timeout in seconds
$timeout = 5;
$fp = fsockopen("", 80, $errno, $errstr, $timeout);
if ($fp) {
    fwrite($fp, "GET / HTTP/1.0\r\n");
    fwrite($fp, "Host: \r\n");
    fwrite($fp, "Connection: Close\r\n\r\n");
    stream_set_blocking($fp, true); // 重要,设置为非阻塞模式
    stream_set_timeout($fp, $timeout); // 设置超时
    $info = stream_get_meta_data($fp);
    while ((!feof($fp)) && (!$info['timed_out'])) {
        $data .= fgets($fp, 4096);
        $info = stream_get_meta_data($fp);
        ob_flush();
        flush();
    }
    if ($info['timed_out']) {
        echo "Connection Timed Out!";
    } else {
        echo $data;
    }
}
?>

file_get_contents 超时:

<?php
$timeout = array(
    'http' => array(
        'timeout' => 5 // 设置一个超时时间,单位为秒
    )
);
$ctx = stream_context_create($timeout);
$text = file_get_contents("", 0, $ctx);
?>

fopen 超时:

<?php
$timeout = array(
    'http' => array(
        'timeout' => 5 // 设置一个超时时间,单位为秒
    )
);
$ctx = stream_context_create($timeout);
if ($fp = fopen("", "r", false, $ctx)) {
    while ($c = fread($fp, 8192)) {
        echo $c;
    }
    fclose($fp);
}
?>

MySQL

PHP 中的 MySQL 客户端都没有设置超时的选项,mysqlimysql 都没有,但是 libmysql 是提供超时选项的,只是我们在 PHP 中隐藏了而已。 那么如何在 PHP 中使用这个操作捏,就需要我们自己定义一些 MySQL 操作常量,主要涉及的常量有:

MYSQL_OPT_READ_TIMEOUT = 11;
MYSQL_OPT_WRITE_TIMEOUT = 12;

这两个,定义以后,可以使用 options 设置相应的值。 不过有个注意点,MySQL 内部实现:

  1. 超时设置单位为秒,最少配置 1 秒
  2. 但 MySQL 底层的 read 会重试两次,所以实际会是 3 秒 重试两次 + 自身一次 = 3 倍超时时间,那么就是说最少超时时间是 3 秒,不会低于这个值,对于大部分应用来说可以接受,但是对于小部分应用需要优化。 查看一个设置访问 MySQL 超时的 PHP 实例:
<?php
// 自己定义读写超时常量
if (!defined('MYSQL_OPT_READ_TIMEOUT')) {
    define('MYSQL_OPT_READ_TIMEOUT', 11);
}
if (!defined('MYSQL_OPT_WRITE_TIMEOUT')) {
    define('MYSQL_OPT_WRITE_TIMEOUT', 12);
}
// 设置超时
$mysqli = mysqli_init();
$mysqli->options(MYSQL_OPT_READ_TIMEOUT, 3);
$mysqli->options(MYSQL_OPT_WRITE_TIMEOUT, 1);
// 连接数据库
$mysqli->real_connect("localhost", "root", "root", "test");
if (mysqli_connect_errno()) {
    printf("Connect failed: %s\n", mysqli_connect_error());
    exit();
}
// 执行查询 sleep 1 秒不超时
printf("Host information: %s\n", $mysqli->host_info);
if (!($res = $mysqli->query('select sleep(1)'))) {
    echo "query1 error: " . $mysqli->error . "/n";
} else {
    echo "Query1: query success/n";
}
// 执行查询 sleep 9 秒会超时
if (!($res = $mysqli->query('select sleep(9)'))) {
    echo "query2 error: " . $mysqli->error . "/n";
} else {
    echo "Query2: query success/n";
}
$mysqli->close();
echo "close mysql connection/n";
?>

Memcached

PHP扩展

php_memcache 客户端: 连接超时:bool Memcache::connect(string $host [, int $port [, int $timeout]])getset 的时候,都没有明确的超时设置参数。 libmemcached 客户端:在 PHP 接口没有明显的超时参数。 说明:所以说,在 PHP 中访问 Memcached 是存在很多问题的,需要自己 hack 部分操作,或者是参考网上补丁。

C/C++访问Memcached

客户端:libmemcached 客户端 说明:memcache 超时配置可以配置小点,比如 5,10 个毫秒已经够用了,超过这个时间还不如从数据库查询。 下面是一个连接和读取 set 数据的超时的 C++ 示例:

// 创建连接超时(连接到Memcached)
memcached_st* MemCacheProxy::_create_handle() {
    memcached_st* mmc = NULL;
    memcached_return_t prc;
    if (_mpool != NULL) { // get from pool
        mmc = memcached_pool_pop(_mpool, false, prc);
        if (mmc == NULL) {
            __LOG_WARNING__("MemCacheProxy", "get handle from pool error [%d]", (int)prc);
        }
        return mmc;
    }
    memcached_st* handle = memcached_create(NULL);
    if (handle == NULL) {
        __LOG_WARNING__("MemCacheProxy", "create_handle error");
        return NULL;
    }
    // 设置连接/读取超时
    memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_HASH, MEMCACHED_HASH_DEFAULT);
    memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_NO_BLOCK, _noblock); // 参数 MEMCACHED_BEHAVIOR_NO_BLOCK 为 1 使超时配置生效,不设置超时会不生效,关键时候会悲剧的,容易引起雪崩
    memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT, _connect_timeout); // 连接超时
    memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_RCV_TIMEOUT, _read_timeout); // 读超时
    memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_SND_TIMEOUT, _send_timeout); // 写超时
    memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_POLL_TIMEOUT, _poll_timeout);
    // 设置一致 hash
    memcached_behavior_set(handle, MEMCACHED_BEHAVIOR_DISTRIBUTION, MEMCACHED_DISTRIBUTION_CONSISTENT);
    memcached_return rc;
    for (uint i = 0; i < _server_count; i++) {
        rc = memcached_server_add(handle, _ips[i], _ports[i]);
        if (MEMCACHED_SUCCESS != rc) {
            __LOG_WARNING__("MemCacheProxy", "add server [%s:%d] failed.", _ips[i], _ports[i]);
        }
    }
    _mpool = memcached_pool_create(handle, _min_connect, _max_connect);
    if (_mpool == NULL) {
        __LOG_WARNING__("MemCacheProxy", "create_pool error");
        return NULL;
    }
    mmc = memcached_pool_pop(_mpool, false, prc);
    if (mmc == NULL) {
        __LOG_WARNING__("MyMemCacheProxy", "get handle from pool error [%d]", (int)prc);
    }
    return mmc;
}
// 设置一个 key 超时(set 一个数据到 memcached)
bool MemCacheProxy::_add(memcached_st* handle, unsigned int* key, const char* value, int len, unsigned int timeout) {
    memcached_return rc;
    char tmp[1024];
    snprintf(tmp, sizeof(tmp), "%u#%u", key[0], key[1]);
    // 有个 timeout 值
    rc = memcached_set(handle, tmp, strlen(tmp), (char*)value, len, timeout, 0);
    if (MEMCACHED_SUCCESS != rc) {
        return false;
    }
    return true;
}
// Memcache 读取数据超时(没有设置)
libmemcahed 源码中接口定义:
LIBMEMCACHED_API char* memcached_get(memcached_st* ptr, const char* key, size_t key_length, size_t* value_length, uint32_t* flags, memcached_return_t* error);
LIBMEMCACHED_API memcached_return_t memcached_mget(memcached_st* ptr, const char* const* keys, const size_t* key_length, size_t number_of_keys);

从接口中可以看出在读取数据的时候,是没有超时设置的。

如何实现超时

程序中需要有超时这种功能,比如你单独访问一个后端 Socket 模块,Socket 模块不属于我们上面描述的任何一种的时候,它的协议也是私有的,那么这个时候可能需要自己去实现一些超时处理策略,这个时候就需要一些处理代码了。

PHP中超时实现

初级:最简单的超时实现(秒级超时)

思路很简单:链接一个后端,然后设置为非阻塞模式,如果没有连接上就一直循环,判断当前时间和超时时间之间的差异。 PHP socket 中实现原始的超时:(每次循环都当前时间去减,性能会很差,CPU 占用会较高)

<?php
$host = "127.0.0.1";
$port = "80";
$timeout = 15; // timeout in seconds
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("Unable to create socket\n");
socket_set_nonblock($socket) or die("Unable to set nonblock on socket\n");
$time = time();
// 循环的时候每次都减去相应值
while (!@socket_connect($socket, $host, $port)) { // 如果没有连接上就一直死循环
    $err = socket_last_error($socket);
    if ($err == 115 || $err == 114) {
        if ((time() - $time) >= $timeout) { // 每次都需要去判断一下是否超时了
            socket_close($socket);
            die("Connection timed out.\n");
        }
        sleep(1);
        continue;
    }
    die(socket_strerror($err) . "\n");
}
socket_set_block($socket) or die("Unable to set block on socket\n");
?>
升级:使用 PHP 自带异步 IO 去实现(毫秒级超时)

说明: 异步 IO:异步 IO 的概念和同步 IO 相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。异步 IO 将比特分成小组进行传送,小组可以是 8 位的 1 个字符或更长。发送方可以在任何时刻发送这些比特组,而接收方从不知道它们会在什么时候到达。 多路复用:复用模型是对多个 IO 操作进行检测,返回可操作集合,这样就可以对其进行操作了。这样就避免了阻塞 IO 不能随时处理各个 IO 和非阻塞占用系统资源的确定。 使用 socket_select() 实现超时:

socket_select(..., floor($timeout), ceil($timeout * 1000000));

select 的特点:能够设置到微秒级别的超时! 使用 socket_select() 的超时代码(需要了解一些异步 IO 编程的知识去理解)

<?php
class select {
    var $sockets;
    function select($sockets) {
        $this->sockets = array();
        foreach ($sockets as $socket) {
            $this->add($socket);
        }
    }
    function add($add_socket) {
        array_push($this->sockets, $add_socket);
    }
    function remove($remove_socket) {
        $sockets = array();
        foreach ($this->sockets as $socket) {
            if ($remove_socket != $socket)
                $sockets[] = $socket;
        }
        $this->sockets = $sockets;
    }
    function can_read($timeout) {
        $read = $this->sockets;
        socket_select($read, $write = NULL, $except = NULL, $timeout);
        return $read;
    }
    function can_write($timeout) {
        $write = $this->sockets;
        socket_select($read = NULL, $write, $except = NULL, $timeout);
        return $write;
    }
}
?>

想做个 网站 ,求一段PHP编程代码,PHP的MYSQL缓存怎么实现? 最好举个例子。

数据库属于 IO 密集型的应用程序,其主要职责就是数据的管理及存储工作。而我们知道,从内存中读取一个数据库的时间是微秒级别,而从一块普通硬盘上读取一个 IO 是在毫秒级别,二者相差 3 个数量级。所以,要优化数据库,首先第一步需要优化的就是 IO,尽可能将磁盘 IO 转化为内存 IO。本文先从 MySQL 数据库 IO 相关参数(缓存参数)的角度来看看可以通过哪些参数进行 IO 优化:

  • query_cache_size/query_cache_type (global) Query cache 作用于整个 MySQL Instance,主要用来缓存 MySQL 中的 ResultSet,也就是一条 SQL 语句执行的结果集,所以仅仅只能针对 select 语句。当我们打开了 Query Cache 功能,MySQL 在接受到一条 select 语句的请求后,如果该语句满足 Query Cache 的要求(未显式说明不允许使用 Query Cache,或者已经显式申明需要使用 Query Cache),MySQL 会直接根据预先设定好的 HASH 算法将接受到的 select 语句以字符串方式进行 hash,然后到 Query Cache 中直接查找是否已经缓存。也就是说,如果已经在缓存中,该 select 请求就会直接将数据返回,从而省略了后面所有的步骤(如 SQL 语句的解析,优化器优化以及向存储引擎请求数据等),极大的提高性能。 当然,Query Cache 也有一个致命的缺陷,那就是当某个表的数据有任何任何变化,都会导致所有引用了该表的 select 语句在 Query Cache 中的缓存数据失效。所以,当我们的数据变化非常频繁的情况下,使用 Query Cache 可能会得不偿失。 Query Cache 的使用需要多个参数配合,其中最为关键的是 query_cache_sizequery_cache_type,前者设置用于缓存 ResultSet 的内存大小,后者设置在何场景下使用 Query Cache。在以往的经验来看,如果不是用来缓存基本不变的数据的 MySQL 数据库,query_cache_size 一般 256MB 是一个比较合适的大小。当然,这可以通过计算 Query Cache 的命中率(Qcache_hits/(Qcache_hits+Qcache_inserts)*100)来进行调整。query_cache_type 可以设置为 0(OFF),1(ON)或者 2(DEMOND),分别表示完全不使用 query cache,除显式要求不使用 query cache(使用 sql_no_cache)之外的所有的 select 都使用 query cache,只有显示要求才使用 query cache(使用 sql_cache)。
  • binlog_cache_size (global) Binlog Cache 用于在打开了二进制日志(binlog)记录功能的环境,是 MySQL 用来提高 binlog 的记录效率而设计的一个用于短时间内临时缓存 binlog 数据的内存区域。 一般来说,如果我们的数据库中没有什么大事务,写入也不是特别频繁,2MB~4MB 是一个合适的选择。但是如果我们的数据库大事务较多,写入量比较大,可与适当调高 binlog_cache_size。同时,我们可以通过 binlog_cache_use 以及 binlog_cache_disk_use 来分析设置的 binlog_cache_size 是否足够,是否有大量的 binlog_cache 由于内存大小不够而使用临时文件(binlog_cache_disk_use)来缓存了。
  • key_buffer_size (global) Key Buffer 可能是大家最为熟悉的一个 MySQL 缓存参数了,尤其是在 MySQL 没有更换默认存储引擎的时候,很多朋友可能会发现,默认的 MySQL 配置文件中设置最大的一个内存参数就是这个参数了。key_buffer_size 参数用来设置用于缓存 MyISAM 存储引擎中索引文件的内存区域大小。如果我们有足够的内存,这个缓存区域最好是能够存放下我们所有的 MyISAM 引擎表的所有索引,以尽可能提高性能。 此外,当我们在使用 MyISAM 存储的时候有一个及其重要的点需要注意,由于 MyISAM 引擎的特性限制了他仅仅只会缓存索引块到内存中,而不会缓存表数据库块。所以,我们的 SQL 一定要尽可能让过滤条件都在索引中,以便让缓存帮助我们提高查询效率。
  • bulk_insert_buffer_size (