96核ssd机型用之前32核的配置性能较差,大约22万set。优化的时候还发现延迟非常高,需要进行参数调整和代码优化。

压测发现延迟长尾部分分布如下:

命令时间ms百分比
SET17.00099.06%
SET200.00099.98%
SET410.00099.99%
SET480.000100.00%

可以看到延迟问题还是很严重的。在测试的过程中发现,第一次测试的时候延迟往往比较严重,后面则会大幅度改善,大约100%都在20ms以内。

初步怀疑是因为第一次压测有一些类似缺页导致内存分配之类的操作,具体原因还待进一步排查。

调整参数

96核机型参数需要做一些调整,经过调整之后,性能大约33w左右。

【结果】

qps提升:

22w->33w

优化后延迟分布:

命令时间ms百分比
SET15.00099.09%
SET19.00099.99%
SET21.000100.00%

std::locale问题

image.png

可以看到MGLock::MGLock()构造函数里面调用std::locale::~locale()占用cpu较多。

分析代码看到getCurThreadId()函数里面调用了std::ostringstream,这个接口会对std::locale进行计数引用计数的操作,这个操作是需要上锁的,在多线程环境下面性能很差。

【解决办法】

干掉getCurThreadId()

【结果】

qps提升:

33w->55w

优化后延迟分布:

命令时间ms百分比
SET8.00099.00%
SET11.00099.99%
SET15.000100.00%

store数量

调试发现,kvstorecount配置从10变为50的时候qps有较大提升,延迟也有很大改善。

为了搞明白代码里面具体是哪儿对kvstorecount这么敏感,分别抓取了两种配置的火焰图

kvstorecount=10火焰图:

image.png

kvstorecount=50火焰图:

image.png

两张火焰图的主要函数占用cpu比例都差不多,但是有两块__lll_lock_wait函数的占用在kvstorecount=10的图中占比较高。

通过调用pstack命令10次生成10个时刻的调用栈关系,然后去调用栈里面找到tendisplus::RocksTxn::commit调用__lll_lock_wait的栈,看看具体是哪个函数调用了__lll_lock_wait。 在这10次里面找到了如下调用关系:

image.png

出现2次,这个说明tendisplus::RocksKVStore::markCommitted()这儿的锁竞争对性能产生了影响

image.png

出现1次,这个说明rocksdb::TransactionBaseImpl::Put会在rocksdb对key进行上锁,这儿对性能产生了影响

【解决办法】

配置kvstore 10->50

【结果】

qps提升:

55w->65w

优化后延迟分布:

命令时间ms百分比
SET8.40099.01%
SET9.90099.99%
SET13.000100.00%

线程数不能太大

测试的时候发现,线程数executorthreadnum不能大于64,一旦出现,qps会下降非常的多。

最后经过添加测试代码跳过其中一些步骤,来观察qps是否会下降。结果发现_txn->Commit()

图片

结果发现,96线程的时候,在图中代码块跳过_txn->Commit()的时候qps大约65万,不跳过qps大约12万。通过调大kvstorecount也不能带来改善。

说明_txn->Commit()对性能产生很大的影响,初步怀疑是rocksdb里面对某个全局的资源产生了锁竞争,需要进一步的排查。

rocksdb事务锁

TransactionLockMgr::lock_maps_会对提交事务的key进行上锁。桶的个数由参数rocksdb::TransactionDBOptions::num_stripes = 16控制,为了减少锁冲突,调大这个参数。

【解决办法】

RocksKVStore::restart()函数设置参数txnDbOptions.num_stripes = 40;

【结果】

qps:

无提升

优化后延迟分布:

命令时间ms百分比
SET7.20099.03%
SET9.60099.99%
SET12.000100.00%

io任务队列和executor任务队列

io任务队列和executor任务队列调度混乱,考虑一一绑定。结果发现绑定之后,qps虽然没有提升,但是延迟提升明显。

【解决办法】

NetSession在创建连接的时候记录io任务队列id,调度的时候传给executor任务队列。

_executorList[ctxId]->schedule(std::forward<fn>(task));

【结果】

qps:

无提升

优化后延迟分布:

命令时间ms百分比
SET1.60099.46%
SET2.20099.91%
SET6.90099.99%
SET9.400100.00%

cpu跑不满

经过上面的优化后,cpu大约只能跑到6000%左右,还有大约20-30个核没有利用起来,还需进一步排查。

延迟彪高

测试发现,第一次压测之后,也会偶尔出现延迟严重的现象。

进一步测试发现,在设置binlog与kv同一个column-family的时候,

binlog-using-defaultCF on,就不会出现了。

原因待进一步追查。

最终配置参数

  1. bind 127.0.0.1
  2. port 51002
  3. binlog-using-defaultCF off
  4. loglevel notice
  5. cluster-enabled on
  6. storage rocks
  7. logdir ./home/log
  8. dir ./home/db
  9. dumpdir ./home/dump
  10. pidfile ./home/tendisplus.pid
  11. rocks.blockcachemb 20480
  12. noexpire true
  13. #maxBinlogKeepNum 2000000
  14. #maxBinlogKeepNum 2
  15. minBinlogKeepSec 600
  16. kvstorecount 50
  17. #requirepass abc
  18. #masterauth abc
  19. netiothreadnum 7
  20. executorthreadnum 56
  21. executorWorkPoolSize 8
  22. truncateBinlogIntervalMs 1000
  23. truncateBinlogNum 5000000
  24. rocks.blockcache_strict_capacity_limit 0
  25. rocks.cache_index_and_filter_blocks 1
  26. rocks.max_open_files -1
  27. incrPushThreadnum 4
  28. rocks.compress_type lz4
  29. #rocks.level0_compress_enabled no
  30. #rocks.level1_compress_enabled no
  31. rocks.level_compaction_dynamic_level_bytes 1
  32. rocks.max_background_compactions 64
  33. #rocks.max_write_buffer_number 16
  34. #rocks.min_write_buffer_number_to_merge 14
  35. rocks.write_buffer_size 67108864
  36. rocks.target_file_size_base 67108864
  37. rocks.max_bytes_for_level_base 536870912
  38. #67108864 64m
  39. #1048576 1m
  40. #rocks.wal_dir /dev/shm/wal
  41. #mapping-command dbsize emptyint
  42. mapping-command keys emptymultibulk
  43. migrateSenderThreadnum 2
  44. migrateReceiveThreadnum 4
  45. #migrateClearThreadnum 40
  46. cluster-migration-binlog-iters 20
  47. cluster-migration-slots-num-per-task 100
  48. cluster-migration-rate-limit 1000
  49. cluster-migration-distance 100