• 最近对自己写的nosql数据gedis和redis做了性能对比压测,碰到一些问题,做了一些思考和尝试,这里做个记录

https://github.com/wosiwo/gedis

长连接

  • 使用长连接能显著提升性能

    unix socket

  • 使用 unixsocket qps对比长连接模式提升超过50%

  • mac系统下 从37446.17qps 到58360.08

    本地循环地址 127.0.0.1

  • 不走网卡

  • 本机的报文的路径是这样的:

  • 应用层-> socket接口 -> 传输层(tcp/udp报文) -> 网络层 -> back to 传输层 -> backto socket接口 -.> 传回应用程序

https://blog.csdn.net/weiyuefei/article/details/78796781

异步io

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))
 }
```