NebulaGraph的session管理能力测试

前文

选题一:Session 管理策略及其优化
数据库在处理大量的并发查询时,会创建和管理众多的 Session,这是维持数据库操作顺利进行的关键环节。会话管理不仅涉及到用户连接的创建、维持和断开,还包括资源分配、权限验证和状态监控等多个方面。在高并发的环境下,有效的会话管理策略是保证数据库性能和稳定性的核心。本选题旨在探讨基于 NebulaGraph 当前的 Session 管理功能,分析其在实际应用中的表现,并提出可行的优化的使用方案

作为一个服务端基础工具,数据库Session 管理方式非常重要。当客户端与服务端建立连接,我们想像公民出国签证与外国建立联系,国家【服务端】是如何对这些外国人【客户端】进行管理的?

session的技术生命周期

创建使用:当客户首次访问服务端时,服务端会为其创建一个唯一的Session对象,并分配一个Session ID。

Session ID传递:客户端通过密码认证与服务端确认,服务端将Session ID发送给客户端,客户端端会自动将此Session ID与服务端建立会话。

授权访问:服务端接收到带有Session ID的请求后,会使用这个ID查找对应的Session对象,为客户端提供个性化服务。

session存储:Session数据可以存储在服务端的内存中,也可以通过分布式缓存、数据库等方式持久化存储,以便于集群环境中各个服务器节点能够共享Session数据。

销毁删除: 服务端销毁唯一的Session对象,客户端如果想使用,必须要重新创建

资源调试上,NebulaGraph与MySQL一样,采用的是Thread-Pool的方式, 线程处理的最小单位是statement(语句),一个线程可以处理多个连接的请求。在保证充分利用硬件资源情况下(合理设置线程池大小),可避免瞬间连接数暴增导致的服务器抖动

与MySQL一样,NebulaGraph也是一个单进程架构的数据库,主要靠线程进行资源调度和网络通讯。 不过NebulaGraph还多了一个分布式标签,它下面的meta模块服务、graphd模块服务、storag模块服务都占用系统一个进程,然后进程通过线程跨网络访问,

客户端-服务端会话过程

NebulaGraph的Session 生命过程图如下

  • NebulaGraph客户端 向NebulaGraph服务端发起请求【connection-pool.init】
  • NebulaGraph服务端同意,并建立两端会话,把NebulaGraph客户端登记注册进入会话里面。
  • NebulaGraph客户端 执行任务后,向NebulaGraph服务端销毁会话【connection-pool.release】

测试方法

测试环境

单机 CPU 内存 版本
i7 8G nebula3.4.0

硬件

  • CPU核数设置为8核, 观测nebula-graphd的 graph-netio线程个数。
  • CPU核数设置为12核,观测nebula-graphd的 graph-netio线程个数。
  • CPU核数设置为16核,观测nebula-graphd的 graph-netio线程个数。

软件

  • 客户端模拟多个用户, 每个用户对应一个session,并执行大任务【插入1亿条数据到nebugraph】,最后释放session
  • 客户端模拟多个用户, 每个用户对应一个session,并执行大任务【1亿条数据有错,不能写入到nebugraph,写入到日志里面】,最后释放session
  • 客户端模拟多个用户, 每个用户对应一个session,并执行大任务【插入1亿条数据到nebugraph】,没有释放session
  • 客户端模拟多个用户, 每个用户对应一个session,并执行大任务【1亿条数据有错,不能写入到nebugraph,写入到日志里面】,没有释放session
  • 客户端模拟多个用户, 每个用户对应一个session,并执行小任务【小任务是指1条数据】,包括释放和没有释放session的状况,超过服务端预置 的session个数和没有超过的session个数。

伪代码

    connection_pool = ConnectionPool()
    assert connection_pool.init([('xxxx.128', 9669)], config)
    threads = list()
    #模拟16个会话
    for i in range(0,16):
        threads.append(
            threading.Thread(target=main_test, name='thread{}'.format(i))
        )

    for thread in threads:
        thread.start()

    for thread in threads:
        thread.join()

    # close connect pool
    #是否注释决定会话有没有关 
    connection_pool.close()

测试过程

Session的极限承值

#测试过程中报错,错因指向无法创建新的Session

nebula3.Exception.AuthFailedException: b"Create Session failed: Too many sessions created from ::ffff:192.168.153.1 by user root. the threshold is 300. You can change it by modifying 'max_sessions_per_ip_per_user' in nebula-graphd.conf"
Closing a connection that is in use
Closing a connection that is in use
Closing a connection that is in use
Process finished with exit code 0

进入nebula的窗品确认下session是不是到达极限,果然超过了

解决的方法是把session kill掉,然后把max_sessions_per_ip_per_user设置大一点,重启nebula

错误数据写入到日志里面

写入数据到nebula 分为两种情况,一种是数据合规将会写入到storaged里面,一种是数据不合规写入日志里面。两个情况调用的graph-netio线程不同,写入到storaged里面将会依照当前CPU核数 启动相当数量的graph-netio,写入到日志则会启动6到8个graph-netio,但是写入日志的速度会很快。

记录一下客户端发起的会话,写入错误数据到日志,服务端产生的graph-netio线程个数

16个CPU核,客户端发起8个会话,服务端产生 8个graph-netio线程
16个CPU核,客户端发起12个会话,服务端产生8个graph-netio线程
16个CPU核,客户端发起10个会话,服务端产生8个graph-netio线程 
12个CPU核,客户端发起8个会话,服务端产生6个graph-netio线程 
12个CPU核,客户端发起8个会话,服务端产生6个graph-netio线程

正确数据写入到storaged里面

记录设置不同的CPU核数【16、8,4】,客户端不同的session个数 【32、16、8】,发起1亿条数据写入的大任务

16个CPU核个数,服务端产生 16个graph-netio线程
8个CPU核个数,服务端产生8个graph-netio线程
4个CPU核个数,服务端产生 4个graph-netio线程
客户端发起的连接个数【32、16、8】,将会形成对应【32、16、8】session

测试总结

  • 理想正常的处理过程,客户端与服务端建立session,客户执行操作任务,当任务执行结束,session会话释放。
  • 大任务中途宕掉导致的异常情况,客户端 处理的是1亿大任务处理过程 ,处理时间实在过长,如果中途把客户端程序中,你会发现服务端的session因此驻留无法释放,最终造成session负荷数量过多。
  • 小任务导致无法建立服务端的异常情况, 服务端默认只支持300个会话,如果客户端发起300个会话,服务端是能够处理的,如果客户端发起301个会话,会出现Closing a connection that is in use, 服务端只限制有300个位置,超出的位置没有把访问的客户排成队列依照顺序分派,所以必要时要调大服务端的服务个数。
  • 代码没有关闭导致的异常情况,如果客户端写的代码太烂 ,在处理任务完后,没有把session销毁,也会发现服务端的session因此驻留无法释放,最终造成session负荷数量过多。
  • 服务端在会话管理上,支持kill session id的单个操作方式 ,删除多个会话或全部会话,可以参照这里 终止会话 - NebulaGraph Database 手册
  • 针对空连接的session,超时的机制让会话退出,参考session_idle_timeout_secsclient_idle_timeout_secs
  • 建立会话后,假设任务的工作量很大并且数据将写入storag,服务端将会根据当前所有的CPU核个数对请求进行处理,一般会打满CPU,例如一共16有CPU核,那么将会启动16个graph-netio线程处理。如果数据有错将会写到NebulaGraph的错误日志,这时调用的资源不会打满CPU,只有6个线程启动工作。工作结束后,会把graph-netio线程回收,会话可以根据任务的工作调度资源处理。
1 个赞

您好~感谢参加 nebula 开源社区征文活动 :clap:

就文章内容有以下问题和建议:

问题1、Session ID 传递:客户端通过密码认证将 Session ID 发送给服务端

写反了?认证后,服务端发 session 给客户端

问题2、同时 NebulaGraph 也是一个单进程架构的数据库

单进程架构怎么说?

问题3、理想正常的处理过程,客户端与服务端建立 Session,客户执行操作事务,当事务执行结束,Session 会话释放。

nebula 没有事务。

问题4、服务端在会话管理上,支持 kill session id 的单个操作方式 ,但是删除多个会话或全部会话,笔者一时间找不到,而且针对空连接的 Session,也没有超时的机制让会话退出。

文档在这~
https://docs.nebula-graph.com.cn/3.8.0/3.ngql-guide/17.query-tuning-statements/2.kill-session/#_2
https://docs.nebula-graph.com.cn/3.8.0/5.configurations-and-logs/1.configurations/3.graph-config/#networking

1 个赞