安装机器
环境参数:4cpu 内存8G 磁盘大小250G
10.19.*.140 主
10.19.*.141 从
磁盘类型判断:rota为1则表示磁盘可旋转,那么就是HDD;如果返回0,则表示磁盘不可以旋转,那么就有可能是SSD。
lsblk -d -o name,rota 执行结果如下:sda为SSD fd0和sr0为HDD
name | rota |
---|---|
fd0 | 1 |
sda | 0 |
sr0 | 1 |
docker启动安装
启动主服务
docker search pika
docker pull pikadb/pika --拉镜像
vi pika.conf --进入配置文件修改后台启动daemonize为yes
docker run -it -d -p 9876:9221 pikadb/pika --后台启动
docker exec -it containerid bash --进入容器
./bin/pika -c conf/pika.conf --进入文件启动pika(启动命令)
启动从服务与主操作基本相同相同,不同点
1.修改启动端口 9877:9221
2.配置主从,进入pika.conf配置文件 进行修改 slaveof ip port
相关启动命令
docker ps --查看进程
docker start 9fe26a76668b
docker stop 9fe26a76668b
docker restart 9fe26a76668b
docker kill -s kill 9fe26a76668b 强制杀死
docker rename containerid newname 重命名容器
docker inspect 9fe26a76668b 查看容器ip
pika文件存储
内存加磁盘
磁盘文件删除后,要执行flushall才会重建文件
pika docker启动问题处理
问题1:启动不生效
docker容器启动的同时内部也要启动
docker run -it -d -p 9876:9221 pikadb/pika --后台启动
docker exec -it containerid bash --进入容器
./bin/pika -c conf/pika.conf --进入文件启动pika(启动命令)
问题2:前台启动
可修改配置文件daemonize为no再启动可查看主从连接情况
问题3:重启主之后,从的连不上
docker启动时每次启动时iP会修改,所以主启动时要指定ip启动,或是在容器内杀掉进程后启动不会改变ip
ps -ef|grep pika
kill -9 47
./bin/pika -c conf/pika.conf
磁盘使用量估算原理
查看文件大小时可执行命令:
echo info | /usr/local/redis/bin/redis-cli -p 9221 | grep db_size_human
由公式key+value+attach=C求取attach值得出以下估算公式
string 容量估算 = key 个数 * (key 字节数 + value 字节数 + attach (30字节))
String | key | value | 净存量(M) | 实际存量(M) | attach(实-净)(M) | 单条attach(B) |
---|---|---|---|---|---|---|
10w | 13 | 0 | 1.269 | 3.809 | 2.54 | 26.63 |
- | 13 | 13 | 2.539 | 5.079 | 2.558 | 26.82 |
- | 65 | 0 | 6.1988 | 8.888 | 2.69 | 28.21 |
- | 65 | 65 | 12.695 | 15.603 | 2.908 | 30.49 |
20w | 13 | 0 | 2.539 | 7.618 | 5.079 | 26.63 |
- | 13 | 13 | 5.078 | 10.158 | 5.08 | 26.63 |
- | 65 | 0 | 12.695 | 17.777 | 5.082 | 26.64 |
- | 65 | 65 | 25.391 | 30.475 | 5.084 | 27.93 |
由公式Key+n(field+value+attach)=C求取attach值
hash 容量估算 =key个数 * [key字节数 + n * (field字节数 + value字节数 + attach(45字节))]
hash | key(B) | field(B) | value(B) | 净存量(M) | 实际存量(M) | 单条attach(B) |
---|---|---|---|---|---|---|
10w | 13 | 0 | 0 | 1.269 | 7.228 | |
- | 13 | 2*14 | 0 | 4.003 | 12.5 | 45.04 |
- | 13 | 2*14 | 13*2 | 6.543 | 15.042 | 45.36 |
20w | 13 | 0 | 0 | 2.539 | 14.456 | |
- | 13 | 2*14 | 0 | 8.006 | 25 | 45.04 |
- | 13 | 2*14 | 13*2 | 13.086 | 30.114 | 45.44 |
50w | 13 | 0 | 0 | 6.348 | 36.140 | |
- | 13 | 2*14 | 0 | 20.0195 | 62.5 | 45.04 |
- | 13 | 2*14 | 13*2 | 32.715 | 75.221 | 45.37 |
list与set类型观察总attach会发现attach是附着在value上的,根据规律得出单条attach的值
list 容量估算 =key字节数 + n * (value字节数 + attach(33字节))
list | key | value | 净存量(B) | 实际存量(B) | 总attach(实-净)(B) | 单条attach(B) |
---|---|---|---|---|---|---|
- | 13 | 100000*13 | 1300013 | 4601047 | 3301034 | 33 |
- | 13 | 200000*13 | 2600013 | 9202027 | 6602014 | 33 |
- | 13 | 300000*13 | 3900013 | 13803014 | 9903001 | 33 |
set 容量估算 =key字节数 + n * (value字节数 + attach(25字节))
set | key | value | 净存量(B) | 实际存量(B) | 总attach(实-净)(B) | 单条attach(B) |
---|---|---|---|---|---|---|
- | 13 | 100000*13 | 1300013 | 3800852 | 2500839 | 25 |
- | 13 | 200000*13 | 2600013 | 7601664 | 5001651 | 25 |
- | 13 | 300000*13 | 3900013 | 11402476 | 7502463 | 25 |
- | 100000*13 | 0 | 1300000 | 7201512 | 5901512 | |
- | 200000*13 | 0 | 2600000 | 14403031 | 118030031 |
pika超时机制
现象1:key设置过期时间,删除数据后,会发现文件中的数据没有删除掉,再存入新的相同的key也可以存入
现象2:相同的key在硬盘中不会覆盖,可以获取到最新的
原因:数据在存储时在数据末尾加上了用于记录的时间戳,如果时间戳里记录的时间比当前时间要小,说明数据已经过去,这时候就不会返回给上层,但是实际的数据可能还存db层。
引发问题:如何清理过期数据?
解决方法:手动删除使用compact会统一删除过期数据
compact介绍:
1.自动全量的compact:通过配置的参数每天定时触发一次自动全量compact,特别适合存在多数据结构。参数结构:“启动时间(小时)-结束时间(小时)、磁盘空间空余空间百分比”,例如需要配置一个每天在凌晨,同时该任务仅仅在磁盘空余时间不低于30%的时候执行,那么应配置为:03-04/30,该参数默认为空
2.自动全量compact,和compact-cron区别为,compact-cron每天仅在指定时间段执行,而compact循环执行,例如需要配置一个每4个小时执行一次的自动compact任务,同时该任务仅仅在磁盘空余空间应配置为:4/30该默认参数为空
3.compact期间可能会额外占用磁盘空间, 连接数不会增加,约要预留db大小的20%给compact
底层会在某些时候触发局部compact, 也可以在Pika端执行compact命令手动触发全局compact(同一个Key的记录,以Sequence Number最大的那个为准)
主从数据同步时间测试
前提:服务器上本地运行(为了防止有网络传输影响)
主输入10w数据完成开始,到从服务读取到主服务输入的最后一个数据的时间差1548667123413-1548667123409=4ms
此数据不一定代表主从同步的时间,待进一步验证
for (int i=0;i<100000;i++){
String s=StringUtils.leftPad(i+"",12,"0");
String key = "y"+s;
masterRedisPoolFactory.setValue(key,key);
}
System.out.println(System.currentTimeMillis());
while(true){
String s=slaveRedisPoolFactory.getValue("y0000000100000");
if (s!=null){
break;
}
}
System.out.println(System.currentTimeMillis());
百万数据遍历时间测试
前提:服务器上本地运行(为了防止有网络传输影响)
遍历数据使用的scan命令,每次查询10万条数据,共查询100万,执行10次,平均遍历速度100万/467ms
Long start = System.currentTimeMillis();
Jedis jedis=slaveRedisPoolFactory.getJedis();
ScanParams scanParams = new ScanParams();
scanParams.count(100000);//每10万条查询
Long startTime = System.currentTimeMillis();
List retList = new ArrayList();//存放遍历出来的数据
String scanRet = "0";
do {
ScanResult ret = jedis.scan(scanRet,scanParams);
scanRet = ret.getStringCursor();// 返回用于下次遍历的游标
retList.addAll(ret.getResult());// 返回结果
} while (!scanRet.equals("1000000"));
Long endTime = System.currentTimeMillis();
log.info("retList size:[{}],using time is:[{}]ms",retList.size(),(endTime - startTime));
线上问题(暂未验证)
问题1.Get child directory when try to do db sync failed线上报这个错,slave同步不了
answer:
这个过程就是pika全量同步并转换为增量同步的过程:
是把dump目录整个拷到slave替换掉db目录,然后启动从库,查看info文件,记录里面的同步位置,然后通过slaveof指定该位置同步
问题2.Pika对一个Key反复写入确实可能会导致硬盘占用持续增长
问题3.写入量很大,然后一段时间之后卡死,server直接hang死 ,重启后 rocksdb进程仍占99.9%cpu?
iostat -mxt 1 (磁盘为SSD盘)查看await的值(一般达到10已经为上限)
这个问题可以这样解释:写入数据的速度大于rocksdb的memtable的刷盘速度,IO吃不消
而没有及时刷盘的memtable不断的积压,最终达到了写保护的阈值,造成写入hang死。然后这个时候仍然有很多memtable没刷盘,此时实例被重启,然后启动的时候rocksdb需要通过WAL来处理这些数据,然后就是现在看到的样子,但现在磁盘还是吃不消的样子,所以这个意外重启的恢复估计还要跑一阵
一台机器一般3~10个pika吧,看压力,动态调整,这个机器吃不消就挪到其它机器上几个实例这样