Feeds:
Posts
Comments

patch 的管理

patch 一般是对一个程序修复了某个bug 或者做了一些改进。 从源代码的角度看,patch 实际上就是一个记录了代码的变动的文件。我们可以应用一个patch,或者回退一个patch。 从代码角度看,就是对代码进行了改动。

假设我们的最初的程序代码放在 pro 路径下面, 然后我们对pro 有一份拷贝,放在了pro-new 下面, 然后对pro-new 下面的代码进行修改。 修改以后,可以产生一个patch来记录所做的变化。 然后将patch 分发出去,其他人可以用这个patch。

patch 的产生:
diff -uNr pro pro-new > p-0.1.patch (pro 和 pro-new 为代码的最上层目录)

patch 的应用(别人下载了你的patch,现在要将这些变化应用到他们的程序中):
patch -p0 < p-0.1.patch

patch 的回滚:
patch -Rp0 < p-0.1.patch

运行上面2个命令的时候,需要将patch 放到代码最上层目录相同的位置。 如果放到了代码的最上层目录的下层, 就需要用p1 来代替p0。

1 如果连接在某一个scci 卡上的设备没有被识别出来,想要再重新识别一次:
a. 确定host id
Lsscsi -H
b 找到相对应的scan 文件
find /sys/ -name scan | grep host{n}
c echo ‘- – -’ >/sys/devices/pci0000:00/0000:00:02.5/host0/scsi_host/host0/scan

2 scsi 设备在遇到某些故障以后,会被永久的offline, 可以看到”device SCSI offline”. 在故障解决以后,需要人为的online 这个设备
Redhat:
echo “running” >/sys/class/scsi_host/hostH/device/targetH:C:T/H:C:T:L/state

3 设置磁盘的read ahead size (不局限于scsi disk)

# blockdev –report
RO RA SSZ BSZ StartSec Size Device
rw 256 512 2048 0 599932581888 /dev/sda
rw 256 512 4096 2048 52428800000 /dev/sda1
rw 256 512 4096 102402048 8589934592 /dev/sda2
rw 256 512 4096 119179264 524288000000 /dev/sda3

RA 的单位是512 bytes 的sectors.
Set readahead to N 512-byte sectors.

# blockdev –setra 8 /dev/sda

这个命令重启就会无效,想长期有效可以加入到/etc/rc.d/rc.local 里面

初识scsi 设备

SCSI 是什么?
SCSI 是small compute system interface 的简称,是计算机与外部设备之间交互的一种标准。 同时又有很多厂商的产品提供了对SCSI 的支持。对SCSI 的直观理解可以是如下图形:
scsi card

根据产品的不同,现在的一些SCSI 卡的同步传输速度可以可以达到640MB/s.

一个SCSI 接口可以同时连接多个(7/16) 个IO 设备,例如CD/DVD, 磁盘,磁带,扫描机等等。连接线可以想象为如下图形(一头连接SCSI 接口,另外的一方有多个插口可以连接多个设备):

SCSI CPU:
SCSI 本身自己带有CPU, 所以占用系统CPU 的资源特别少。在工作时主机CPU只要向SCSI卡发出工作指令,SCSI卡就会自己进行工作,工作结束后返回工作结果给CPU,在整个过程中,CPU均可以进行自身工作。SCSI卡自己可对CPU指令进行排队,这样就提高了工作效率。在多任务时硬盘会在当前磁头位置,将邻近的任务先完成,再逐一进行处理.

/dev/sd….
所有的设备在Linux 中都是以文件的形式存在,连接在SCSI 卡上的磁盘都被称为sd{n}。 内核通过/dev/sd{n}来完成对改磁盘文件的访问。但一个磁盘设备可能对应几个不同的设备文件,这就是多路环境。

对于Redhat, redhat 启动的时候就会自动创建128 个device 文件。

设备文件到设备的映射:
查看scsi 设备, 就是连接线多的那头的设备:
$ lsscsi -l
[6:0:0:0] storage HP P410 5.14 –
state=running queue_depth=1020 scsi_level=6 type=12 device_blocked=0 timeout=0
[6:0:0:1] disk HP LOGICAL VOLUME 5.14 /dev/sda
state=running queue_depth=1020 scsi_level=6 type=0 device_blocked=0 timeout=30

查看scsi 卡的信息:
$ lsscsi -H -l
[0] ahci
cmd_per_lun=1 host_busy=0 sg_tablesize=168 unchecked_isa_dma=0
[1] ahci
cmd_per_lun=1 host_busy=0 sg_tablesize=168 unchecked_isa_dma=0
[2] ahci
cmd_per_lun=1 host_busy=0 sg_tablesize=168 unchecked_isa_dma=0
[3] ahci
cmd_per_lun=1 host_busy=0 sg_tablesize=168 unchecked_isa_dma=0
[4] ahci
cmd_per_lun=1 host_busy=0 sg_tablesize=168 unchecked_isa_dma=0
[5] ahci
cmd_per_lun=1 host_busy=0 sg_tablesize=168 unchecked_isa_dma=0
[6] hpsa
cmd_per_lun=1020 host_busy=0 sg_tablesize=543 unchecked_isa_dma=0

查看一个设备是如何连接到scsi 卡, scsi 卡又是如何连接到 主板的。以上面的sda 为例:
$ find /sys -name sda
/sys/devices/pci0000:00/0000:00:07.0/0000:04:00.0/host6/target6:0:0/6:0:0:1/block/sda
/sys/class/block/sda
/sys/block/sda

可见,sda 连接的是host6 也就是 hpsa 的卡。

识别多路环境:
同一个设别可以通过不同的路径最终连接到主板, 可以通过scsi_id 来识别多个scsi device 是不是同一个device

/sbin/scsi_id --whitelisted --device=/dev/sda
输出的就是device 的uid, 如果一样 就是同一个设备。

本文供想用c++ 测试mongo 性能的人参考。
个人用c++ 来写主要是因为想尽快熟悉mongod 的一些类和方法。 mongo 本身就是c++ 写的,mongo 的c++ driver 中的很多类和mongod 的代码也是一样的。

所需要的软件:
1 boost_1.49
mongo c++ driver 使用了大量的boost 里面的类,官方建议使用boost1.49. 更早的版本是不行的,更新的版本没有测试。 mgLoad 和mongod 一样, 都使用了boost_program_options 来解析命令行参数。

2 libmongoclient.a mongod 的c++ driver. 2.2 使用源码安装后会自动包含。
[root@localhost d-mongo222]# ls
bin include lib
[root@localhost d-mongo222]# ls lib/
libmongoclient.a

3 scons, 可有可无,类似于make 工具。 make 通过读取makefile 来获得编译选项, scons 通过SConstruct 来获得编译信息。 始终没有见到makefile 的完整教程,所以就使用了scons.
(mongod 的编译也是通过scons). mgLoad 只有5个文件,可以手工编译。

4 g++ 等等

编译:
代码可以在 https://github.com/zhihuiFan/zhifan/tree/master/mongoTool/mgLoad 下载,
把SConstructs 中
mongo = Environment(CPPPATH = ['/root/boost149/include','/sfw/d-mongo222/include','.'],
CXXFLAGS=["-std=c++0x","-w","-ggdb"],
LIBPATH=['/sfw/d-mongo222/lib','/root/boost149/lib'],
LIBS=['pthread','mongoclient','boost_thread','boost_filesystem','boost_program_options','boost_system'])
mongo.Program(['mgload'],['tool.cpp', 'main.cpp','db.cpp'])

/root/boost149/include 为boost1.49 的头文件路径
/sfw/d-mongo222/include 为mongo c++ driver 的头文件路径
/sfw/d-mongo222/lib 为mongo c++ driver 的lib 路径
/root/boost149/lib 为boost1.49 的libs 路径
根据需要进行修改。

$ scons -Q


运行:

默认情况, 是用了boost 的 动态连接库,这样运行的时候需要将 将其路径添加到LD_LIBRARY_PATH 下面。
如果将binary 拷贝到其他机器上运行,如果其他机器上没有boost 的路径,需要在安装boost 的时候指定为静态链接。
关于boost 的编译安装,包括静态链接 可以参考:

http://www.fanzhihui.com/2013/01/reslove-dynamic-library-dependence/

支持的选项有:
[root@localhost mongo]# ./mgload -h
Allowed options:
-h [ --help ] show this message and exit
–host arg set host info, default :localhost
–read arg read threads number, default :0
–write arg read threads number, default :0
–coll arg namespace to operate, default: test.test
–wsleep arg sleep n ms per write operation, defalut: 0ms
–rsleep arg sleep n ms per read operation, defalut: 0ms
–length arg recordLen, only for writer thread, default: 50
–append arg will not drop the $coll at the writer begining,
defalut: false
–count arg the times of loop in rw threads,defaults 1000
–maxid arg was used by reader thread to generate query id

目前需要人工指定replication set 中的master。

PS: 写这类工具还是用perl/python 等的脚本工具比较好。

在mongo 的log 中经常看到
“warning: virtual size (mMB) – mapped size (nMB) is large (m-nMB). could indicate a memory leak”

cat /proc/pgrep mongod`/maps 发现有很多1M 的size, 刚好吻合在mongo 2.0 以后一个thread 的 stack size, 但这个数目却和mongod 进程的线程数目不等。查看进程内有多少线程,可以通过 pstack ·pgrep monnod| grep -c Thread。

mongoDB 会为每一个连接创建一个thread, 通过增加和减少mongo connection 来看maps 的变化,然后发现,刚开始增加1个连接, 会分配一个1M 的size, 断开连接并不减少,再连接时这块内存可以被重用。

Root Cause:
这个glibc 的一个特性, 但一个线程结束后,线程的stack 并不会返回个os, 而是cache 在进程内。当下一次再启动一个线程,并且设置的stack size 也一样大,这部分内存会从进程内分配。

所以如果要追究mongodb 是否内存泄露,除了mmap 的size, 再看一下曾经最大连接数是多少,乘以1M。 就可以估计出这部分的所占的内容大小。

详细信息可以查看:http://repo.or.cz/w/glibc.git/blob/HEAD:/nptl/allocatestack.c#l173

如果程序中使用到了动态连接库,那么需要在以后该软件所运行的环境中都有该库。这就造成了某些不便。 如果可以得到相关的源代码,可以用过2种方式来解决。
1 直接编译成 .o 而不是.so 文件, 然后将 .o 和 我们的程序在连接在一起。
2 编译成静态库,然后和我们的程序连接在一起。
前者需要对整个源代码有很清晰的认识,所以实施起来有些困难。 对于源码安装,一般都提供了将library 编译成静态连接的接口,相对容易实施。下面拿boost 为例来说明:

使用动态连接的结果:
[root@localhost tools]# make nsparse
g++ -ggdb -std=c++0x -lboost_program_options nsparse.cpp -o nsparse
[root@localhost tools]# ldd nsparse
linux-vdso.so.1 => (0x00007fff151ff000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x000000376a800000)
libboost_program_options.so.1.48.0 => /lib64/libboost_program_options.so.1.48.0 (0x00007f4f7dfd1000) libstdc++.so.6 => /lib64/libstdc++.so.6 (0x0000003777c00000)
libm.so.6 => /lib64/libm.so.6 (0x000000376b400000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x000000376f000000)
libc.so.6 => /lib64/libc.so.6 (0x000000376a400000)
/lib64/ld-linux-x86-64.so.2 (0x000000376a000000)

这样就要求以后运行该程序的机器上都装有libboost_program_options.so .

使用静态编译:
下载boost 源码后,
[root@localhost boost]# ./bootstrap.sh –with-libraries=program_options –prefix=/root/boost/program_options
[root@localhost boost]# ./b2 –help

link=static|shared Whether to build static or shared libraries

[root@localhost boost]# ./b2 –link=static
[root@localhost boost]# ./b2 install
[root@localhost lib]# cd /root/boost/program_options/lib
[root@localhost lib]# ls
libboost_program_options.a libboost_program_options.so libboost_program_options.so.1.52.0

[root@localhost tools]# g++ -I /root/boost/program_options/include nsparse.cpp -o stnsparse /root/boost/program_options/lib/libboost_program_options.a
[root@localhost tools]# ldd stnsparse
linux-vdso.so.1 => (0x00007fff2c30e000)
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x0000003777c00000)
libm.so.6 => /lib64/libm.so.6 (0x000000376b400000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x000000376f000000)
libc.so.6 => /lib64/libc.so.6 (0x000000376a400000)
/lib64/ld-linux-x86-64.so.2 (0x000000376a000000)

这样,再运行该程序则不需要libboost_program_options 的动态链接库。

Started with 2.2.0, mongod will start several threads to apply oplog. one backgroupSync thread is to get the oplog from master, one is to organize the oplog to apply them parallelly, and, there are some work threads to apply them. 8 worker threads in 2.2.0.

BackgroupSync thread it use OplogReader to fetch oplog from master, and store them into a blockingQueue struct locally. BlockingQueue is fixed-size, but its size is std::numeric_limits::max() (seems to large?). mongod can push oplog into this queue only if there are some free space avaiable in this queue. mongo records that how many oplog in this queue, how big is it, and how long time it takes to put the oplog into the queue, we can find this information with command db.serverStatus().replNetworkQueue;
Actually, the “push time” doesn’t include the network shipping time, it just start when bgsync get a oplog, and finish when the oplog is pushed into the queue.

@bgsync.cpp
Timer timer;
// the blocking queue will wait (forever) until there's room for us to push
OCCASIONALLY {
LOG(2) << "bgsync buffer has " << _buffer.size() << " bytes" << rsLog;
}
_buffer.push(o);

{
boost::unique_lock lock(_mutex);

// update counters
_queueCounter.waitTime += timer.millis();
_queueCounter.numElems++;
...
}

Since the time is too short, sometime, the recorded time maybe 0.

SyncTail thread it will get the oplog from the blocking dequeue, prefetch them parallel, and hash them into several vectors to let them applied parallelly. the hash function is like this:

void SyncTail::fillWriterVectors(const std::deque& ops,
std::vector< std::vector >* writerVectors) {
for (std::deque::const_iterator it = ops.begin();
it != ops.end();
++it) {
const BSONElement e = it->getField("ns");
verify(e.type() == String);
const char* ns = e.valuestr();
int len = e.valuestrsize();
uint32_t hash = 0;
MurmurHash3_x86_32( ns, len, 0, &hash);

(*writerVectors)[hash % writerVectors->size()].push_back(*it);
}
}

This thread submit the job and wait them completed.
about prefetch prefetch includes both index and records.
Per my understanding, that mongo introduce “prefetch” is for “parallel apply”. For example, There are 3 work threads to recover parallel, and all the 3 collections are in the same database, so they will compete for the same Writelock. Let’s assume that T1 obtains the writeLock, and then T1 find the page are not in physical memory. “page fault” and fetch the data into physical memory. but during this process, the other two threads T2/T3 are waiting for T1′s writeLock. it doesn’t have to wait in this case in fact. so the “prefetch” means “prefetch parallel” before get the writeLock. after the prefetch parallel, T1 get the writeLock (no page fault at this time), modify collection, release lock. T2 and T3 … . this feature will decrease the wait time for wirteLock, but will not decrease the possibility of page fault, it just make “page fault” happen during different period(before/after get writeLock).

Also, there are some sistuations, we didn’t changed the indexed columns, so we will not willing to prefetch all the indexes since it will increase some unnecessary io request. mongo also provide a option that “replIndexPrefetch”, we can set it to “_id_only".

Work Thread pool there are 8 threads in thread pool. and they will apply the oplog finally.

How to improve apply performance
1. we need to check if the network is bottneck.
we can run db.serverStatus().replNetworkQueue to see if we have got some oplog, but they aren’t be applied. if have, we can assume that the network is not the root cause.
2. if network is not the root cause, we can see if we can use more collections and put them in different databases.
since mongod use database level lock since 2.2.0, we can use different database to let them applied parallel really.
if possible, we can split one collection to several collections like “t_0,t_1,t_2″ and so on.
3 disable unnecessary prefetch

undo change vector size
transaction tables consistent reads – undo records applied
rollback changes – undo records applied
CR blocks created

第一个数据反映了产生了多少Undo. 主要是dml 操作。
第二个参数是oracle 读取undo segment hearder 的transaction table 所读的undo,读这个undo 目前知道有2种可能。 1. oracle 进行一致性读。 2. delayed clearout。
第三个参数就是rollback 所用的实际undo 数目。
第四个参数 显示了oracle 读取到了所需要的undo, 然后构建的CR 的数目。

对于一个正在执行DML 的session, 如果第一个 “undo change vector size” 没有增加,可以认为这段时间并没有dml 任何数据。 可能在做其他事情。

如果第二个数据增加比较厉害, 考虑 一致性读 和 delayed clearout。 可以通过第四个数据“CR blocks created” 来一定程度上区分 是delayed clearout 还是 consistent reads. 如果是后者,是需要通过构建相应
CR 来结束的。

对于delayed clearout 可以通过在操作之前fts 相关的segment,来先clearout,然后再进行其他操作。这时数据就已经是clear 的了。

本文所说的dml 可以考虑成 insert into t select * from test;   select * from test; 可能会需要读取相应的undo。
在进行online redefinition 的start 步骤,也可以通过这几个统计数据来判断start 的进度。

关于delayed clear out 可以参考: http://jonathanlewis.wordpress.com/2009/06/16/clean-it-up/

CPU 负载分析-1

从vmstat 里面,可以看到有us,sy,id。在某些系统上还会有wa. 它们分别代表user, kernel, idle, 和iowait.
假设我们运行了vmstat 3, 通过一下方式来计算用作各个部分CPU的百分比。
一般情况下, 内核会每个10ms 检测一下cpu 在做什么。 如果是在运行user 的code, 那么相关的统计数据值加1, 如果在运行内核的code,那么sy 相关的值加1.
如果有些系统也记录了wa, 那么当cpu 没有什么可做的时候(即Idel), Kernel 会判断是否有IO 没有完成,如果有,就增加wa 相关的值。 如果没有io,就记录到idle上。等过了3s中
以后,Kernel 会计算这段时间内各个指标所增长的值,从而计算出百分比。

例如在一个单核CPU 上, 一个事务有10ms 的cpu 时间 和 90ms 的io 时间来完成。这样在1s 内, 可以完成10个这样的事务,而cpu 的利用率可以为10%, iowait 或者idle 的为90%。
假设我们提高了io 的性能,同样的io 需要40ms 就可以完成,这样在1s 内可以完成20个这样的事务,io 性能的提高促使了cpu 的更充分利用,而cpu 的使用率也因此提高到了20%.
所以,cpu 负载的提升未必是坏事,也有可能是cpu 得到了更充分的利用。

对于上述例子,cpu 使用率的优化不能从 增长IO time 来优化,但可以减少本来10ms 的cpu time 来优化负载。 减少io wait time,让cpu 负载更高, 或者 同样的事务,让cpu time 更少,
从而 cpu 负载更低,都是好的。

交换空间由实际物理内存和 物理交换空间组成。 例如 10G 的 物理内存(内存条) + 8G 的物理交换空间(可配置的) 就组成 18G 的交换空间。
交换空间有以下4中状态: 空闲,已保留,已分配,已换出。
在Solaris 中,当我们分配一个数据段,例如mmap (…. MAP_PRIVATE) 或者malloc 的时候,内核就会分配相应的交换空间,但只有真正使用的时候,才会实际分配。所以在预留的交换空间可能既没有和物理内存对应,也没有和物理交换空间对应。 AIX 则与之不同, 在malloc 时,不会预留空间,之后在需要的时候才分配, 如果交换空间不足,就会发出SIGDANGER 的信号。)

在Solaris 上,例如一个程序需要分配100M 的交换空间, 而实际使用的仅仅有10M. 而系统有 130 M 的物理内存。
1 没有物理交换空间。 则总共只能运行一个这样的程序。
2 如果配置了1200M 的交换空间。 则可以运行13个这样的程序。 (没有考虑内核使用)

swap -s 可以看到预留的,分配的 和 可用的大小。
$> swap -s
total: 114443648k bytes allocated + 1304952k reserved = 115748600k used, 28299552k available

关于更详细的信息可以使用prtswap -l 查看更详细的信息。 例如allocate 的交换空间多少是在物理内存多少是在物理交换空间中的。