2012年12月31日星期一

MYSQL Tuning Tips

最近发现产品环境buffer pool争用比较高, 且OS Top命令显示loading很高的时候, CPU使用率其实很低, IO Wait很高.
调整了几个个参数:
innodb_buffer_pool_instances
该参数在设置了大的buffer pool的时候设置, 可以有效的降低由于管理大内存带来的资源争用.
官方建议在buffer pool大于1G的时候使用, 当前环境buffer pool为4G, 我将该参数设置为了4

innodb_read_io_threads
innodb_write_io_threads
这两个参数原来为4, 我修改为了8.

关于innodb_adaptive_hash_index

最近发现生产环境的CPU loading经常上升的很高, 查看innodb status的SEMAPHORES发现其中有一项争用很高,
查看对应的源码位置, 发现跟adaptive hash index有关.
于是将该参数disable, 效果很明显, loading再也没有升高过.

看来该参数果然如官方文档所说, 仅在某些情况下能够改进performance, 感觉应该在写比较频繁的系统, 该参数不太适合, 因为需要频繁的刷新index.

2012年12月27日星期四

innodb_buffer_pool_instances参数

innodb_buffer_pool_instances参数的设置可以减少innodb buffer pool的争用.

innodb_buffer_pool_instances仅在buffer pool空间大于1G的时候起作用, 官方建议两个参数配合使用,
保证每个buffer pool instance空间至少1G.

linux中MYSQL使用痕迹

今天偶然机会看到在/root目录下面有一个.mysql_history的文件, 打开一看, 其中包含了所有在mysql命令行中的操作记录.

Google了一下, 说mysql做这个记录是内核中编译好的, 可以通过以下方式来影响:

1. 软链接到空文件, 使其不记录
rm ~/.mysql_history
ln -s /dev/null ~/.mysql_history
禁用该记录以后, 可以通过上一篇中所说的tee来临时记录操作

2. 修改该文件路径
可以通过修改环境变量来达到修改该文件路径的目的
export MYSQL_HISTFILE=/home/mysql/.mydb_history

2012年12月26日星期三

MYSQL命令行妙用(linux & Unix)

1. pager, 可以使用pager命令使输出更加人性化
第一种使用就是利用pager来使用操作系统的more命令
mysql> pager more
PAGER set to 'more'
mysql> select title from sakila.film;
+-----------------------------+
| title |
+-----------------------------+
| ACADEMY DINOSAUR |
| ACE GOLDFINGER |
| ADAPTATION HOLES |
| AFFAIR PREJUDICE |
| AFRICAN EGG |
| AGENT TRUMAN |
| AIRPLANE SIERRA |
| AIRPORT POLLOCK |
| ALABAMA DEVIL |
| ALADDIN CALENDAR |
| ALAMO VIDEOTAPE |
| ALASKA PHANTOM |
| ALI FOREVER |
| ALICE FANTASIA |
| ALIEN CENTER |
| ALLEY EVOLUTION |
| ALONE TRIP |
| ALTER VICTORY |
| AMADEUS HOLY |
--Plus--
可以看到现在一次只输入一屏结果集.
第二种情况就是利用grep来查找过滤结果中的某一行:
mysql> pager grep sequence
PAGER set to 'grep sequence'
mysql> show engine innodb status\Gselect sleep(60);show engine innodb status\G
Log sequence number 380166807992
1 row in set (0.41 sec)

1 row in set (1 min 0.00 sec)

Log sequence number 380170274979
1 row in set (0.00 sec)
使用完成, 你需要重设pager
mysql> pager
Default pager wasn't set, using stdout.

2. edit
在任何时候, 在mysql命令行终端输入edit, 就会利用vi来编辑上一次执行的mysql命令,
在vi界面中编写完毕SQL语句后输入"\G"或者";"来执行.

3. tee
可以使用tee来记录mysql终端的所有输入输出:
mysql> tee queries.log
Logging to file 'queries.log'
mysql> use sakila
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> select count(*) from sakila;
ERROR 1146 (42S02): Table 'sakila.sakila' doesn't exist
mysql> select count(*) from film;
+----------+
| count(*) |
+----------+
| 1000 |
+----------+
1 row in set (0.00 sec)

mysql> exit
然后你就可以在queries.log中查看到刚才执行的SQL语句以及输出的结果集了.

2012年12月19日星期三

MYSQL Monitor Tips

最近用MySQL Enterprise Monitor监控公司多个明mysql服务器,
这些环境很多都是从一个production库clone出来的, 建立agent监控的时候经常出现问题, 要么就是识别不到,
要么就是识别错误.
研究之下发现mysql的mysql库下有个inventory表, 这个表中有uuid以及host id两个条目,
clone的环境经常这两个条目值都一样, 导致异常. 切忌在建立agent之前将这个表truncate掉.

2012年12月14日星期五

使用 rsync 同步[转]


使用 rsync 同步

内容提要

  1. 熟悉 rsync 的功能及其特点
  2. 掌握 rsync 语法及常用选项的功能
  3. 掌握 rsync 命令的三种基本使用方法
  4. 掌握如何筛选 rsync 的传输目标
  5. 掌握使用 rsync 进行镜像和增量备份的方法

rsync 简介

rsync(remote synchronize)是一个远程数据同步工具,可通过 LAN/WAN 快速同步多台主机之间的文件。也可以使用 rsync 同步本地硬盘中的不同目录。
rsync 是用于替代 rcp 的一个工具,rsync 使用所谓的 rsync算法 进行数据同步,这种算法只传送两个文件的不同部分,而不是每次都整份传送,因此速度相当快。 您可以参考 How Rsync Works A Practical Overview 进一步了解 rsync 的运作机制。
rsync 的初始作者是 Andrew Tridgell 和 Paul Mackerras,目前由 http://rsync.samba.org 维护。
rsync 支持大多数的类 Unix 系统,无论是 Linux、Solaris 还是 BSD上 都经过了良好的测试。 CentOS系统默认就安装了 rsync 软件包。 此外,在 windows 平台下也有相应的版本,如 cwrsync 和DeltaCopy 等。
rsync 具有如下的基本特性:
  1. 可以镜像保存整个目录树和文件系统
  2. 可以很容易做到保持原来文件的权限、时间、软硬链接等
  3. 无须特殊权限即可安装
  4. 优化的流程,文件传输效率高
  5. 可以使用 rsh、ssh 方式来传输文件,当然也可以通过直接的 socket 连接
  6. 支持匿名传输,以方便进行网站镜象
在使用 rsync 进行远程同步时,可以使用两种方式:远程 Shell 方式(建议使用 ssh,用户验证由 ssh 负责)和 C/S 方式(即客户连接远程 rsync 服务器,用户验证由 rsync 服务器负责)。
无论本地同步目录还是远程同步数据,首次运行时将会把全部文件拷贝一次,以后再运行时将只拷贝有变化的文件(对于新文件)或文件的变化部分(对于原有文件)。
本节重点介绍 rsync 客户命令的使用,有关 rsync 服务器的配置和使用请参见下节
rsync 在首次复制时没有速度优势,速度不如 tar,因此当数据量很大时您可以考虑先使用 tar 进行首次复制,然后再使用 rsync 进行数据同步。

镜像、备份和归档

实施备份的两种情况:
  • 需保留备份历史归档:在备份时保留历史的备份归档,是为了在系统出现错误后能恢复到从前正确的状态。这可以使用完全备份和增量备份来完成。
    • 可以使用 tar 命令保存归档文件。
    • 为了提高备份效率,也可以使用 rsync 结合 tar 来完成。
  • 无需保留备份历史归档:若无需从历史备份恢复到正确状态,则只备份系统最“新鲜”的状态即可。这可以简单地使用 rsync 同步来完成。此时通常称为镜像。镜像可以分为两种:
    • 被镜像的目录在各个主机上保持相同的位置。此时一般是为了实施负载均衡而对多个主机进行同步镜像。例如:将主机 A 的 /srv/www 目录同步到主机 B 的 /srv/www 目录等。
    • 被镜像的目录在各个主机上不保持相同的位置。例如:主机 A 和主机 B 都运行着各自的业务,同时又互为镜像备份。此时主机 A 的 /srv/www 目录同步到主机 B 的 /backups/hosta/www 目录;主机 B 的 /srv/www 目录同步到主机 A 的 /backups/hostb/www 目录等。

rsync 命令

rsync 是一个功能非常强大的工具,其命令也有很多功能选项。rsync 的命令格式为:
1)本地使用:
rsync [OPTION...] SRC... [DEST]

2)通过远程 Shell 使用:
拉: rsync [OPTION...] [USER@]HOST:SRC... [DEST]
推: rsync [OPTION...] SRC... [USER@]HOST:DEST

3)访问 rsync 服务器:
拉: rsync [OPTION...] [USER@]HOST::SRC... [DEST]
推: rsync [OPTION...] SRC... [USER@]HOST::DEST
拉: rsync [OPTION...] rsync://[USER@]HOST[:PORT]/SRC... [DEST]
推: rsync [OPTION...] SRC... rsync://[USER@]HOST[:PORT]/DEST
其中:
  • SRC: 是要复制的源位置
  • DEST: 是复制目标位置
  • 若本地登录用户与远程主机上的用户一致,可以省略 USER@
  • 使用远程 shell 同步时,主机名与资源之间使用单个冒号“:”作为分隔符
  • 使用 rsync 服务器同步时,主机名与资源之间使用两个冒号“::”作为分隔符
  • 当访问 rsync 服务器时也可以使用 rsync:// URL
  • “拉”复制是指从远程主机复制文件到本地主机
  • “推”复制是指从本地主机复制文件到远程主机
  • 当进行“拉”复制时,若指定一个 SRC 且省略 DEST,则只列出资源而不进行复制
下面列出常用选项:
选项说明
-a, ––archive归档模式,表示以递归方式传输文件,并保持所有文件属性,等价于 -rlptgoD (注意不包括 -H)
-r, ––recursive对子目录以递归模式处理
-l, ––links保持符号链接文件
-H, ––hard-links保持硬链接文件
-p, ––perms保持文件权限
-t, ––times保持文件时间信息
-g, ––group保持文件属组信息
-o, ––owner保持文件属主信息 (super-user only)
-D保持设备文件和特殊文件 (super-user only)
-z, ––compress在传输文件时进行压缩处理
––exclude=PATTERN指定排除一个不需要传输的文件匹配模式
––exclude-from=FILE从 FILE 中读取排除规则
––include=PATTERN指定需要传输的文件匹配模式
––include-from=FILE从 FILE 中读取包含规则
––copy-unsafe-links拷贝指向SRC路径目录树以外的链接文件
––safe-links忽略指向SRC路径目录树以外的链接文件(默认)
––existing仅仅更新那些已经存在于接收端的文件,而不备份那些新创建的文件
––ignore-existing忽略那些已经存在于接收端的文件,仅备份那些新创建的文件
-b, ––backup当有变化时,对目标目录中的旧版文件进行备份
––backup-dir=DIR与 -b 结合使用,将备份的文件存到 DIR 目录中
––link-dest=DIR当文件未改变时基于 DIR 创建硬链接文件
––delete删除那些接收端还有而发送端已经不存在的文件
––delete-before接收者在传输之前进行删除操作 (默认)
––delete-during接收者在传输过程中进行删除操作
––delete-after接收者在传输之后进行删除操作
––delete-excluded在接收方同时删除被排除的文件
-e, ––rsh=COMMAND指定替代 rsh 的 shell 程序
––ignore-errors即使出现 I/O 错误也进行删除
––partial保留那些因故没有完全传输的文件,以是加快随后的再次传输
––progress在传输时显示传输过程
-P等价于 ––partial ––progress
––delay-updates将正在更新的文件先保存到一个临时目录(默认为 “.~tmp~”),待传输完毕再更新目标文件
-v, ––verbose详细输出模式
-q, ––quiet精简输出模式
-h, ––human-readable输出文件大小使用易读的单位(如,K,M等)
-n, ––dry-run显示哪些文件将被传输
––list-only仅仅列出文件而不进行复制
––rsyncpath=PROGRAM指定远程服务器上的 rsync 命令所在路径
––password-file=FILE从 FILE 中读取口令,以避免在终端上输入口令,通常在 cron 中连接 rsync 服务器时使用
-4, ––ipv4使用 IPv4
-6, ––ipv6使用 IPv6
––version打印版本信息
––help显示帮助信息
  • 若使用普通用户身份运行 rsync 命令,同步后的文件的属主将改变为这个普通用户身份。
  • 若使用超级用户身份运行 rsync 命令,同步后的文件的属主将保持原来的用户身份。

rsync 的基本使用

在本地磁盘同步数据

# rsync -a --delete /home /backups
# rsync -a --delete /home/ /backups/home.0
在指定复制源时,路径是否有最后的 “/” 有不同的含义,例如:
  • /home : 表示将整个 /home 目录复制到目标目录
  • /home/ : 表示将 /home 目录中的所有内容复制到目标目录

使用基于 ssh 的 rsync 远程同步数据

  1. 同步静态主机表文件
# 执行“推”复制同步(centos5 是可解析的远程主机名)
[root@soho ~]# rsync /etc/hosts centos5:/etc/hosts

# 执行“拉”复制同步(soho 是可解析的远程主机名)
[root@centos5 ~]# rsync soho:/etc/hosts /etc/hosts
  1. 同步用户的环境文件
# 执行“推”复制同步
[osmond@soho ~]$ rsync ~/.bash* centos5:

# 执行“拉”复制同步
[osmond@cnetos5 ~]$ rsync soho:~/.bash* .
  1. 同步站点根目录
# 执行“推”复制同步
[osmond@soho ~]$ rsync -avz --delete /var/www root@192.168.0.101:/var/www

# 执行“拉”复制同步
[osmond@cnetos5 ~]$ rsync -avz --delete root@192.168.0.55:/var/www /var/www
  • 使用基于 ssh 的 rsync 同步数据可以使用 -e ssh 参数,当前的 CentOS 默认指定使用 ssh 作为远程Shell。若您在其他系统上执行 rsync 命令,为确保使用 ssh 作为远程 Shell,请添加 -e ssh 参数。
  • 通常 rsync 命令在后台以 cron 任务形式执行,为了避免从终端上输入口令需要设置 ssh。ssh 的设置方法请参考 安全登录守护进程。

使用 rsync 从远程 rsync 服务器同步数据

下面以镜像 CentOS 和 Ubuntu 的软件库为例来说明。
您可以到如下站点查找离自己最近的提供 rsync 服务的镜像站点
然后执行类似如下命令:
rsync -aqzH --delete --delay-updates \
rsync://mirror.centos.net.cn/centos /var/www/mirror/centos
rsync -azH --progress --delete --delay-updates \
rsync://ubuntu.org.cn/ubuntu /var/www/mirror/ubuntu/
rsync -azH --progress --delete --delay-updates \
rsync://ubuntu.org.cn/ubuntu-cn /var/www/mirror/ubuntu-cn/
为了每天不断更新,可以安排一个 cron 任务:
# crontab -e
# mirror centos at 0:10AM everyday
10 0 * * * rsync -aqzH --delete --delay-updates rsync://mirror.centos.net.cn/centos /var/www/mirror/centos/
# mirror ubuntu at 2:10AM everyday
10 2 * * * rsync -azH --progress --delete --delay-updates rsync://ubuntu.org.cn/ubuntu /var/www/mirror/ubuntu/
# mirror ubuntu-cn at 4:10AM everyday
10 4 * * * rsync -azH --progress --delete --delay-updates rsync://ubuntu.org.cn/ubuntu-cn /var/www/mirror/ubuntu-cn/
如果您安装了自己的匿名 rsync 服务器请相应地更改 rsync URL。有关如何配置匿名 rsync 服务器的内容请参见下节。

筛选 rsync 的传输目标

使用 --exclude/--include 选项

可以使用 ––exclude 选项排除源目录中要传输的文件;同样地,也可以使用 ––include 选项指定要传输的文件。
例如:下面的 rsync 命令将 192.168.0.101 主机上的 /www 目录(不包含 /www/logs 和 /www/conf子目录)复制到本地的 /backup/www/ 。
# rsync -vzrtopg --delete --exclude "logs/" --exclude "conf/" --progress \
backup@192.168.0.101:/www/ /backup/www/
又如:下面的 rsync 命令仅复制目录结构而忽略掉目录中的文件。
# rsync -av --include '*/' --exclude '*' \
backup@192.168.0.101:/www/ /backup/www-tree/
选项 ––include 和 ––exclude 都不能使用间隔符。例如:
--exclude "logs/" --exclude "conf/"
不能写成
--exclude "logs/ conf/"

使用 --exclude-from/--include-from 选项

当 include/exclude 的规则较复杂时,可以将规则写入规则文件。使用规则文件可以灵活地选择传输哪些文件(include)以及忽略哪些文件(exclude)。
  • 若文件/目录在剔除列表中,则忽略传输
  • 若文件/目录在包含列表中,则传输之
  • 若文件/目录未被提及,也传输之
在 rsync 的命令行中使用 ––exclude-from=FILE 或 ––include-from=FILE 读取规则文件。
规则文件 FILE 的书写约定:
  • 每行书写一条规则 RULE
  • 以 # 或 ; 开始的行为注释行
包含(include)和排除(exclude)规则的语法如下:
  • include PATTERN 或简写为 + PATTERN
  • exclude PATTERN 或简写为 - PATTERN
PATTERN 的书写规则如下:
  • 以 / 开头:匹配被传输的跟路径上的文件或目录
  • 以 / 结尾:匹配目录而非普通文件、链接文件或设备文件
  • 使用通配符
  • *:匹配非空目录或文件(遇到 / 截止)
  • **:匹配任何路径(包含 / )
  • ?:匹配除了 / 的任意单个字符
  • [:匹配字符集中的任意一个字符,如 [a-z] 或 [[:alpha:]]
  • 可以使用转义字符 \ 将上述通配符还原为字符本身含义
下面给出几个使用规则的例子:
例1:
# 不传输所有后缀为 .o 的文件
- *.o

# 不传输传输根目录下名为 foo 的文件或目录
- /foo

# 不传输名为 foo 的目录
- foo/

# 不传输 /foo 目录下的名为 bar 的文件或目录
- /foo/bar
例2:
# 传输所有目录和C语言源文件并禁止传输其他文件
+ */
+ *.c
- *
例3:
# 仅传输 foo 目录和其下的 bar.c 文件
+ foo/
+ foo/bar.c
- *
将规则写入规则文件之后,如何在命令行上使用它呢?下面给出一个例子:
首先将下面的规则存入名为 www-rsync-rules 的文件
# 不传输 logs 目录
- logs/

# 不传输后缀为 .tmp 的文件
- *.tmp

# 传输 Apache 虚拟主机文档目录(/*/ 匹配域名)
+ /srv/www/
+ /srv/www/*/
+ /srv/www/*/htdocs/
+ /srv/www/*/htdocs/**

# 传输每个用户的 public_html 目录(/*/ 匹配用户名)
+ /home/
+ /home/*/
+ /home/*/public_html/
+ /home/*/public_html/**
# 禁止传输其他
- *
然后即可使用类似如下的 rsync 命令:
rsync -av --delete --exclude-from=www-rsync-rules / remotehost:/dest/dir

rsync 应用示例

使用 rsync 镜像

使用 rsync 对目录做镜像实际上就是做无历史归档的完全备份。下面给出一个镜像远程 Web 站点例子。
笔者在 dreamhost 上维护了3个 Dokuwiki 站点。为了备份这3个站点笔者使用 rsync 进行镜像。远程站点的目录结构如下:
~
|-- sinosmond.com
| `-- dokuwiki
|-- smartraining.cn
| `-- dokuwiki
`-- symfony-project.cn
`-- dokuwiki
每个 Dokuwiki 的目录结构如下:
dokuwiki
|-- bin
|-- inc
|-- conf --- 存放配置文件的目录
| |-- acl.auth.php --- 访问控制配置文件 ★
| |-- local.php --- 本地配置文件 ★
| |-- users.auth.php --- 用户口令文件 ★
| `-- ………………
|-- data --- 存放数据的目录
| |-- attic --- 存放WIKI版本信息 ★
| |-- cache --- 存放数据缓存
| |-- index --- 存放站内索引
| |-- locks --- 存放编辑页面时的锁定文件
| |-- media --- 存放图片等 ★
| |-- meta --- 存放 meta 以便系统读取这些信息生成页面 ★
| `-- pages --- 存放 wiki 页面 ★
`-- lib
|-- plugins --- 存放插件的目录 ☆
|-- tpl --- 存放模版的目录 ☆
`-- ………………
为了减少网络流量,只同步标有 ★ 的目录或文件。若在站点运行过程中新安装了插件或更换了模板,也应该同步标有 ☆ 的目录。为此编写如下的规则文件 /root/bin/backup/dw-exclude.txt:
- dokuwiki/bin/
- dokuwiki/inc/
- dokuwiki/data/cache/
- dokuwiki/data/locks/
- dokuwiki/data/index/
+ dokuwiki/conf/acl.auth.php
+ dokuwiki/conf/local.php
+ dokuwiki/conf/users.auth.php
- dokuwiki/conf/*
+ dokuwiki/lib/plugins/

# 不同步系统默认安装的插件
- dokuwiki/lib/plugins/acl/
- dokuwiki/lib/plugins/config/
- dokuwiki/lib/plugins/importoldchangelog/
- dokuwiki/lib/plugins/importoldindex/
- dokuwiki/lib/plugins/info/
- dokuwiki/lib/plugins/plugin/
- dokuwiki/lib/plugins/revert/
- dokuwiki/lib/plugins/usermanager/
- dokuwiki/lib/plugins/action.php
- dokuwiki/lib/plugins/admin.php
- dokuwiki/lib/plugins/syntax.php
+ dokuwiki/lib/tpl

# 不同步系统默认安装的模板
- dokuwiki/lib/tpl/default/
- dokuwiki/lib/*
- dokuwiki/COPYING
- dokuwiki/doku.php
- dokuwiki/feed.php
- dokuwiki/index.php
- dokuwiki/install*
- dokuwiki/README
- dokuwiki/VERSION
下面是同步脚本 /root/bin/backup/rsync-dw.sh
#!/bin/bash
#####################################
# mirror dokuwiki website
# $1 --- domain (ex: smartraining.cn)
# $2 --- full or update
#####################################
# declare some variable
RmtUser=osmond
RmtIP=208.113.163.110
RmtPath=$1/dokuwiki
BackupRoot=/backups/$1
Excludes="--exclude-from=/root/bin/backup/dw-exclude.txt"

# use rsync for mirror
if [ "$2" == "full" ]
then

[ -d /backups/$1 ] || mkdir -p /backups/$1
excludesfile="/tmp/first-excludes"
cat > ${excludesfile} << EOF
+ dokuwiki/data/cache/_dummy
- dokuwiki/data/cache/*
+ dokuwiki/data/locks/_dummy
- dokuwiki/data/locks/*
+ dokuwiki/data/index/_dummy
- dokuwiki/data/index/*
EOF
/usr/bin/rsync -avzP --exclude-from=${excludesfile} \
$RmtUser@$RmtIP:$RmtPath $BackupRoot

else
/usr/bin/rsync -avzP --delete $Excludes \
$RmtUser@$RmtIP:$RmtPath $BackupRoot

fi
首次备份可以使用类似如下的命令(为了在本地保留一个完整复本):
# /root/bin/backup/rsync-dw.sh smartraining.cn full
# /root/bin/backup/rsync-dw.sh sinosmond.com full
# /root/bin/backup/rsync-dw.sh symfony-project.cn full
可以安排 cron 任务以便日后更新:
# crontab -e
05 1 * * * /root/bin/backup/rsync-dw.sh smartraining.cn
25 1 * * * /root/bin/backup/rsync-dw.sh sinosmond.com
45 1 * * * /root/bin/backup/rsync-dw.sh symfony-project.cn

普通型增量备份

使用 rsync 可以做增量备份。rsync 提供了 -b ––backup-dir 选项,使用这个选项可以将有变化的文件进行更新同时将其旧版本保存在指定的目录中,从而实现增量备份。 下面是对 /home 进行增量备份的步骤说明:
# 第0次备份
# 首先复制 /home 目录的内容到备份目录 /backups/daily/home.0,
# rsync -a /home/ /backups/daily/home.0
# /backups/daily/home.0 总是同步到最新的状态,可以每隔一段时间(如一周)
# 对其内容进行打包压缩生成归档文件(完全备份)存在 /backups/archive/。

# 第1次备份(此为核心操作)
# 将 /home 目录的内容同步到目录 /backups/daily/home.0,
# 并将有变化的文件的旧版本保存到 /backups/daily/home.1,
# 若每天执行一次,则目录 /backups/daily/home.1 保存了有变化文件一天前的状态。
# rsync -a --delete -b --backup-dir=/backups/daily/home.1 /home/ /backups/daily/home.0

# 第2次备份
# 将备份目录 /backups/daily/home.1 更名为 /backups/daily/home.2
# mv /backups/daily/home.1 /backups/daily/home.2
# 执行第1次备份的核心操作

# 第n次备份
# 将早先的备份目录 /backups/daily/home.n 到 /backups/daily/home.1
# 依次更名为 /backups/daily/home.(n+1) 到 /backups/daily/home.2
# 执行第1次备份的核心操作
下面给出一个增量备份示例脚本。
#!/bin/bash
#========================
# 您可以安排 cron 任务执行本脚本
# > crontab -e
#
# daily : 1 1 * * * /path/to/script/rsync-backup.sh
#========================
mydate="`date '+%Y%m%d.%H%M'`"

# Define rmt location
RmtUser=root
RmtHost=192.168.0.55
RmtPath=/home/
BackupSource="${RmtUser}@${RmtHost}:${RmtPath}"
#BackupSource="/home/"             # 若进行本地备份则用本地路径替换上面的行
# Define location of backup
BackupRoot="/backups/$RmtHost/"
# BackupRoot="/backups/localhost/" # 若进行本地备份则用本地路径替换上面的行
LogFile="${BackupRoot}/backup.log"
ExcludeList="/root/backup/backup-exclude-list.txt"
BackupName='home'
BackupNum="7"                      # 指定保留多少个增量备份(适用于每周生成归档文件)
#BackupNum="31"                    # 指定保留多少个增量备份(适用于每月生成归档文件)

# 定义函数检查目录 $1 是否存在,若不存在创建之
checkDir() {
    if [ ! -d "${BackupRoot}/$1" ] ; then
        mkdir -p "${BackupRoot}/$1"
    fi
}
# 定义函数实现目录滚动
# $1 -> 备份路径
# $2 -> 备份名称
# $3 -> 增量备份的数量
rotateDir() {
    for i in `seq $(($3 - 1)) -1 1`
    do
        if [ -d "$1/$2.$i" ] ; then
            /bin/rm -rf "$1/$2.$((i + 1))"
            mv "$1/$2.$i" "$1/$2.$((i + 1))"
        fi
    done
}

# 调用函数 checkDir ,确保目录存在
checkDir "archive"
checkDir "daily"

#======= Backup Begin =================
# S1: Rotate daily.
rotateDir "${BackupRoot}/daily" "$BackupName" "$BackupNum"

checkDir "daily/${BackupName}.0/"
checkDir "daily/${BackupName}.1/"

mv ${LogFile} ${BackupRoot}/daily/${BackupName}.1/

cat >> ${LogFile} <<_eof --backup-dir="${BackupRoot}/daily/${BackupName}.1" --delete="--delete" --exclude-from="${ExcludeList}" -av="-av" -b="-b" 1="1" _eof="_eof" ackupname="ackupname" ackuproot="ackuproot" ackupsource="ackupsource" and="and" backup="backup" daily="daily" difference="difference" do="do" done="done" in="in" mydate="==========================================" on:="on:" rsync="rsync" s2:="s2:" save="save" the="the">> ${LogFile} 2>&1

# S3: Create an archive backup every week
if [ `date +%w` == "0" ] # 每周日做归档
# if [ `date +%d` == "01" ] # 每月1日做归档
then
    tar -cjf ${BackupRoot}/archive/${BackupName}-${mydate}.tar.bz2 \
      -C ${BackupRoot}/daily/${BackupName}.0 .
fi
您可以适当修该上述脚本中变量:
RmtPath="$1/"
#BackupSource="$1/"
BackupName="$1"
然后传递脚本参数备份其他目录,例如要备份 /www 可以使用如下命令:
./rsync-backup.sh /www

快照型增量备份

使用 rsync 可以做快照(Snapshot)型增量备份。每一个快照都相当于一个完全备份。其核心思想是:对有变化的文件进行复制;对无变化的文件创建硬链接以减少磁盘占用。
下面是对 /home 进行快照型增量备份的步骤说明:
# 第0次备份
# 首先复制 /home 目录的内容到备份目录 /backups/home.0
# rsync -a /home/ /backups/home.0

# 第1次备份(此为核心操作)
# 以硬链接形式复制 /backups/home.0 到 /backups/home.1
# cp -al /backups/home.0 /backups/home.1
# 将 /home 目录的内容同步到目录 /backups/home.0
# (rsync 在发现变化的文件时,先删除之,然后在创建该文件)
# rsync -a --delete /home/ /backups/home.0

# 第2次备份
# 将备份目录 /backups/home.1 更名为 /backups/home.2
# mv /backups/home.1 /backups/home.2
# 执行第1次备份的核心操作

# 第n次备份
# 将早先的备份目录 /backups/home.n 到 /backups/home.1
# 依次更名为 /backups/home.(n+1) 到 /backups/home.2
# 执行第1次备份的核心操作
rsync 2.5.6 版本之后提供了 ––link-dest 选项,如下两条核心操作命令:
cp -al /backups/home.0 /backups/home.1
rsync -a --delete /home/ /backups/home.0
可以简化为如下的一条命令:
rsync -a --delete --link-dest=/backups/home.1 /home/ /backups/home.0
下面给出一个快照型增量备份示例脚本,该脚本来自http://www.mikerubel.org/computers/rsync_snapshots/contributed/peter_schneider-kamp
#!/bin/bash
# ----------------------------------------------------------------------
# mikes handy rotating-filesystem-snapshot utility
# ----------------------------------------------------------------------
# RCS info: $Id: make_snapshot.sh,v 1.6 2002/04/06 04:20:00 mrubel Exp $
# ----------------------------------------------------------------------
# this needs to be a lot more general, but the basic idea is it makes
# rotating backup-snapshots of /home whenever called
# ----------------------------------------------------------------------

# ------------- system commands used by this script --------------------
ID='/usr/bin/id';
ECHO='/bin/echo';

MOUNT='/bin/mount';
RM='/bin/rm';
MV='/bin/mv';
CP='/bin/cp';
TOUCH='/usr/bin/touch';

RSYNC='/usr/bin/rsync';

# ------------- file locations -----------------------------------------

MOUNT_DEVICE=/dev/hdb1;
SNAPSHOT_RW=/root/snapshots;
EXCLUDES=/etc/snapshot_exclude;

# ------------- backup configuration------------------------------------

BACKUP_DIRS="/etc /home"
NUM_OF_SNAPSHOTS=3
BACKUP_INTERVAL=hourly

# ------------- the script itself --------------------------------------

# make sure we're running as root
if (( `$ID -u` != 0 )); then { $ECHO "Sorry, must be root. Exiting..."; exit; } fi

echo "Starting snapshot on "`date`

# attempt to remount the RW mount point as RW; else abort
$MOUNT -o remount,rw $MOUNT_DEVICE $SNAPSHOT_RW ;
if (( $? )); then
{
 $ECHO "snapshot: could not remount $SNAPSHOT_RW readwrite";
 exit;
}
fi;

# rotating snapshots
for BACKUP_DIR in $BACKUP_DIRS
do
 NUM=$NUM_OF_SNAPSHOTS
 # step 1: delete the oldest snapshot, if it exists:
 if [ -d ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_INTERVAL}.$NUM ] ; then \
 $RM -rf ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_INTERVAL}.$NUM ; \
 fi ;
 NUM=$(($NUM-1))
 # step 2: shift the middle snapshots(s) back by one, if they exist
 while [[ $NUM -ge 1 ]]
 do
  if [ -d ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_INTERVAL}.$NUM ] ; then \
   $MV ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_INTERVAL}.$NUM ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_IN}
  fi;
  NUM=$(($NUM-1))
 done

 # step 3: make a hard-link-only (except for dirs) copy of the latest snapshot,
 # if that exists
 if [ -d ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_INTERVAL}.0 ] ; then \
  $CP -al ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_INTERVAL}.0 ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_INTERVAL}
 fi;
 # step 4: rsync from the system into the latest snapshot (notice that
 # rsync behaves like cp --remove-destination by default, so the destination
 # is unlinked first. If it were not so, this would copy over the other
 # snapshot(s) too!
 $RSYNC \
  -va --delete --delete-excluded \
  --exclude-from="$EXCLUDES" \
  ${BACKUP_DIR}/ ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_INTERVAL}.0 ;
 # step 5: update the mtime of ${BACKUP_INTERVAL}.0 to reflect the snapshot time
 $TOUCH ${SNAPSHOT_RW}${BACKUP_DIR}/${BACKUP_INTERVAL}.0 ;
done

# now remount the RW snapshot mountpoint as readonly

$MOUNT -o remount,ro $MOUNT_DEVICE $SNAPSHOT_RW ;
if (( $? )); then
{
 $ECHO "snapshot: could not remount $SNAPSHOT_RW readonly";
 exit;
} fi;























































































2012年12月11日星期二

CENTER OS Server安装桌面系统

yum groupinstall "GNOME Desktop Environment"

Xmanager连接linux安装图形软件DISPLAY环境变量设置

export DISPLAY your_ip(local IP, not server IP):0.0

MySQL Replication跟不上

最近发现Slave库落后从库很远, 而且几乎是出于停滞状态.
最终发现是由于某几个用来同步数据的表没有主键, 而这几个表每天都有大量的数据出入.
给这几个表加上主键就OK了.

查看linux版本

查看内核版本号的方法是:
打开一个terminal,执行命令uname -r
执行命令 uname -a 可以看到更多内核版本号的信息。

查看发行版本号的方法是:
用命令找到/etc目录下的issue文件或release文件。 ls /etc/*issue* 或 ls /etc/*release*
然后直接用cat命令查看文件内容就知道版本号了:

比如在Ubuntu linux 10.04 上cat /etc/lsb-release得到的结果为:
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=10.04
DISTRIB_CODENAME=lucid
DISTRIB_DESCRIPTION="Ubuntu 10.04.1 LTS"
说明版本号为10.04,版本别名是lucid

2012年3月26日星期一

kill sql server2008联接

select 'kill '+ cast(spid as varchar(10)) +';' from sys.sysprocesses where dbid=db_id('Epicor905');

2012年3月23日星期五

换行符陷阱

昨天帮同事将一组外部数据导入到mysql中,  今天同事反应有一个表的数据有问题,  最后一列的数据后面有特殊字符.
我查看了下, 原来同事给的数据是windows下面生成的, windows的换行符是"\r\n", 但是我是在linux下使用load data infile导入的,  这个语法默认会使用linux的换行符"\n"作为行分隔符, 所以最后一列的数据留下了一个"\r", 就导致了数据显示异常.
而另外几个导入的表都没有问题, 原因是那几个表的最后一列都是数字类型, 导入的时候转换成数字类型会将特殊字符去掉.

看来这还是个比较容易忽视的陷阱啊, 虽然查出这个问题比较容易, 但是确实是很容易犯错的.
当年我刚工作的时候, 就因为不知道这个换行符的差别, 为了写一个linux的shell脚本折腾了一整天的.

MySQL下的db link: FEDERATED

以前经常要跨库查询处理数据, 用sql server的时候有link server, oracle有db link, mysql呢?  最近了解到, 其实mysql也是有这种机制的, mysql的federated引擎就可以达到这个目的.
默认mysql是没有启用这个引擎的, 具体可以用show engines查看一下.
我这边的没有, 但是也只需要在my.cnf文件中添加一行"FEDERATED"然后重启mysql, 就启用了.
有两种方法创建federated表, 一种是直接利用connection string:
CREATE TABLE federated_table (
    id     INT(20) NOT NULL AUTO_INCREMENT,
    name   VARCHAR(32) NOT NULL DEFAULT '',
    other  INT(20) NOT NULL DEFAULT '0',
    PRIMARY KEY  (id),
    INDEX name (name),
    INDEX other_key (other)
)
ENGINE=FEDERATED
DEFAULT CHARSET=latin1
CONNECTION='mysql://username:password@hostname:port/database/tablename';
第二种方法, 利用预先创建好的server link:
CREATE SERVER server_name
    FOREIGN DATA WRAPPER wrapper_name
    OPTIONS (host 'host_name', database 'db_name', user 'user_name', password 'password', socket 'socket_path', owner 'owner', port port_no);
CREATE TABLE test_table (
    id     INT(20) NOT NULL AUTO_INCREMENT,
    name   VARCHAR(32) NOT NULL DEFAULT '',
    other  INT(20) NOT NULL DEFAULT '0',
    PRIMARY KEY  (id),
    INDEX name (name),
    INDEX other_key (other)
)
ENGINE=FEDERATED
DEFAULT CHARSET=latin1
CONNECTION='server_name/table_name';

2012年3月22日星期四

Tuning MySQL Query Cache

最近一直在研究mysql的query cache, 对于怎样tuning query cache, <High Performance MySQL>上面的一张图基本上能够很清楚的说明了, 但是里面具体怎么做讲的不是很清楚, 我按照我的理解整理了下.

第一步, 查看hit_rate是否可接受.
hit_rate目前给出的公式是qcache_hits/qcache_hits + com_select, 这个是系统启动以来的总体hit_rate,  但我觉得使用某一段时期的hit_rate应该更合理, 因为就我来讲tuning期间我可能会经常对query cache相关参数进行调整, 要查看调整的效果, 当然就不能是看数据库运行期间的整体hit_rate, 而应该是自我调整以来这一段时间的hit_rate.
所以我给出的公式识是qcache_hits_inc/qcache_hits_inc + com_select_inc.
所谓inc, 就是"incremental value", 就是一段时间以来的增量值.
至于是否可接受, 没有一个一定的准则, 要根据你应用的特点来定, 更新比较多的, 就不能期望这个hit_rate太高, 但是更新比较少, 你的hit_rate也很低, 那就有问题了. 我目前的目标系统是一个订单系统, 因为更新频繁, hit_rate是30%到50%, 我觉得已经可以了.
第二步, 是否大部分的query都是uncacheable的.
这个我觉得可以由qcache_not_cached_inc/com_select_inc来决定, 如果得出的数据比较大, 接近1, 那么就符合这个"大部分uncacheable"的命题了.
(不过最近在一个5.5.15版本的mysql上测试, 发现在运行一个很大的查询的时候, Qcache_not_cached增加了2, 而com_select只增加了1, 暂时还不知道这是个什么情况)
query_cache_limit large enough? 这个我觉得也需要根据你应用的特点来决定, 默认的1M, 我个人觉得对一般系统来说, 已经相当大了,  也就是一个sql的结果集要超过1M, 才不会被cache. 我觉得如果sql结果集超过1M, 还需要被cache, 也就是说运行的还比较频繁, 那这个应用肯定是有问题的.
第三部, 是否有很多的validations? 其实我觉得这里应该是invalidation.  Invalidation其实指的就是query cache中的内容被刷掉. 衡量invalidation我觉得可以有两种方法: 1. qcache_insert_inc / com_select_insert  2. query_in_cache_inc / qcache_insert_inc
第一种方法, 如果值接近1, 也就是说几乎每一个新增的select, 都需要新insert进query cache, 那就代表了invalidation比较多
第二种方法, 如果这个值很小,  也就是说, 不断的有大量的query进入query cache, 但是query cache中的数量并没有增多
判断是否有太多碎片, 个人觉得需要从两方面来判断, 一是free block片数量与query cache中query数量的对比, 二是freeblock平均大小与query_cache_min_res_unit大小的对比, 如果小于query_cache_min_res_unit, 则是碎片.
判断是否有太多的low-memory-prunes, 可以根据Qcache_lowmem_prunes_inc / com_select_inc来对比.
判断是否有很多update, 直接根据Com_update_inc与com_select_inc的数量来判断.
判断query cache是否已经warmed up, 这个我觉得直接根据系统运行时间来判断就可以了.

2012年3月21日星期三

查看当前数据库query cache hit ratio

想弄一个定期查看mysql query cache hit ratio的工具, 前天弄了个store procedure的, 使用的information_schema.global_status中获得status数据, 但是无奈我们目前production环境的版本太低, 还没有这个表, 只能从show global status中获取数据, 废了九牛二虎之力弄了个bash脚本的版本, 下次要弄类似工具还是用python算了, 方便快捷多了.
#!/bin/bash
echo "input password: "
read  -s password
while read line
do
   echo "$line" | grep -q "qcache_hits"
   if [ $? -eq 0 ]
   then
     old_qcache_hits=`echo $line | sed 's/qcache_hits=//'`
   fi
   echo "$line" | grep -q  "com_select"
   if [ $? -eq 0 ]
   then
     old_com_select=`echo $line | sed 's/com_select=//'`
   fi
new_qcache_hits=`mysql -u root -p$password -s -N -e "show global status like 'Qcache_hits';" | sed 's/Qcache_hits[\t]*//'`
new_com_select=`mysql -u root -p$password -s -N -e "show global status like 'Com_select';" | sed 's/Com_select[\t]*//'`
hit_rate=$(echo "scale=3;$new_qcache_hits/($new_qcache_hits+$new_com_select)"|bc)
inc_hit_rate=$(echo "scale=3;($new_qcache_hits-$old_qcache_hits)/($new_com_select+$new_qcache_hits-$old_qcache_hits-$old_com_select)"|bc)
echo qcache_hits=$new_qcache_hits > last_query_cache.info
echo com_select=$new_com_select >> last_query_cache.info
echo "hit_rate: "$hit_rate
echo "inc_hit_rate: "$inc_hit_rate

2012年3月16日星期五

mysql锁调度方法

1. 设置 concurrent_insert值
2. 设置low-priority-updates值

误设query_cache_limit参数

刚在检查几个数据库的时候, 发现一个库明明有query cache, 但是show status却显示query cache中一个query也没有, 小兴奋了一下, 还以为遇到了什么bug. 仔细检查才发现, 将query_cache_limit设为了1,  查看配置文件, 应该是想设为1M的, 结果把后面那个M丢了....