找到10230个回复
  • linux deploy下运行游戏服务端(正式宣布失败)
    43278点击 / 2022-10-29发布 / 2022-10-30回复 / /
    @罐子,有arm64能架设的吗?
  • @老虎会游泳,我做了下『闭包表』和『改良后的邻接表』的测试 (结尾附上一键建表和查询的 SQL 供测试)

    • 数据源:2022 年中国全国 5 级行政区划
    • 数据库:MySQL 8.0.29SQLite 3.39.0
    • 表结构:『闭包表』和『 (<pid, id>, is_leaf) 型邻接表』
    • 测试项:『查询根节点所有后代』和『查询根节点第 5 层后代』

    结果如下 (多次测试稳定后)

    『查询根节点所有后代』速度对比

    表结构 MySQL SQLite
    闭包表 1.3 秒 0.13 秒
    递归邻接表 1.2 秒 0.60 秒
    理想中递归损耗很小的邻接表 0.6 秒 0.12 秒

    『查询根节点第 5 层后代』速度对比

    表结构 MySQL SQLite
    闭包表 1.2 秒 0.12 秒
    递归邻接表 0.5 秒 0.13 秒
    理想中递归损耗很小的邻接表 0.4 秒 0.10 秒

    目前观点

    1. 4W 多次的 refWHERE pid = ?,还是能和 66W 次 eq_ref 级的 WHERE id = ? 过过招,甚至更快的。而且,磁盘IO越慢,这个差异应该越大。

    2. 数据库们的 WITH RECURSIVE 查询,损耗有点大。

      • MySQL 好歹每次递归都将上一次所有结果当作一张表来计算。但大概 5 次递归的耗时,就比非递归的多一倍了

      • SQLite 最摆烂,每次递归只取以前结果的一行来计算,直到取完为止。所以有 66W 次的递归,耗时大概 5 倍多。。

        Extract a single row from the queue.

        Pretend that the single row just extracted is the only row in the recursive table and run the recursive-select, adding all results to the queue.

    『查询根节点所有后代』通用 SQL

    下面 SQL 基本可用于 MySQLSQLite (不支持的特性,数据库会报错,改掉即可)

    PRAGMA cache_size = -204800; -- 允许 SQLite 缓存 200 MB
    
    -- 闭包表查询
    SELECT COUNT(*), SUM(code), SUM(CHAR_LENGTH(name)) -- SQLite 写法:SUM(LENGTH(name))
      FROM closure_tree
     FORCE INDEX (idx_closure_tree) -- 我这测试,MySQL 不加这行,耗时翻好几倍。SQLite 需去掉此行
      JOIN closure ON id = descendant
     WHERE ancestor = 0;
    
    -- 递归邻接表查询
    WITH RECURSIVE
      find(id, code, name, is_leaf) AS (
        SELECT id, code, name, is_leaf
          FROM adjacent
         WHERE pid = 0
         UNION ALL
        SELECT b.id, b.code, b.name, b.is_leaf
          FROM find a
          JOIN adjacent b ON NOT a.is_leaf AND b.pid = a.id
      )
    SELECT COUNT(*), SUM(code), SUM(CHAR_LENGTH(name)) -- SQLite 写法:SUM(LENGTH(name))
      FROM find;
    
    -- 理想中,没有递归损耗的邻接表查询
    SELECT COUNT(*), SUM(b.code), SUM(CHAR_LENGTH(b.name)) -- SQLite 写法:SUM(LENGTH(b.name))
      FROM adjacent a
      LEFT JOIN adjacent b ON b.pid = a.id -- SQLite 需要 LEFT JOIN,否则耗时翻几倍
     WHERE NOT a.is_leaf;
    

    『查询根节点第 5 层后代』通用 SQL

    PRAGMA cache_size = -204800; -- 允许 SQLite 缓存 200 MB
    
    -- 闭包表查询
    SELECT COUNT(*), SUM(code), SUM(CHAR_LENGTH(name)) -- SQLite 写法:SUM(LENGTH(name))
      FROM closure_tree
     FORCE INDEX (idx_closure_tree) -- 我这测试,MySQL 不加这行,耗时翻好几倍。SQLite 需去掉此行
      JOIN closure ON id = descendant
     WHERE ancestor = 0
       AND distance = 5;
    
    -- 递归邻接表查询
    WITH RECURSIVE
      var(depth) AS (
        SELECT 5
      ),
    
      -- 递归部分查前 N - 1 层
      find(id, is_leaf, depth) AS (
        SELECT 0, FALSE, var.depth - 1
          FROM var
         UNION ALL
        SELECT b.id, b.is_leaf, a.depth - 1
          FROM find a
          JOIN adjacent b ON b.pid = a.id
         WHERE a.depth > 0
           AND NOT a.is_leaf
      )
    
    -- 最后一次性 JOIN 第 N 层
    SELECT COUNT(*), SUM(b.code), SUM(CHAR_LENGTH(b.name)) -- SQLite 写法:SUM(LENGTH(b.name))
      FROM find a
     CROSS JOIN adjacent b ON a.id = b.pid -- SQLite 要加 CROSS,否则耗时翻几倍
     WHERE a.depth = 0;
    
    -- 理想中,没有递归损耗的邻接表查询(需要根据层数 N,动态生成 SQL)
    SELECT COUNT(*), SUM(t5.code), SUM(CHAR_LENGTH(t5.name)) -- SQLite 写法:SUM(LENGTH(t5.name))
      FROM adjacent t1
      JOIN adjacent t2 ON t2.pid = t1.id
      JOIN adjacent t3 ON t3.pid = t2.id
      JOIN adjacent t4 ON t4.pid = t3.id
      JOIN adjacent t5 ON t5.pid = t4.id
     WHERE t1.pid = 0;
    

    MySQL 一键建表 SQL

    (在我低配笔记本和固态上,大约执行了 1 分钟)

    -- 允许 200 MB 的内存表
    SET max_heap_table_size = 200 << 20;
    
    -- 建临时数据表,装载 csv 数据,以及计算序号和父子关系
    CREATE TABLE data (
        code    BIGINT      NOT NULL,
        p_code  BIGINT      NOT NULL,
        type    TINYINT     NOT NULL,
        name    VARCHAR(25) NOT NULL,
        id      INT         NOT NULL,
        pid     INT         NOT NULL,
        PRIMARY KEY (code) USING BTREE,
        INDEX USING BTREE (id),
        INDEX USING BTREE (pid, id)
    ) ENGINE = MEMORY;
    
    -- 加载 csv
    LOAD DATA INFILE 'area_code_2022.csv'
    INTO TABLE data
    CHARACTER SET UTF8MB4
    FIELDS TERMINATED BY ','
    ENCLOSED BY '"'
    (code, name, type, p_code);
    
    -- 按照 code 顺序计算 id
    UPDATE data
      JOIN (SELECT code, ROW_NUMBER() OVER win row_num
              FROM data
            WINDOW win AS (ORDER BY code)) t USING(code)
       SET id = row_num;
    
    -- 计算 parent_id(不存在的标0)
    UPDATE data a
      LEFT JOIN data b ON b.code = a.p_code
       SET a.pid = IFNULL(b.id, 0);
    
    -- 建邻接表,并从临时数据表填充数据
    CREATE TABLE adjacent (
        id      INT         NOT NULL,
        pid     INT         NOT NULL,
        is_leaf BOOL        NOT NULL,
        type    TINYINT     NOT NULL,
        code    BIGINT      NOT NULL,
        name    VARCHAR(25) NOT NULL,
        PRIMARY KEY (pid, id)
    )
    SELECT -1 pid, 0 id, FALSE is_leaf, 0 type, 0 code, '' name
     UNION ALL
    SELECT pid, id, type = 5 is_leaf, type, code, name
      FROM data;
    
    -- 建闭包表主表,并从临时数据表填充数据
    CREATE TABLE closure (
        id      INT         NOT NULL,
        type    TINYINT     NOT NULL,
        code    BIGINT      NOT NULL,
        name    VARCHAR(25) NOT NULL,
        PRIMARY KEY (id)
    )
    SELECT 0 id, 0 type, 0 code, '' name
     UNION ALL
    SELECT id, type, code, name
      FROM data;
    
    -- 建闭包表树形关系表
    CREATE TABLE closure_tree (
        ancestor    INT     NOT NULL,
        descendant  INT     NOT NULL,
        distance    TINYINT NOT NULL,
        PRIMARY KEY (descendant, distance)
    );
    
    -- 递归构建树形关系
    INSERT INTO closure_tree(ancestor, descendant, distance)
    WITH RECURSIVE
      parent_of(orig_id, id, dist) AS (
        SELECT id, id, 0
          FROM data
         UNION ALL
        SELECT orig_id, pid, dist + 1
          FROM parent_of
          JOIN data USING(id)
         WHERE id
      )
    SELECT id, orig_id, dist
      FROM parent_of;
    
    -- 为闭包表树形关系表建二级索引
    CREATE INDEX idx_closure_tree ON closure_tree (ancestor, distance);
    
    -- 丢弃临时数据表
    DROP TABLE data;
    

    SQLite 一键建表 SQL

    下列 SQL 需要依赖 SQLite Shell.import --csv,核心 SQLite 库不提供此功能。

    因此,需要使用命令行的 SQLite 来运行Windows 可去官网下载个 1~2 MB 的 sqlite3.exe

    下面使用 Bash Shell 来包装执行命令与 SQL,大约需要运行 30 秒,然后在同目录下生成 150 MB 左右的 test.db

    #!/bin/bash
    
    sqlite3 :memory: <<'EOF'
    
    -- 在内存中计算,最后整理紧凑才写入文件
    PRAGMA TEMP_STORE = MEMORY;
    
    -- 导入 csv 文件至临时表
    CREATE TEMP TABLE csv (code INTEGER PRIMARY KEY, name TEXT, type INT, p_code INT);
    .import --csv area_code_2022.csv csv
    
    -- 建邻接表
    CREATE TABLE adjacent (
        id      INT     NOT NULL,
        pid     INT     NOT NULL,
        is_leaf INT     NOT NULL,
        type    INT     NOT NULL,
        code    INT     NOT NULL,
        name    TEXT    NOT NULL,
        PRIMARY KEY (pid, id)
    ) WITHOUT ROWID;
    
    -- 填充邻接表
    INSERT INTO adjacent (pid, id, is_leaf, type, code, name)
    SELECT -1, 0, FALSE, 0, 0, ""
     UNION ALL
    SELECT p_code, ROW_NUMBER() OVER (), type = 5, type, code, name
      FROM csv
     ORDER BY code;
    
    -- 建临时索引,提速 code 搜索
    CREATE INDEX i ON adjacent (code);
    
    -- 更新 pid
    UPDATE adjacent
       SET pid = t2.id
      FROM adjacent t2
     WHERE adjacent.pid = t2.code;
    
    -- 丢弃临时索引
    DROP INDEX i;
    
    -- 建 id -> pid 索引
    CREATE INDEX idx_adjacent_id ON adjacent (id);
    
    -- 建闭包表主表
    CREATE TABLE closure (
        id      INTEGER PRIMARY KEY,
        type    INT     NOT NULL,
        code    INT     NOT NULL,
        name    TEXT    NOT NULL
    );
    
    -- 建闭包表树形关系表
    CREATE TABLE closure_tree (
        ancestor    INT NOT NULL,
        descendant  INT NOT NULL,
        distance    INT NOT NULL,
        PRIMARY KEY (descendant, distance)
    ) WITHOUT ROWID;
    
    -- 填充闭包表主表
    INSERT INTO closure (id, type, code, name)
    SELECT id, type, code, name
      FROM adjacent;
    
    -- 递归构建树形关系
    WITH RECURSIVE
      parent_of(orig_id, id, dist) AS (
        SELECT id, id, 0
          FROM adjacent
         UNION ALL
        SELECT orig_id, pid, dist + 1
          FROM parent_of
          JOIN adjacent USING(id)
         WHERE id
      )
    INSERT INTO closure_tree (ancestor, descendant, distance)
    SELECT id, orig_id, dist
      FROM parent_of;
    
    -- 为闭包表树形关系表建二级索引
    CREATE INDEX idx_closure_tree ON closure_tree (ancestor, distance);
    
    -- 整理紧实数据库后,写入磁盘
    ANALYZE;
    VACUUM INTO 'test.db';
    
    EOF
    
  • @罐子,这些游戏的服务端,全都是只有可执行文件,没有源码,是吗?

  • @老虎会游泳,虎绿林自己的链接应该可以不用机审吧
    一加8Pro

  • @crbee,基本没问题啊。frp我都编译成功过,
    tinc
    我也编译过

    一加8Pro

  • linux deploy下运行游戏服务端(正式宣布失败)
    43278点击 / 2022-10-29发布 / 2022-10-29回复 / /
    @罐子,正解 @无名啊,核心文件指的是游戏引擎 一般游戏引擎都有专业公司开发编译
  • @666,市面上真正开源的游戏私服没多少,很多都是提供了.几个二进制程序
    和游戏的一些相关东西。
    二进制程序开不开源不影响游戏二次修改。
    对游戏服务端的资源包修改就可以了。比如我之前搭建过的私服"地下城与勇士"就有100级版本的
    一加8Pro

  • @666,那篇文章不是说是源码压缩包吗?

    如果是 C/C++ 写的,自己编译一下应该也行?

    (如果如 @罐子 所说,只有个可执行文件,那就是文章胡说八道了

  • @666,网站程序只是负责更新补丁包和更新检测以及支付接口
    核心程序是X86的二进制程序程序而且有些还是golang程序编译的,我以前玩私服的时候发现了核心二进制程序都是不开源的只有游戏资源包可以修改
    一加8Pro

  • linux deploy下运行游戏服务端(正式宣布失败)
    43278点击 / 2022-10-29发布 / 2022-10-29回复 / /
    @老虎会游泳,谢谢你了!回答了我这么久问题!太复杂了!躺平 我还是换个游戏吧 我找找arm64能运行的去😄(虽然很难找到 但感觉H5类的应该能行)
  • linux deploy下运行游戏服务端(正式宣布失败)
    43278点击 / 2022-10-29发布 / 2022-10-29回复 / /
    @无名啊,百度云盘青春版 不限速
  • @666,这个视频教程会用Termux安装一个x86的Ubuntu。
    我建议你在ARM64里安装宝塔,在Ubuntu里运行主程序,两者分开。
    proot可能无法正常运行宝塔。

    视频链接

  • linux deploy下运行游戏服务端(正式宣布失败)
    43278点击 / 2022-10-29发布 / 2022-10-29回复 / /
    Screenshot_2022-10-29-20-23-17.png(178.72 KB)
  • linux deploy下运行游戏服务端(正式宣布失败)
    43278点击 / 2022-10-29发布 / 2022-10-29回复 / /
    @老虎会游泳,csdn里的那篇文章貌似也是用proot 运行的arm64版本的centos7 
    我想用proot运行个amd64的centos7或者debian10也行
  • linux deploy下运行游戏服务端(正式宣布失败)
    43278点击 / 2022-10-29发布 / 2022-10-29回复 / /
    @老虎会游泳,正解 网站只是提供后台服务对数据库进行增查删操作!
  • @老虎会游泳,他发的那篇知乎文章没有了。。

    我记得文章里没有其他要求了。。

    源码好像是百度盘的,我没会员,懒得下。。

  • @无名啊,这说明网站并不是游戏服务器主程序

  • @666,主程序不是 php 源码吗?

    @老虎会游泳,php 有编译成 x86 这种说法吗?