TOC
最近对自己写的nosql数据gedis和redis做了性能对比压测,碰到一些问题,做了一些思考和尝试,这里做个记录
长连接
使用长连接能显著提升性能
unix socket
使用 unixsocket qps对比长连接模式提升超过50%
mac系统下 从37446.17qps 到58360.08
本地循环地址 127.0.0.1
- 不走网卡
- 本机的报文的路径是这样的:
- 应用层-> socket接口 -> 传输层(tcp/udp报文) -> 网络层 -> back to 传输层 -> backto socket接口 -.> 传回应用程序
redis-benchmark
- 仅使用多连接,没有使用多线程
- mac系统测试,考虑到cpu为双核四线程,选择同时打开4个redis-benchmark 使用unix socket
*gedis的 qps分别为
16052.65,15807.78,16217.97,16990.91 合计65069.31的qps,单个benchmark下为43658.59
- redis 单个压测的qps为56085.25
- 四个并行压测的qps为,11913.98,13163.96,12466.50,12496.10 合计为50040.54
- 可见客户端单线程的情况下即使使用了IO多路复用模型,仍然无法得出并发运行的服务端的性能瓶颈,客户端的命令归根结底是串行发出的,由于nosql本身的执行速度极快,在下一个命令到来前就已经执行完毕,所以可以大致认为单线程的客户端只能压出单核的性能瓶颈
- go的单线程性能稍弱与c,但是能够方便的利用多核,在高并发场景下还是有自己的优势的
优化TCP/IP 栈
在局域网环境下只要传输的包不超过一个 MTU (以太网下大约 1500 bytes),那么对于 10、100、1000 bytes 不同包大小的处理吞吐能力实际结果差不多
- 局域网内启用巨帧 MTU 包可以对性能有很大的提升
- 测试下来对本机无命中的get压测影响不大
TODO 其他内核参数的调优
ifconfig ${Interface} mtu ${SIZE} up ifconfig eth1 mtu 9000 up
https://www.ibm.com/developerworks/cn/linux/l-hisock.html
修改go 的net包,支持多实例监听同个端口
对比单个和多个端口监听实例
- redis 只有主线程监听端口
- gedis 5个端口监听实例
- 最后的实践与思考发现,当前压测的redis始终高于gedis的原因在于redis-benchmark是单线程,无法充分测试并发服务端的性能瓶颈
- 多个监听实例在长连接场景下的价值有限
TODO 后面继续测试多个监听实例对大量短连接的意义
diff --git a/src/pkg/net/sockopt_linux.go b/src/pkg/net/sockopt_linux.go --- a/src/pkg/net/sockopt_linux.go +++ b/src/pkg/net/sockopt_linux.go @@ -9,6 +9,10 @@ "syscall" ) +const SO_REUSEPORT = 15 + +var USE_SO_REUSEPORT bool + func setDefaultSockopts(s, family, sotype int, ipv6only bool) error { if family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW { // Allow both IP versions even if the OS default @@ -21,6 +25,11 @@ } func setDefaultListenerSockopts(s int) error { + if USE_SO_REUSEPORT { + if err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, SO_REUSEPORT, 1); err != nil { + return os.NewSyscallError("setsockopt", err) + } + } // Allow reuse of recently-used addresses. return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)) }
TODO 尝试linux下的异步io
comments powered by Disqus