使用ICC优化编译Mysql percona 分支(Compile mysql-percona v5.0.87)
生产环境跑的是打了google mysql-patch v4的mysql, 运行效果一直不错. Percona提供的mysql补丁集也不错,
尤其是增加了很多有用的信息,在运行时分析性能瓶颈很有用. Google的v3/v4补丁相对来说就少了一些.
最新的5.0.97b20出来后,我决定替换slave,目的是希望更方便的分析运行期统计信息.
和google v4一样,我使用了新的icc v11.1.x进行了优化编译.
步骤如下:
1. 编译libunwind
CC=icc \
CXX=icpc \
LD=xild \
AR=xiar \
CFLAGS=’-O3 -ipo -no-prec-div -xSSE3 -axSSE4.2,SSE4.1,SSE3,SSE2′ \
CXXFLAGS=’-O3 -ipo -no-prec-div -xSSE3 -axSSE4.2,SSE4.1,SSE3,SSE2′ \
./configure –prefix=/opt/local
make install
2.编译google-perftools-1.4
CC=icc \
CXX=icpc \
LD=xild \
AR=xiar \
CPPFLAGS=” -I/opt/local/include ” \
CXXFLAGS=’-xSSE3 -axSSE4.2,SSE4.1,SSE3,SSE2 -O3 -ip -no-prec-div ‘ \
LDFLAGS=’ -L/opt/local/lib ‘ \
./configure –prefix=/opt/local
make install
3.编译mysql-percona 5.0.87b20
#!/bin/bash
ICC_FLAGS=’-O3 -no-prec-div -ip -unroll2 -restrict -fno-implicit-templates -fno-exceptions -fno-rtti -static-intel -static-libgcc -xSSE3 -axSSE2,SSE3,SSE4.1,SSE4.2′
MYSQL_ROOT=/opt/mysql-percona
BUILD_VERSION=’ICC v11.1.059/Percona v5.0.87-b20′
ICC=icc
ICPC=icpc
build_client() {
CFLAGS=”$ICC_FLAGS” \
CXXFLAGS=”$ICC_FLAGS” \
CPPFLAGS=’-I/opt/local/include’ \
LDFLAGS=’-L/opt/local/lib’ \
LD=xild \
AR=xiar \
CC=$ICC \
CXX=$ICPC \
./configure \
–prefix=$MYSQL_ROOT \
–with-server-suffix=’-cv-mysql’ \
–with-comment=”$BUILD_VERSION” \
–with-collation=utf8_general_ci \
–with-charset=utf8 \
–with-extra-charsets=complex \
–with-client-ldflags=’-all-static’ \
–enable-thread-safe-client \
–enable-assembler \
–with-fast-mutexes \
–with-innodb \
–with-pic \
–enable-assembler \
–enable-local-infile \
–without-server \
–without-ndbcluster \
–without-embedded-server\
–without-example-storage-engine \
–without-archive-storage-engine \
–without-blackhole-storage-engine \
–without-csv-storage-engine \
–without-federated-storage-engine \
–with-zlib-dir=bundled \
–without-debug \
–with-readline
make -j8
make install
}
build_server(){
CFLAGS=”$ICC_FLAGS” \
CXXFLAGS=”$ICC_FLAGS” \
CPPFLAGS=’-I/opt/local/include’ \
LDFLAGS=’-L/opt/local/lib’ \
CC=$ICC \
CXX=$ICPC \
LD=xild \
AR=xiar \
./configure \
–disable-shared \
–prefix=/opt/mysql-percona \
–with-server-suffix=’-cv-mysql’ \
–with-comment=”$BUILD_VERSION” \
–with-collation=utf8_general_ci \
–with-charset=utf8 \
–with-extra-charsets=complex \
–with-mysqld-ldflags=’-all-static -ltcmalloc_minimal’ \
–enable-thread-safe-client \
–enable-assembler \
–with-innodb \
–with-pic \
–with-fast-mutexes \
–enable-assembler \
–enable-local-infile \
–without-bench \
–without-extra-tools \
–without-docs \
–without-man \
–without-ndbcluster \
–without-embedded-server\
–without-example-storage-engine \
–without-archive-storage-engine \
–without-blackhole-storage-engine \
–without-csv-storage-engine \
–without-federated-storage-engine \
–with-zlib-dir=bundled \
–without-debug \
–with-readline
make -j8
install -s -D sql/mysqld $MYSQL_ROOT/libexec/mysqld
}
make clean distclean
build_client
make clean distclean
build_server
client和server是分别编译的,server是static.
修改调优mysql的配置
cat /etc/my.cnf
[mysqld]
# generic configuration options
port = 3306
socket = /tmp/mysql.sock
datadir = /db/data
back_log = 50
max_connections = 500
max_connect_errors = 100
table_cache = 2048
max_allowed_packet = 16M
binlog_cache_size = 1M
max_heap_table_size = 64M
sort_buffer_size = 8M
join_buffer_size = 8M
thread_cache_size = 8
thread_concurrency = 8
query_cache_size = 64M
query_cache_limit = 2M
ft_min_word_len = 4
default_table_type = InnoDB
thread_stack = 192K
transaction_isolation = REPEATABLE-READ
tmp_table_size = 64M
log-bin=mysql-bin
long_query_time = 3
log_long_format
replicate-same-server-id
server-id = 100
binlog-ignore-db=mysql
binlog-ignore-db=test
key_buffer_size = 32M
read_buffer_size = 2M
read_rnd_buffer_size = 16M
bulk_insert_buffer_size = 64M
myisam_sort_buffer_size = 128M
myisam_max_sort_file_size = 10G
myisam_max_extra_sort_file_size = 10G
myisam_repair_threads = 1
myisam_recover
innodb_additional_mem_pool_size = 16M
innodb_buffer_pool_size = 2G
innodb_data_file_path = ibdata1:5G;idbdata2:10G;idbdata3:30G;idbdata4:40G
innodb_data_home_dir = /db/tb
innodb_file_io_threads = 4
innodb_thread_concurrency = 0
innodb_flush_log_at_trx_commit = 1
innodb_log_buffer_size = 8M
innodb_log_file_size = 256M
innodb_log_files_in_group = 3
innodb_log_group_home_dir= /db/tlog
innodb_max_dirty_pages_pct = 80
innodb_flush_method=O_DIRECT
innodb_lock_wait_timeout = 120
auto_increment_increment=2
auto_increment_offset=1
expire_logs_days=3
allow_view_trigger_sp_subquery
#google patch
innodb_adaptive_checkpoint
innodb_adaptive_checkpoint=1
innodb_write_io_threads=4
innodb_io_capacity=200
#percona only
rpl_transaction_enabled=1
rpl_mirror_binlog_enabled
sync-mirror-binlog
#slow log
#sql_log_filename=/db/slowlog/s2.log
#log_slow_queries=/db/slowlog/s2.log
#log_queries_not_using_indexes
几个重要的参数:
innodb_adaptive_checkpoint=1
要开启
innodb_max_dirty_pages_pct
要根据运行时信息进行微调
innodb_io_capacity=200 or 300
这里的数量是raid中磁盘stripe size*100
例如raid10,2*2, 设置为200, 2*3则可设置为300
rpl_transaction_enabled=1
rpl_mirror_binlog_enabled
sync-mirror-binlog
和replication相关.需要手动打补丁
mirror-binlog.patch
update:(当前补丁列表,自己打补丁按此顺序):
show_patches.patch
microslow_innodb.patch
profiling_slow.patch
userstatv2.patch
microsec_process.patch
innodb_io_patches.patch
mysqld_safe_syslog.patch
innodb_locks_held.patch
innodb_show_bp.patch
innodb_check_fragmentation.patch
innodb_io_pattern.patch
innodb_fsync_source.patch
innodb_show_hashed_memory.patch
innodb_dict_size_limit.patch
innodb_extra_rseg.patch
innodb_thread_concurrency_timer_based.patch
innodb_use_sys_malloc.patch
innodb_recovery_patches.patch
innodb_misc_patch.patch
innodb_split_buf_pool_mutex.patch
innodb_rw_lock.patch
mysql-test.patch
Compile gearmand with icc (ICC v11.x编译Gearmand)
系统已安装:
1. tcmalloc (google-perftools-1.4 )
2. libmemcached v0.35(v0.30+)
编译gearmand-0.10:
tar zxvf gearmand-0.10.tar.gz
./compile-gearman.sh
=========gearman.sh=====
make distclean
CC=icc \
CXX=icpc \
CFLAGS=” -O3 -ip -std=gnu99 -no-prec-div -xSSE3 -axSSE4.2,SSE4.1,SSE3 -static-intel -no-gcc” \
CPPFLAGS=’-I/opt/local/include -Wno-error’ \
LDFLAGS=’-L/opt/local/lib’ \
./configure \
–prefix=/opt/gearmand \
–enable-tcmalloc \
–disable-libsqlite3 \
–disable-libdrizzle \
–with-libevent-prefix=/opt/local \
–with-libmemcached-prefix=/opt/local
make install
=======end===
note:
1. 关闭gcc宏定义
2. 打开std gnu99支持
3. -ipo failed
My deployment keynote
1. Mysql(percona branch 5.0.x)
* 主数据库
* Master-Master replication( MMM powered)
2. MongoDb
* GridFS, 分布式文件存储
* Some models(Logging,Tag, etc.)
3. Flare
* cluster
* Session storage(persisten memcahed).
* Gearmand backend persisten storage. (libmemcached plugin).
4. Gearman
* Job server
* Message queues
5. Daemontools
* Power PHP/Perl service script or gearman worker
备忘.
MongoDb Replication
MongoDb的Replication支持:
1. master-slave:
slave可以有多个.
2. Replica Pairs
实际上是一个failover的master-slave模式. 启动时,2个node的mongo会协商,其中1个成为master,另一个为slave. 当master down了,那么slave会自动接管成为master.
不过,这种模式需要driver支持. 需要在driver connect时候
选择pairs 模式.
3. 有限的master-master
可忽略
问题是,我希望是 replica pairs + slave(s) 模式.
不幸的是, 目前版本不支持. mailinglist说是在开发中.
主要的一个限制就是slave的source只能在启动时候指定,
虽然支持多个upstream的source,但是无法中途修改.
如果source改变,需要shutdown然后restart.
此外,一个缺陷是,需要client端链接时指定host.
我理想的模式是使用虚拟ip, 一个是writer,一个是reader.
当某个node down了,则通过arp 转到另一个实际的node的真实ip.
这是writer, 如果是reader,那么可以通过LVS来负载均衡
到不同的节点.
粗粗想一下,实现这个解决方案的难度倒不大. 可以参考mysql的mmm. 等等看,如果未来mongodb没有出类似的方案,可以考虑实现一个.
Compile php+php-fpm with ICC v11.1
打算把生产环境的PHP升级到5.2.11, 于是重新使用ICC编译了PHP-5.2.11+PHP-FPM-0.6.
结果编译时失败,出现以下错误:
fpm_atomic.h(116): catastrophic error: #error directive: unsupported architecture. please write a patch and send it in
#error unsupported architecture. please write a patch and send it in
开始以为是我使用独立安装造成的,不过尝试了integrated安装,问题也一样.
于是检查了fpm_atomic.h 116行:
#else
#error unsupported architecture. please write a patch and send it in
#endif
原来是没有检测当前arch的宏分支. 由于icc的x86_64是定义了__x86_64 而不是__amd64__.
修改了一下:
#elif ( __amd64__ || __amd64 || __x86_64__ )
Patch (php-fpm-icc.patch) :
=====================================
@@ -37,7 +37,7 @@
return res;
}
-#elif ( __amd64__ || __amd64 )
+#elif ( __amd64__ || __amd64 || __x86_64__ )
typedef int64_t atomic_int_t;
typedef uint64_t atomic_uint_t;
======================================
我把patch提交到了php-fpm mailinglist.
附:更新版本的PHP-5.2.11+php-fpm0.6-x86_64 编译过程:
环境准备:
* Icc v11.1.059 EM64T
* MYSQL: mysql-percona分支, ICC优化
* google-perftools-1.4 (我使用tcmallock_minimal来优化PHP)
* php-5.2.11.tar.bz2
* php-fpm-0.6~5.2.11.tar.gz
1. 生成php-fpm patch
tar zxvf php-fpm-0.6~5.2.11.tar.gz
cd php-fpm-0.6-5.2.11
cd fpm
path -p0 < php-fpm-icc.patch ( 这是修正fpm_atomic.h)
cd ../..
./php-fpm-0.6-5.2.11/generate-fpm-patch
这生成一个fpm.patch
2. 准备php源码
tar zxvf php-5.2.11.tar.gz
cd php-5.2.11
patch -p1 < ../fpm.patch
./buildconf –force
3. 编译:
./compile-php.sh
=======compile-php.sh============
#!/bin/bash
make distclean
VERSION=5.2.11
DEST=/opt/php-$VERSION
CFLAGS=’ -O3 -ip -unroll2 -no-prec-div -fp-model source -restrict -static-intel -xSSE2,SSE3,SSE4.1,SSE4.2 -axSSE2,SSE3,SSE4.1,SSE4.2 ‘ \
CXXFLAGS=’ -O3 -ip -unroll2 -no-prec-div -fp-model source -restrict -static-intel -xSSE2,SSE3,SSE4.1,SSE4.2 -axSSE2,SSE3,SSE4.1,SSE4.2 -fno-implicit-templates -fno-exceptions -fno-rtti’ \
LDFLAGS=’ -ltcmalloc_minimal -L/opt/local/lib’ \
CC=icc \
CXX=icpc \
LD=xild \
AR=xiar \
./configure \
–prefix=$DEST \
–with-libdir=lib64 \
–with-fpm \
–enable-force-cgi-redirect \
–enable-fastcgi \
–enable-mbstring \
–enable-mbregex \
–enable-pcntl \
–enable-exif \
–enable-sockets \
–enable-sysvsem \
–enable-sysvshm \
–enable-inline-optimization \
–enable-zend-multibyte \
–disable-ipv6 \
–disable-debug \
–with-mysql=/opt/mysql \
–with-mysqli \
–with-config-file-path=$DEST/etc \
–with-config-file-scan-dir=$DEST/etc/php.d \
–with-zlib \
–with-curl \
–with-gettext \
–with-jpeg-dir=/usr \
–with-png-dir=/usr \
–with-freetype-dir=/usr \
–with-iconv \
–with-pcre-regex
make install
[ -e $DEST/etc/php.ini ] || cp -u php.ini-recommend $DEST/etc/php.ini
我习惯把所有优化过的lib都安装到/opt/local,因此需要修改成你自己的配置(通常是/usr/local)
备注:
1. 以上脚本是将php-fpm使用integrated方式安装. 省事也是官方推荐的方式.
2. 若使用mysqlnd,可以修改为:
–with-mysql=mysqlnd \
–with-mysqli=mysqlnd \
不过由于mysqlnd只在官方的5.3中存在,除了我自己的特制php source,估计没人会在5.2.x使用到mysqlnd吧.
闲谈MongoDb+GridFS+Nginx
MongoDb果然是个好东西. 我在最近的一个项目实践中, 实验性的用到了这个东西.
在测试中,对于GridFS相当满意. 首先, 和传统的MogileFS不同, gridfs可以和其它的meta数据部署在同一个
db中,默认的会为gridfs的collection分别创建fs.files和fs.chunks.
当存储一个文件时,可以附加存入任意的附加信息,因为这些信息实际上也是一个普通的collection.
这个特性给我们省了好多的事情. 以前,如果要存储一个附件,通常的做法是,在主数据库中存放文件的属性,并且记录
文件的path.当查询某个文件时,需要首先查询数据库,获得path,然后从存储系统中获得相应的文件.
在使用gridfs时,则非常简单, 我们可以直接将这些信息直接存储到文件中. 比如下面的PHP代码,存储上传的文件到gridfs:
public function store($file,$attrs=array()) {
if (!is_file($file)) {
throw new CZone_Core_Service_Exception("File:$file not exists");
}
$defaults = array(
'content_type'=> null,
'art_id'=>-1,
'state' => self::STORE_STATE_TMP,
'created_on' => time(),
'is_thumb'=> false,
'md5'=> md5_file($file)
);
$asset_attrs = $attrs+$defaults;
if (!isset($asset_attrs['content_type'])) {
$asset_attrs['content_type'] = Doggy_Util_File::mime_content_type($file);
}
$fs = $this->db->get_fs();
return $fs->storeFile($file,$asset_attrs);
}
调用store时,可以附件任意属性数组. 之后, 检索文件时则可以根据这些属性来查找:
public function fetch_by_id($id) {
return $this->fetch(array('_id'=>$id));
}
public function delete_by_id($id) {
return $this->delete(array('_id'=>$id));
}
public function delete_art_assets($art_id) {
return $this->delete(array('art_id'=>$art_id));
}
public function delete_asset($asset_id) {
return $this->delete_by_id($asset_id) && $this->delete_asset_thumbs($asset_id);
}
public function delete($options) {
if (isset($options['_id'])) {
$options['_id'] = Doggy_Mongo_Db::id($options['_id']);
}
$fs = $this->db->get_fs();
return $fs->remove($options);
}
public function find_all($query=array(),$fields=array()) {
return $this->db->fs_find($query,$fields);
}
使用gridfs,可以把原先复杂的操作变得相当简单, 真正实现了mogodb设计者的想法,
数据库为什么不能做文件系统?
在实践中,我发现GridFS和之前研究的MogileFS一些基本方式其实是相通的. 只不过, mogilefs的存储节点是
使用了简化版本的DAV 而已.
从这个角度,完全可以设计一个基于mongo gridfs的mogilefs.
至于性能, 从我的体会来说, 还不错. 毕竟我的项目的目前看存储仅限于TB级别.
不过在生产环境中,国外有用于存储视频流的.
GridFS的一个优点是可以存储上百万的文件而无需担心扩容性.
通过同步复制,可以解决分布式文件的备份问题.
目前,mongo支持主-从和Replica Pairs以及受限的Master-Master Replication.
比较实用的还是前2种.
通过ARP-ping可以实现一个双机热备切换,类似我正在用的mysql的mmm.
在实验过后,感觉使用mongo是非常轻松. 很轻松就解决了高并发中经常会遇到的问题,
比如实时的日志处理,实时的统计,更新某个字段.
通过使用mongo的capped collection,可以实现cache, message queue等特性,无需附加成本.
还有share session.
部署
gridfs的部署的选择方案不多,大概有以下几种:
1. 通过mongo client 的script, 比如PHP.
优点是简单,缺点是每次都要读取mongo数据库. 虽然mongo的性能不错,但是似乎总是不忍.
另外,像PHP的DRIVER并不支持HTTP RANGE header,这样就无法支持断点续传.
2.使用Nginx module
http://github.com/mdirolf/nginx-gridfs
这是gridfs的nginx module. 可以通过nginx直接访问读取mongo gridfs中的文件.
和nginx对应的mogilefs module类似.
优点: 由于直接通过nginx,速度是最快的.
缺点: 只能通过file_path来查找,目前不支持_id来查找.因此必须在file_path上建立索引.
优化方案:
我自己构想了以下的优化方案:
1. squid/varnish+script-backend
在nginx前端加上一个squid或者varnish作为反向加速. 如果没有则通过 PHP脚本来获取.
应用场景: 特别适合读取频繁的文件,比如用户的头像,热门图片,缩略图等. 不适合大文件.
缺点: 文件的过期必须正确设置. 此外配置好varnish或者squid
2. 基于proxy_store或fastcgi-cache, try_files
这种方案的应用场景同1, 但都是使用nginx的相应模块即可实现.
通过对fastcgi/proxy进行cache或store,就可以实现文件按需存储.
当使用proxy_store时,当后端文件变动时,需要purge这些文件.实现起来不难.
对于大文件,我觉得性价比比较高的一个方案是:
使用Perl或者PHP写一个脚本作为fastcgi运行. 前端用nginx进行负载均衡.
如果使用Pelr则当前driver支持随机读取,支持断点续传. 用PHP则需要做个简单处理,
手动判断HEADER,并计算出offset,然后再读取相应的字节流.
注意,如果是用PHP,则最好的方案是单独编译一个PHP,仅保留”最基本”的特性.
这样,可以节省很多的资源占用,稳定性和速度也比较好.我建议的保留的特性有:
json+mongo+spl.
PHP driver要比Perl更为成熟,虽然二者核心开发者都是一个人. Perl目前还是beta,也没有特别
广泛的使用,但据说由于大部分使用的是PHP的C代码,所以还是非常可靠的.
其他一些信息:
1.通过runcommand可以直接在mongodb端运行处理脚本. 比如像mapreduce,或者一些需要读取数据然后进行处理的.
这些command则是使用javascript方式来编写的,很容易. 好处就是避免了数据在服务端和客户端之间的读取和传输,
提高效率.
2. sharding
sharding在目前开发版中已经具备,但还不成熟. 但是可以自己实现sharding比较好.因为目前的sharding还是比较硬性的.
3.灵活使用magic操作符和upsert,比如$inc,$all,$in 等等
这些轻松解决一些麻烦的操作.
3.其他的复制方案
对于文件系统, 其实可以通过一个脚本来定期将文件复制到其他的节点. 实现类似mogilefs的功能.
我对此比较有兴趣.