回顾一下
上一节我们讲解了RandomAccessFile
打开的文件句柄过多导致cpu占用较多,同时我们设想,使用缓存缓存住打开的文件句柄,那么这时候就应该能大幅度降低打开的句柄数目。
尝试前的思考
之前在分析场景的时候,不充分,忽略了一个很重要的问题:在分布式文件系统中,通过版本号来保证每次写入的内容全新,那么每个版本的文件都会新开一个文件句柄,这个不能避免新文件的打开;所以将文件句柄放到hashmap中不可取
后续尝试
待完成
每天code一点点
上一节我们讲解了RandomAccessFile
打开的文件句柄过多导致cpu占用较多,同时我们设想,使用缓存缓存住打开的文件句柄,那么这时候就应该能大幅度降低打开的句柄数目。
之前在分析场景的时候,不充分,忽略了一个很重要的问题:在分布式文件系统中,通过版本号来保证每次写入的内容全新,那么每个版本的文件都会新开一个文件句柄,这个不能避免新文件的打开;所以将文件句柄放到hashmap中不可取
待完成
一直以来都在做分布式存储的事情,从最开始的本地文件系统到伪分布式文件系统,从
FastDFS 到 HDFS ,以及到自研的 DFS,说到 DFS 不得不提两点:1.DFS 存什么?2.
DFS
怎么存?那首先,存什么,其实除了原本的文件内容本身外,我们需要将文件的元数据信息以及文件所在的目录信息也保存下来;其次,我们需要考虑的是如何存,目前主流的分布式文件系统都是依照google
提出的论文所开发的,那就是元数据保存在一个进程,文件内容放在另一个进程,进程间通信,实现文件的读写。
文件系统I/O利用率低:目前自研的dfs文件系统I/O吞吐量上不去,那造成这个问题的元凶到底是什么?
环境信息:
首先,作为公司的统一的dfs平台,接收来自业务的读写数据,从业务方跑benchmark,发现tps
只有2k 多,那作为一个这么牛配置的机器,跑这么低的tps我们现在分析一下。
首先,要做性能分析,上来top 先看系统平均负载信息,很低,对应R 状态的进程也只有dfs
的进程 紧接着,看一下iotop,发现只有很少的7-8M/s的文件写入,这是什么鬼。
初步怀疑是SSD 没配置好,使用dd 命令跑一下现在的磁盘读写速度:
写性能:dd if=/dev/zero of=/data/test bs=8M count=1000
读性能:dd
if=/data/test of=/dev/null bs=8M count=1000
根目录下创建data目录,发现每秒都是1.9G/s
这个排除SSD配置的问题
那我们把焦点放到存储进程的cpu 使用情况上来,使用火焰图分析:
简单说一下生成火焰图步骤,root 用户:
- yum install perf
- 安装cmake
- 安装 perf-map-agent (这款工具是为了解决perf生成的方法栈展示成地址没有方法名的问题)
- jvm 中增加参数 -XX:+PreserveFramePointer
- 安装FlameGraph
- 使用脚本 gen-flame-graph.sh生成火焰图,如果本地java命令安装不规范,修改脚本43行路径)
分析火焰图,发现大量的new
RandomAccessFile(),频繁的创建关闭,影响性能,尝试缓存。
RandomAccessFile 构造方法
1 |
|
那今天先到这,顺便休息下眼睛
本文不是对redis-lua运行环境的优化,而是针对日常开发过程中,具体的业务而进行的优化。
结合一个具体的案例,我们来分析下:
首先
,将业务抽象,要保存一棵目录树到redis,比如说人员组织架构(我们不是搞用户中心的),
一种做法是将数据打平,放到redis中 eg:
XX集团/YY分公司/ZZ子公司,一种是使用hash结构 eg:XX集团:{YY分公司:ZZ子公司}
其次
,我们现在要获取每个层级的所有子节点,表面上看使用hash结构更加快捷,但如果在集群环境下
,这个结构就保存到了一个实例上了,必然会引起
hotkey和bigkey 问题。反观使用打平的方式,因为数据分散到多个节点上,性能反而会提高很多。
接着
,今天主要跟大家聊下lua来查询子节点的方案,我们的要求是如何能随便拿出来某一层,当然我们最后选择使用hash结构存储(历史原因吧,客户端每次发送的请求都是某一层级的值,而不是完整路径,所以用模糊匹配查找起来难度较大)
那lua查询获得所有子节点的脚本如下:
1 | local treeKey = KEYS[1] |
以上代码出自 https://my.oschina.net/u/1469495/blog/1504818#comments
做了些调整,但是性能存在问题,未在生产上使用。
但是
,我们发现业务中查询节点是一个高频的操作,追踪代码发现一个for循环4万次调用,里面又有4次hget操作,合下来就是16w,于是又开始尝试使用lua,将这4次hget合并,当然有同学会问
为什么不使用mget,或者pipeline,因为我们for 循环里的get操作还有业务逻辑,key之间有依赖关系,需要拿到上一个key对应的value,做截取然后在加工,作为下一次的key,所以单纯使用mget解决不了我们的问题
尝试过后,性能提升了好几倍
redis-lua 的调试,推荐使用参数 --ldb-sync-mode
因为该参数,再调试的过程不会被打断,直接使用 –ldb 会被打断
,或者hang住到那,这都不是我们期待的,所以条件允许,请使用 --ldb-sync-mode
参数
调试过程中,常用命令如下:
业务进程hang住不动了
1 | public static void main(String[] args) { |
执行上面的语句会发现,已经死循环了,为什么?编译的时候并没有报错!!!
直接remove掉数组是remove不掉的,这就不是一个对象,在写基础代码的时候要注意
1 | wget http://www.apuebook.com/src.3e.tar.gz |
1 | sudo cp lib/libapue.a /usr/local/lib/ |
可以直接fork我的库,apuebegin,或者直接clone
1 | git clone https://github.com/excel-bat/apuebegin.git |
1.如果自己已经安装了gdb,无法调试,出现如下错误
1 | apuebegin/cmake-build-debug/ls1: unknown load command 0x32 |
请将自己安装的gdb(如下图),切换为默认Budle gdb
2.无法找到apue.h 文件
1 | 检查是否正确将头文件添加 |
在使用netty过程中,对channelOption设置,当设置tcp _no delay
true的话,会客户端断掉,客户端会发送RST标志, 使用nio的话是错误是
1 | java.io.IOException: Connection reset by peer at |
使用epoll
1 | Unhandled exception in native epoll: |
于是乎,改回后,一切安静