- 浏览: 591005 次
文章分类
最新评论
日志系统性能对比分析
1. 引言
日志系统主要负责记录系统运行过程中的行为和数据,这些行为和数据将作为系统恢复、错误查找、数据纠正的重要依据,其重要性可见一斑!但是往往对于一个有高性能要求的信息系统而言,日志系统往往又是整个系统的瓶颈所在,针对这个问题,以下为寻找一个更优的日志系统设计方案做一些前期的探索工作。
以下将对常用的日志系统进行较简单的实现和测试,因采用以下方式的日志系统都是依赖于以下基本的实现过程,因此其基本上能较为准确的反应出各实现方式的性能差异!
2. TCP日志系统[异步]
2.1 服务端代码
>主函数代码
主函数主要负责侦听指定端口,等待接受客户端的连接请求,同时启动子进程与客户端进行交互。[注意:实际应用中可以使用进程池和线程池机制进行完善,但是测试过程中不必过于复杂]
int main(int argc, const char *argv[]) { int ret = 0, sckid = -1, clifd = 0, len = 0; struct sockaddr_in svraddr, cliaddr; /* Create socket */ sckid = socket(AF_INET, SOCK_STREAM, 0); if(sckid < 0) { fprintf(stderr, "Socket failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Bind port */ bzero(&svraddr, sizeof(svraddr)); svraddr.sin_family = AF_INET; svraddr.sin_addr.s_addr = htonl(INADDR_ANY); svraddr.sin_port = htons(PORT); ret = bind(sckid, (struct sockaddr *)&svraddr, sizeof(svraddr)); if(ret < 0) { fprintf(stderr, "Bind failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } ret = listen(sckid, 20); if(ret < 0) { fprintf(stderr, "Listen failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Receive client connection */ while(1) { memset(&cliaddr, 0, sizeof(cliaddr)); len = sizeof(cliaddr); clifd = accept(sckid, (struct sockaddr *)&cliaddr, &len); ret = fork(); if(ret < 0) { fprintf(stderr, "Fork failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } if(0 == ret) { close(sckid); recv_msg(clifd); exit(1); } } close(sckid); return 0; }
>接收代码
此函数由服务端子进程调用,用于接收客户端发送的日志信息,并将信息写入指定的日志文件中!
int recv_msg(int clifd) { int ret = 0, fd = -1; char buf[1024] = {0}; fd = open("test.log", O_CREAT|O_WRONLY|O_APPEND, 0666); if(fd < 0) { fprintf(stderr, "Open failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } while(1) { memset(buf, 0, sizeof(buf)); ret = read(clifd, buf, sizeof(buf) - 1); if(ret < 0) { if(EINTR == errno) { continue; } fprintf(stderr, "Read failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } else if(0 == ret) { break; } write(fd, buf, ret); } close(clifd); close(fd); return 0; }
2.2 客户端代码
此函数负责连接至远程服务端,并将日志信息发送至远程服务端!
int main(int argc, const char *argv[]) { int ret = 0, sckid = -1, idx = 0; struct sockaddr_in server; char buf[BUFLEN] = {0}; memset(&server, 0, sizeof(server)); /* Create socket */ sckid = socket(AF_INET, SOCK_STREAM, 0); if(sckid < 0) { fprintf(stderr, "Socket failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Connect to server */ server.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1", &server.sin_addr); server.sin_port = htons(PORT); ret = connect(sckid, (void *)&server, sizeof(server)); if(ret < 0) { fprintf(stderr, "Connect failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Send log information */ for(idx=0; idx<LOOP; idx++) { snprintf(buf, sizeof(buf), "This is just a test![%d]\n", idx); ret = write(sckid, buf, strlen(buf)); if(ret < 0) { if(EINTR == errno) { --idx; continue; } fprintf(stderr, "Write failed! errmsg:[%d]%s\n", errno, strerror(errno)); break; } } close(sckid); return 0; }
2.3 测试结果
撰写100w条日志的测试结果如下图所示:[注:请关注红线区域的系统调用情况]
图1 TCP日志系统测试结果
3. UDP日志系统[异步]
3.1 服务端代码
此模块负责绑定指定端口,并接收UDP客户端发送过来的数据,并将数据写入到指定的日志文件中。[注:在实际的应用过程中,可以引入线程池机制进行完善]
int main(int argc, const char *argv[]) { int ret = 0, sckid = -1, fd = 0, len = 0; char msg[BUFLEN] = {0}; struct sockaddr_in svraddr, fromaddr; /* Create socket */ sckid = socket(AF_INET, SOCK_DGRAM, 0); if(sckid < 0) { fprintf(stderr, "Socket failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Bind port */ bzero(&svraddr, sizeof(svraddr)); svraddr.sin_family = AF_INET; svraddr.sin_addr.s_addr = htonl(INADDR_ANY); svraddr.sin_port = htons(PORT); ret = bind(sckid, (struct sockaddr *)&svraddr, sizeof(svraddr)); if(ret < 0) { fprintf(stderr, "Bind failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } fd = open("test.log", O_CREAT|O_WRONLY|O_APPEND, 0666); if(fd < 0) { fprintf(stderr, "Open failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Receive log information */ while(1) { memset(&fromaddr, 0, sizeof(fromaddr)); len = sizeof(fromaddr); ret = recvfrom(sckid, msg, sizeof(msg), 0, (struct sockaddr *)&fromaddr, &len); if(ret < 0) { if(EINTR == ret) { continue; } fprintf(stderr, "Recvfrom failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } write(fd, msg, ret); } close(sckid); close(fd); return 0; }
3.2 客户端代码
客户端代码主要将日志信息发送到服务端指定端口!
int main(int argc, const char *argv[]) { int ret = 0, sckid = -1, idx = 0; struct sockaddr_in server; char buf[BUFLEN] = {0}; memset(&server, 0, sizeof(server)); /* Create socket */ sckid = socket(AF_INET, SOCK_DGRAM, 0); if(sckid < 0) { fprintf(stderr, "Socket failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Connect to server */ server.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1", &server.sin_addr); server.sin_port = htons(PORT); ret = connect(sckid, (void *)&server, sizeof(server)); if(ret < 0) { fprintf(stderr, "Connect failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Send log information */ for(idx=0; idx<LOOP; idx++) { snprintf(buf, sizeof(buf), "This is just a test![%d]\n", idx); ret = write(sckid, buf, strlen(buf)); if(ret < 0) { if(EINTR == errno) { --idx; continue; } fprintf(stderr, "Write failed! errmsg:[%d]%s\n", errno, strerror(errno)); break; } } close(sckid); return 0; }
3.3 测试结果
撰写100w条日志的测试结果如下图所示:[注:请关注红线区域的系统调用情况]
图2 UDP日志系统测试结果
4. U-TCP日志系统[异步]
4.1 服务端代码
>主函数代码
主函数主要负责绑定指定文件,并等待接收客户端的连接请求,再启动子进程处理与客户端的交互![注:实际应用中可使用进程池或线程池机制进行完善]
int main(int argc, const char *argv[]) { int ret = 0, sckid = -1, clifd = 0, len = 0, flag = 1; struct sockaddr_un svraddr, cliaddr; /* Create socket */ sckid = socket(AF_UNIX, SOCK_STREAM, 0); if(sckid < 0) { fprintf(stderr, "Socket failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } setsockopt(sckid, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); /* Bind port */ bzero(&svraddr, sizeof(svraddr)); svraddr.sun_family = AF_UNIX; snprintf(svraddr.sun_path, sizeof(svraddr.sun_path), "%s", SVRPATH); ret = bind(sckid, (struct sockaddr *)&svraddr, sizeof(svraddr)); if(ret < 0) { fprintf(stderr, "Bind failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } ret = listen(sckid, 20); if(ret < 0) { fprintf(stderr, "Listen failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Send log information */ while(1) { memset(&cliaddr, 0, sizeof(cliaddr)); len = sizeof(cliaddr); clifd = accept(sckid, (struct sockaddr *)&cliaddr, &len); ret = fork(); if(ret < 0) { fprintf(stderr, "Fork failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } else if(0 == ret) { close(sckid); recv_msg(clifd); exit(1); } } close(sckid); return 0; }
>接收代码
此函数被子进程调用,主要负责接收客户端的日志信息,并将信息写入到指定的日志文件中!
int recv_msg(int clifd) { int ret = 0, fd = -1; char buf[1024] = {0}; fd = open("test.log", O_CREAT|O_WRONLY|O_APPEND, 0666); if(fd < 0) { fprintf(stderr, "Open failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } while(1) { memset(buf, 0, sizeof(buf)); ret = read(clifd, buf, sizeof(buf) - 1); if(ret < 0) { if(EINTR == errno) { continue; } fprintf(stderr, "Read failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } else if(0 == ret) { break; } write(fd, buf, ret); } close(clifd); close(fd); return 0; }
4.2 客户端代码
此代码主要负责侦听指定文件,同时将日志信息发送到服务端!
int main(int argc, const char *argv[]) { int ret = 0, sckid = -1, idx = 0, flag = 1; struct sockaddr_un cliaddr, svraddr; char buf[BUFLEN] = {0}; memset(&cliaddr, 0, sizeof(cliaddr)); memset(&svraddr, 0, sizeof(svraddr)); /* Create socket */ sckid = socket(AF_UNIX, SOCK_STREAM, 0); if(sckid < 0) { fprintf(stderr, "Socket failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } setsockopt(sckid, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); /* Bind file */ cliaddr.sun_family = AF_UNIX; snprintf(cliaddr.sun_path, sizeof(cliaddr.sun_path), "%s", CLI_LSN); ret = bind(sckid, (struct sockaddr *)&cliaddr, sizeof(cliaddr)); if(ret < 0) { fprintf(stderr, "Bind failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Connect to server */ svraddr.sun_family = AF_UNIX; snprintf(svraddr.sun_path, sizeof(svraddr.sun_path), "%s", SVR_LSN); ret = connect(sckid, (void *)&svraddr, sizeof(svraddr)); if(ret < 0) { fprintf(stderr, "Connect failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Send log information */ for(idx=0; idx<LOOP; idx++) { snprintf(buf, sizeof(buf), "This is just a test![%d]\n", idx); ret = write(sckid, buf, strlen(buf)); if(ret < 0) { if(EINTR == errno) { --idx; continue; } fprintf(stderr, "Write failed! errmsg:[%d]%s\n", errno, strerror(errno)); break; } } close(sckid); return 0; }
4.3 测试结果
客户端写100W条日志的测试结果如下图所示:[注:请注意红线区域的系统调用情况]
图3 U-TCP日志系统测试结果
5. U-UDP日志系统[异步]
5.1 服务端代码
此代码负责侦听指定文件,同时接受客户端发送过来的数据,再将信息写入指定日志文件中。[注:实际实现过程,可使用线程池进行完善]
int main(int argc, const char *argv[]) { int ret = 0, sckid = -1, len = 0, flag = 1, fd = -1; struct sockaddr_un svraddr, fromaddr; char msg[MSGLEN] = {0}; /* Create socket */ sckid = socket(AF_UNIX, SOCK_DGRAM, 0); if(sckid < 0) { fprintf(stderr, "Socket failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } setsockopt(sckid, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); /* Bind port */ bzero(&svraddr, sizeof(svraddr)); svraddr.sun_family = AF_UNIX; snprintf(svraddr.sun_path, sizeof(svraddr.sun_path), "%s", SVRPATH); ret = bind(sckid, (struct sockaddr *)&svraddr, sizeof(svraddr)); if(ret < 0) { fprintf(stderr, "Bind failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } setsockopt(sckid, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); fd = open("test.log", O_CREAT|O_WRONLY|O_APPEND, 0666); if(fd < 0) { fprintf(stderr, "Open failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Send log information */ while(1) { len = sizeof(fromaddr); memset(&fromaddr, 0, sizeof(fromaddr)); fromaddr.sun_family = AF_UNIX; ret = recvfrom(sckid, msg, sizeof(msg), 0, (struct sockaddr *)&fromaddr, &len); if(ret < 0) { if(EINTR == errno) { continue; } fprintf(stderr, "Recvfrom failed! errmsg:[%d]%s\n", errno, strerror(errno)); break; } write(fd, msg, ret); } close(sckid); close(fd); return 0; }
5.2 客户端代码
客户端代码侦听指定文件后,再将日志信息发送到服务端!
int main(int argc, const char *argv[]) { int ret = 0, sckid = -1, idx = 0, flag = 1; struct sockaddr_un cliaddr, svraddr; char buf[BUFLEN] = {0}; memset(&cliaddr, 0, sizeof(cliaddr)); memset(&svraddr, 0, sizeof(svraddr)); /* Create socket */ sckid = socket(AF_UNIX, SOCK_DGRAM, 0); if(sckid < 0) { fprintf(stderr, "Socket failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } setsockopt(sckid, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); /* Bind file */ cliaddr.sun_family = AF_UNIX; snprintf(cliaddr.sun_path, sizeof(cliaddr.sun_path), "%s", CLI_LSN); ret = bind(sckid, (struct sockaddr *)&cliaddr, sizeof(cliaddr)); if(ret < 0) { fprintf(stderr, "Bind failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Connect to server */ svraddr.sun_family = AF_UNIX; snprintf(svraddr.sun_path, sizeof(svraddr.sun_path), "%s", SVR_LSN); ret = connect(sckid, (void *)&svraddr, sizeof(svraddr)); if(ret < 0) { fprintf(stderr, "Connect failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Send log information */ for(idx=0; idx<LOOP; idx++) { snprintf(buf, sizeof(buf), "This is just a test![%d]\n", idx); ret = write(sckid, buf, strlen(buf)); if(ret < 0) { if(EINTR == errno) { --idx; continue; } fprintf(stderr, "Write failed! errmsg:[%d]%s\n", errno, strerror(errno)); break; } } close(sckid); return 0; }
5.3 测试结果
客户端写100W条日志的测试结果如下图所示:[注:请注意红线区域的系统调用情况]
图4 U-UDP日志系统测试结果
6. 同步日志系统[无锁]
6.1 代码实现
该函数是打开文件后,直接将日志写入指定文件中。[注:此日志系统适合在日志文件不共用的系统中]
int main(int argc, const char *argv[]) { int ret = 0, fd = 0, idx = 0; char buf[BUFLEN] = {0}; fd = open("test.log", O_CREAT|O_APPEND|O_WRONLY, 0666); if(fd < 0) { fprintf(stderr, "Open failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } /* Write log information */ for(idx=0; idx<LOOP; idx++) { snprintf(buf, sizeof(buf), "This is just a test![%d]\n", idx); ret = write(fd, buf, strlen(buf)); if(ret < 0) { if(EINTR == errno) { --idx; continue; } fprintf(stderr, "Write failed! errmsg:[%d]%s\n", errno, strerror(errno)); break; } } close(fd); return 0; }
6.2 测试结果
客户端写100W条日志的测试结果如下图所示:[注:请注意红线区域的系统调用情况]
图5 同步日志系统(无锁)测试结果
7. 同步日志系统[加锁]
7.1 代码实现
该函数打开文件后,再往文件中写入日志信息之前,需要加锁并重新调整文件流的位置![注:此日志系统适合在日志文件共用的系统中]
int main(int argc, const char *argv[]) { int ret = 0, fd = 0, idx = 0; char buf[BUFLEN] = {0}; for(idx=0; idx<LOOP; idx++) { fd = open("test.log", O_CREAT|O_APPEND|O_WRONLY, 0666); if(fd < 0) { fprintf(stderr, "Open failed! errmsg:[%d]%s\n", errno, strerror(errno)); return -1; } snprintf(buf, sizeof(buf), "This is just a test![%d]\n", idx); lockf(fd, F_LOCK, 0); lseek(fd, 0, SEEK_END); ret = write(fd, buf, strlen(buf)); if(ret < 0) { if(EINTR == errno) { --idx; continue; } fprintf(stderr, "Write failed! errmsg:[%d]%s\n", errno, strerror(errno)); break; } close(fd); } return 0; }
7.2 测试结果
客户端写100W条日志的测试结果如下图所示:[注:请注意红线区域的系统调用情况]
图6 同步日志系统(加锁)测试结果
8. 其他日志系统
其他日志系统包括使用共享内存、消息队列等等方式实现的日志系统,因其过程相对较为复杂,在此不做实现!感兴趣的可以自己去实现,并对比一下各自的性能情况!
9. 性能分析
以上系统调用的结果是通过strace -c ./proc-name进行统计的,通过对比可知性能排序如下所示:(依次递减)
01 | 同步日志[无锁] | 1 (参照t0) |
+285% |
02 | U-UPD异步日志 | 1.58 | +143.7% |
03 | UDP异步日志 | 1.70 | +126.5% |
04 | U-TCP异步日志 | 2.10 | +83.3% |
05 | TCP异步日志 | 2.10 | +83.3% |
06 | 同步日志[加锁] | 3.85 | 0% (参照t0) |
表1 性能排序
总结:以上6种日志系统中,同步日志系统(无锁)的性能比其他5种日志系统的性能明显优异,而使用加锁的日志系统性能明显比其他的差很多!
注意:使用共享内存的日志缓存+无锁机制+同步机制+SVR进程的日志系统的性能在同步日志系统[无锁]的基础上提高150%以上,关于此日志系统的设计我将在后续的博文中给出设计思路。
作者: 邹祁峰
2013年10月17日
相关推荐
使用mysqldumpslow就可以相对快速地进行筛选出需要优化的SQL语句,是手工进行数据库系统性能瓶径分析的最佳工具之一。 在使用mysqldumpslow工具之前必须要确保已经开启了慢日志,如何开启慢日志请参考《mysql慢查询...
日志包括系统日志、应用程序日志和安全日志。每条日志都记载着时间戳、主机名、使用者及操作行为等相关的描述,系统运维和开发人员可以...经常分析日志可以了解服务器的负荷,性能安全性,及时分析问题、追查错误根源。
这东西太好,不得不十分,呵呵比较少的资料而且好 MySQL数据库系统监控 JDBC SQL日志记录工具P6spy P6spy的安装、配置(以Jboss3.2为例) DBMonster的安装和配置 DBMonster命令示例-提取Schema 在以源码方式安装的...
当然, 本系统还有很多不足的地方:数据源比较单一,还应该加入日志数据源;而且现在对于应用层协议只实现了HTTP协议分析,以后还可以加入其他协议分析,只有将入侵检测系统与 其他安全工具结合起来,才能构筑起一道...
我在这个日志系统里还加入了按照文件大小轮替,比如默认 100MB 轮替一次,太大了分析起来很吃力,编辑器带不动(我的日志不会太多,所以我自己使用会按照 10MB 来轮替,但是如果你的日志很频繁,轮替大小设置太小就...
二、基于大数据的数据分析系统架构 (一)传统的大数据数据分析架构 传统的大数据数据分析架构,传统的BI数据分析,由于数据量和系统性能不能 满足大数据,所以基于此类的数据分析技术上是使用了大数据的数据分析...
HDFS不管是在存储的性能,稳定性 吞吐量 都是在主流文件系统中很占有优势的 如果感觉HDFS存储还是比较慢,可以采用SSD硬盘等方案。存储模块:搭建和配置HDFS分布式存储系统,并Hbase和MySQL作为备用方案。 ETL模块...
确定用户对系统的使用情况是设计用例具体数据的基础,后面并发用户数据设计、疲劳强度...当用户比较分散、现场调查比较困难时,可以采用对系统日志进行分析的方法,以此作为对用户现场调查信息的补充。 (PS:大多数的
经常分析日志可以了解服务器的负荷,性能安全性,从而及时采取措施纠正错误。通常,日志被分散的储存不同的设备上。如果你管理数十上百台服务器,你还在使用依次登录每台机器的传统方法查阅日志。这样是不是感觉
HDFS不管是在存储的性能,稳定性 吞吐量 都是在主流文件系统中很占有优势的 如果感觉HDFS存储还是比较慢,可以采用SSD硬盘等方案。存储模块:搭建和配置HDFS分布式存储系统,并Hbase和MySQL作为备用方案。 ETL模块...
HDFS不管是在存储的性能,稳定性 吞吐量 都是在主流文件系统中很占有优势的 如果感觉HDFS存储还是比较慢,可以采用SSD硬盘等方案。存储模块:搭建和配置HDFS分布式存储系统,并Hbase和MySQL作为备用方案。 ETL模块...
第2章 系统分析 7 2.1 需求调研 7 2.2 系统可行性分析 7 2.3 设计的基本思想 8 2.4 性能需求 8 2.5 界面需求 9 2.6 系统用户用例图 9 2.7 功能模块需求分析 13 第3章 系统总体框架 14 3.1 系统模型结构 14 3.2 系统...
HDFS不管是在存储的性能,稳定性 吞吐量 都是在主流文件系统中很占有优势的 如果感觉HDFS存储还是比较慢,可以采用SSD硬盘等方案。存储模块:搭建和配置HDFS分布式存储系统,并Hbase和MySQL作为备用方案。 ETL模块...
HDFS不管是在存储的性能,稳定性 吞吐量 都是在主流文件系统中很占有优势的 如果感觉HDFS存储还是比较慢,可以采用SSD硬盘等方案。存储模块:搭建和配置HDFS分布式存储系统,并Hbase和MySQL作为备用方案。 ETL模块...
log只存起来是没有意义的,最关键的是要从日志中发现业务的趋势、系统的性能漏洞等。之前有一个用Java写的分析模块,运行在Tomcat下。实现相当的重量级,添加一个新指标的流程也比较繁琐,而且由于NFS的原因还导致...
10.4 系统性能分析标准和优化原则 10.5 几种典型应用对系统资源使用的特点 10.5.1 以静态内容为主的Web应用 10.5.2 以动态内容为主的Web应用 10.5.3 数据库应用 10.5.4 软件下载应用 10.5.5 流媒体服务...
§10.13 应用系统性能优化原则 123 §10.13.1 分而治之 123 §10.13.2 预分配,预支取,预编译 123 §10.13.3 筛选 124 §10.13.4 大量(bluk),块和批处理 124 §10.13.5 对应用适当的分段 124 §10.13.6 优化目标 ...
第2周 DB2性能优化方法系统:包括经典三招、性能问题分析、使用PAT方法找到性能瓶颈、硬件规划等。 第3周 DB2性能优化:从监控开始,包括监控方法学、操作系统监控、快照监控、管理试图监控、事件监控器、db2pd工具...