【文末有奖竞猜】让 seekdb 告诉你,谁是今年的 F1 车手总冠军

还是回到上周 OceanBase 年度发布会发布的 AI 数据库 seekdb,很多高手已经在第一时间完成了评测,作为一个 AI 门外汉,我来从另一个角度探索一下 seekdb 的产品能力怎么样,做一个小实验,让 seekdb 来告诉我们,谁是今年的 F1 车手总冠军。

1. 安装准备环境

操作系统:Redhat 8.5

CPU:4C

内存:16GB

磁盘空间:100GB(实际使用量非常少)

在官网下载的 EL7 版 rpm,经过实际测试,可以在 EL8 上正常安装部署。


    [root@seekdbtst tmp]# yum install ./seekdb-1.0.0.0-100000262025111218.el7.x86_64.rpm 
    Updating Subscription Management repositories.
    Unable to read consumer identity
    
    This system is not registered with an entitlement server. You can use subscription-manager to register.
    
    Last metadata expiration check: 0:02:41 ago on Thu 27 Nov 2025 09:49:02 AM CST.
    Dependencies resolved.
    =================================================================================================================================================================================================================================================
     Package                                            Architecture                                       Version                                                                    Repository                                                Size
    =================================================================================================================================================================================================================================================
    Installing:
     seekdb                                             x86_64                                             1.0.0.0-100000262025111218.el7                                             @commandline                                             131 M
    
    Transaction Summary
    =================================================================================================================================================================================================================================================
    Install  1 Package
    
    Total size: 131 M
    Installed size: 605 M
    Is this ok [y/N]: y
    Downloading Packages:
    Running transaction check
    Transaction check succeeded.
    Running transaction test
    Transaction test succeeded.
    Running transaction
      Preparing        :                                                                                                                                                                                                                         1/1 
      Running scriptlet: seekdb-1.0.0.0-100000262025111218.el7.x86_64                                                                                                                                                                            1/1 
    execute pre install script
    
      Installing       : seekdb-1.0.0.0-100000262025111218.el7.x86_64                                                                                                                                                                            1/1 
      Running scriptlet: seekdb-1.0.0.0-100000262025111218.el7.x86_64                                                                                                                                                                            1/1 
    execute post install script
    To configure OceanBase : edit /etc/oceanbase/seekdb.cnf
    To start OceanBase     : systemctl start seekdb
    To enable OceanBase auto reboot  : systemctl enable seekdb
    To get more infomation : https://www.oceanbase.com/docs
    
      Verifying        : seekdb-1.0.0.0-100000262025111218.el7.x86_64                                                                                                                                                                            1/1 
    Installed products updated.
    
    Installed:
      seekdb-1.0.0.0-100000262025111218.el7.x86_64                                                                                                                                                                                                   
    
    Complete!

这一步结束,按照提示使用 systemctl 启动服务即可,我直接使用的 mysql 客户端测试没有单独安装 obclient

2. 实验内容

我先说一下背景,F1 今年只剩下两站比赛,目前诺里斯领跑积分榜,390分,皮亚斯特里和维斯塔潘都是366分第二名,还有两站卡塔尔和阿布扎比,一共两场正赛一场冲刺赛,理论上这三位车手都有机会获得最多的25+25+8分,也就意味着三个人分差24分都有机会夺冠,其中诺里斯的动力单元存在较高的爆缸风险,因此我们来用 seekdb 模拟一下,最终三个人夺冠的概率是多少,主要测试数据类型、约束、存储过程以及少量的函数。

2.1 创建对象

先把环境清理完

    -- 1.清理旧对象
    DROP PROCEDURE IF EXISTS batch_simulation;
    DROP PROCEDURE IF EXISTS calculate_champion;
    DROP PROCEDURE IF EXISTS record_race_event;
    DROP TABLE IF EXISTS driver_race_scores;
    DROP TABLE IF EXISTS simulation_results;
    DROP TABLE IF EXISTS race_events;
    DROP TABLE IF EXISTS remaining_races;
    DROP TABLE IF EXISTS drivers;
    DROP TABLE IF EXISTS points_rule;
    DROP DATABASE IF EXISTS f1_wdc_simulator;

然后统一字符集,这个在我测试时遇到了问题,出现mysql客户端、操作系统字符集、数据库字符集不一致的情况

    -- 2. 会话字符集配置(解决字符集报错,强制统一utf8mb4)
    SET NAMES utf8mb4 COLLATE utf8mb4_general_ci;
    SET character_set_client = utf8mb4;
    SET character_set_connection = utf8mb4;
    SET character_set_database = utf8mb4;
    SET character_set_results = utf8mb4;
    SET character_set_server = utf8mb4;

创建库表

    -- 3. 建库建表
    CREATE DATABASE f1_wdc_simulator CHARACTER SET utf8mb4;
    USE f1_wdc_simulator;
    
    -- 车手基础信息表(更新为当前最新积分)
    CREATE TABLE drivers (
      driver_id INT PRIMARY KEY AUTO_INCREMENT,
      driver_name VARCHAR(50) NOT NULL COMMENT '车手姓名',
      initial_points INT NOT NULL COMMENT '初始积分',
      race_wins INT NOT NULL COMMENT '分站冠军数(这个作为同分决胜用)',
      final_points INT DEFAULT 0 COMMENT '最终积分(模拟结束后更新)',
      is_champion TINYINT DEFAULT 0 COMMENT '是否最终冠军(0=否,1=是)',
      create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
      UNIQUE KEY uk_driver_name (driver_name)
    ) COMMENT '车手基础信息表';
    
    -- 插入当前最新积分:诺里斯390 | 皮亚斯特里366 | 维斯塔潘366
    INSERT INTO drivers (driver_name, initial_points, race_wins)
    VALUES 
    ('诺里斯', 390, 6),  -- 最新积分390分,分站冠军6个
    ('皮亚斯特里', 366, 7),  -- 最新积分366分,分站冠军7个
    ('维斯塔潘', 366, 6);  -- 最新积分366分,分站冠军6个
    
    -- (2)剩余赛事表(本赛季剩下的卡塔尔站和阿布扎比站)
    CREATE TABLE remaining_races (
      race_id INT PRIMARY KEY AUTO_INCREMENT,
      race_name VARCHAR(100) NOT NULL COMMENT '赛事名称',
      race_type VARCHAR(10) NOT NULL COMMENT '赛事类型(正赛、冲刺赛)',
      max_points INT NOT NULL COMMENT '该赛事最高可获积分(正赛25,冲刺赛8)',
      is_completed TINYINT DEFAULT 0 COMMENT '是否已模拟完成(0=否,1=是)',
      create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
      CONSTRAINT chk_remaining_race_type CHECK (race_type IN ('正赛', '冲刺赛'))
    ) COMMENT '剩余赛事表(实际未举办赛事)';
    
    INSERT INTO remaining_races (race_name, race_type, max_points)
    VALUES 
    ('卡塔尔站-冲刺赛', '冲刺赛', 8),  -- 冲刺赛最高8分
    ('卡塔尔站-正赛', '正赛', 25),    -- 正赛最高25分
    ('阿布扎比站-正赛', '正赛', 25);  -- 正赛最高25分
    
    -- (3)积分规则映射表(来自F1官方2025赛季规则)
    CREATE TABLE points_rule (
      rule_id INT PRIMARY KEY AUTO_INCREMENT,
      race_type VARCHAR(10) NOT NULL COMMENT '赛事类型(正赛、冲刺赛)',
      rank INT NOT NULL COMMENT '排名(1-10,10名后0分)',
      points INT NOT NULL COMMENT '对应得分(冲刺赛8-1,正赛25-1)',
      CONSTRAINT chk_points_rule_type CHECK (race_type IN ('正赛', '冲刺赛')),
      CONSTRAINT uk_race_type_rank UNIQUE (race_type, rank)
    ) COMMENT 'F1 2025赛季积分规则映射表';
    
    INSERT INTO points_rule (race_type, rank, points)
    VALUES
    -- 冲刺赛积分规则(1-8名得分:8、7、6、5、4、3、2、1)
    ('冲刺赛', 1, 8), ('冲刺赛', 2, 7), ('冲刺赛', 3, 6),
    ('冲刺赛', 4, 5), ('冲刺赛', 5, 4), ('冲刺赛', 6, 3),
    ('冲刺赛', 7, 2), ('冲刺赛', 8, 1), ('冲刺赛', 9, 0), ('冲刺赛', 10, 0),
    -- 正赛积分规则(1-10名得分:25、18、15、12、10、8、6、4、2、1)
    ('正赛', 1, 25), ('正赛', 2, 18), ('正赛', 3, 15),
    ('正赛', 4, 12), ('正赛', 5, 10), ('正赛', 6, 8),
    ('正赛', 7, 6), ('正赛', 8, 4), ('正赛', 9, 2), ('正赛', 10, 1);
    
    -- (4)赛事事件记录表
    CREATE TABLE race_events (
      event_id INT PRIMARY KEY AUTO_INCREMENT,
      race_id INT NOT NULL COMMENT '关联赛事ID',
      driver_id INT NOT NULL COMMENT '关联车手ID(1=诺里斯,2=皮亚斯特里,3=维斯塔潘)',
      is_retired TINYINT NOT NULL COMMENT '是否退赛(0=否,1=是)',
      retire_reason VARCHAR(20) DEFAULT '无' COMMENT '退赛原因(无、PU爆缸、机械故障、碰撞)',
      is_change_pu TINYINT DEFAULT 0 COMMENT '是否更换PU(0=否,1=是)',
      penalty_grid INT DEFAULT 0 COMMENT '罚退位数(换PU≥10)',
      rank INT DEFAULT 0 COMMENT '该站排名(退赛则为0)',
      create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (race_id) REFERENCES remaining_races(race_id),
      FOREIGN KEY (driver_id) REFERENCES drivers(driver_id),
      CONSTRAINT chk_race_events_reason CHECK (retire_reason IN ('无', 'PU爆缸', '机械故障', '碰撞')),
      CONSTRAINT uk_race_driver UNIQUE (race_id, driver_id)
    ) COMMENT '赛事事件记录表(全车手事件追踪)';
    
    -- (5)车手单站得分表
    CREATE TABLE driver_race_scores (
      score_id INT PRIMARY KEY AUTO_INCREMENT,
      race_id INT NOT NULL COMMENT '关联赛事ID',
      driver_id INT NOT NULL COMMENT '关联车手ID',
      race_type VARCHAR(10) NOT NULL COMMENT '赛事类型(关联积分规则)',
      rank INT NOT NULL COMMENT '该站排名(退赛=0)',
      points_earned INT NOT NULL COMMENT '该站得分(按F1官方规则计算)',
      create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (race_id) REFERENCES remaining_races(race_id),
      FOREIGN KEY (driver_id) REFERENCES drivers(driver_id),
      CONSTRAINT uk_race_driver_score UNIQUE (race_id, driver_id)
    ) COMMENT '车手单站得分表(细化每站积分)';
    
    -- (6)模拟结果表
    CREATE TABLE simulation_results (
      result_id INT PRIMARY KEY AUTO_INCREMENT,
      simulation_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '模拟时间',
      simulation_no INT NOT NULL COMMENT '模拟序号(1、2、...、N)',
      champion_name VARCHAR(50) NOT NULL COMMENT '冠军姓名',
      champion_points INT NOT NULL COMMENT '冠军最终积分',
      norris_final INT NOT NULL COMMENT '诺里斯最终积分',
      piastri_final INT NOT NULL COMMENT '皮亚斯特里最终积分',
      verstappen_final INT NOT NULL COMMENT '维斯塔潘最终积分',
      key_reason VARCHAR(200) NOT NULL COMMENT '冠军归属关键依据',
      CONSTRAINT uk_simulation_no UNIQUE (simulation_no)
    ) COMMENT '模拟结果表(含模拟序号,用于批量统计)';

然后我们查看一下结果,6张表创建完毕,相关表也有了数据

    mysql> show tables;
    +----------------------------+
    | Tables_in_f1_wdc_simulator |
    +----------------------------+
    | driver_race_scores         |
    | drivers                    |
    | points_rule                |
    | race_events                |
    | remaining_races            |
    | simulation_results         |
    +----------------------------+
    6 rows in set (0.00 sec)

2.2 创建存储过程

我们使用存储过程来模拟这三场比赛的结果,主要需要创建三个存储过程:

存储过程1,用于记录赛事事件和计算单站得分,其中赛事事件主要包含的是:是否退赛(包含动力单元爆缸的可能)、是否更换动力单元维修区罚退、完赛名次和积分

    CREATE PROCEDURE record_race_event(
      IN p_race_id INT,
      IN p_norris_retired TINYINT,
      IN p_norris_reason VARCHAR(20),
      IN p_norris_change_pu TINYINT,
      IN p_norris_rank INT,
      IN p_piastri_retired TINYINT,
      IN p_piastri_reason VARCHAR(20),
      IN p_piastri_change_pu TINYINT,
      IN p_piastri_rank INT,
      IN p_verstappen_retired TINYINT,
      IN p_verstappen_reason VARCHAR(20),
      IN p_verstappen_change_pu TINYINT,
      IN p_verstappen_rank INT
    )
    BEGIN
      DECLARE v_race_type VARCHAR(10);
      DECLARE v_norris_points INT DEFAULT 0;
      DECLARE v_piastri_points INT DEFAULT 0;
      DECLARE v_verstappen_points INT DEFAULT 0;
    
      SELECT race_type INTO v_race_type FROM remaining_races WHERE race_id = p_race_id;
    
      -- 诺里斯得分(退赛得0分,否则按排名匹配积分规则,其他两个车手也是)
      IF p_norris_retired = 1 THEN
        SET v_norris_points = 0;
      ELSE
        SELECT points INTO v_norris_points FROM points_rule WHERE race_type = v_race_type AND rank = p_norris_rank;
      END IF;
    
      -- 皮亚斯特里得分
      IF p_piastri_retired = 1 THEN
        SET v_piastri_points = 0;
      ELSE
        SELECT points INTO v_piastri_points FROM points_rule WHERE race_type = v_race_type AND rank = p_piastri_rank;
      END IF;
    
      -- 维斯塔潘得分
      IF p_verstappen_retired = 1 THEN
        SET v_verstappen_points = 0;
      ELSE
        SELECT points INTO v_verstappen_points FROM points_rule WHERE race_type = v_race_type AND rank = p_verstappen_rank;
      END IF;
    
      -- 写入赛事事件表(记录退赛、PU更换、排名等信息)
      INSERT INTO race_events (race_id, driver_id, is_retired, retire_reason, is_change_pu, penalty_grid, rank)
      VALUES 
      (p_race_id, 1, p_norris_retired, p_norris_reason, p_norris_change_pu, IF(p_norris_change_pu=1,10,0), p_norris_rank),
      (p_race_id, 2, p_piastri_retired, p_piastri_reason, p_piastri_change_pu, IF(p_piastri_change_pu=1,10,0), p_piastri_rank),
      (p_race_id, 3, p_verstappen_retired, p_verstappen_reason, p_verstappen_change_pu, IF(p_verstappen_change_pu=1,10,0), p_verstappen_rank);
    
      -- 写入单站得分表(按F1规则计算的实际得分)
      INSERT INTO driver_race_scores (race_id, driver_id, race_type, rank, points_earned)
      VALUES 
      (p_race_id, 1, v_race_type, p_norris_rank, v_norris_points),
      (p_race_id, 2, v_race_type, p_piastri_rank, v_piastri_points),
      (p_race_id, 3, v_race_type, p_verstappen_rank, v_verstappen_points);
    
      -- 标记赛事完成
      UPDATE remaining_races SET is_completed = 1 WHERE race_id = p_race_id;
    END //

存储过程2,基于当前最新积分排名加上三场比赛的实际结果,计算最终积分和冠军归属

    CREATE PROCEDURE calculate_champion(IN p_simulation_no INT)
    BEGIN
      DECLARE v_norris_initial INT;
      DECLARE v_piastri_initial INT;
      DECLARE v_verstappen_initial INT;
      DECLARE v_norris_total INT;
      DECLARE v_piastri_total INT;
      DECLARE v_verstappen_total INT;
      DECLARE v_norris_wins INT;
      DECLARE v_piastri_wins INT;
      DECLARE v_verstappen_wins INT;
      DECLARE v_champion_name VARCHAR(50);
      DECLARE v_champion_points INT;
      DECLARE v_key_reason VARCHAR(200);
    
      -- 获取当前最新初始积分和分站冠军数
      SELECT initial_points, race_wins INTO v_norris_initial, v_norris_wins FROM drivers WHERE driver_id = 1;
      SELECT initial_points, race_wins INTO v_piastri_initial, v_piastri_wins FROM drivers WHERE driver_id = 2;
      SELECT initial_points, race_wins INTO v_verstappen_initial, v_verstappen_wins FROM drivers WHERE driver_id = 3;
    
      -- 最终积分 = 当前积分 + 剩余赛事得分
      SELECT IFNULL(SUM(points_earned), 0) INTO v_norris_total FROM driver_race_scores WHERE driver_id = 1;
      SELECT IFNULL(SUM(points_earned), 0) INTO v_piastri_total FROM driver_race_scores WHERE driver_id = 2;
      SELECT IFNULL(SUM(points_earned), 0) INTO v_verstappen_total FROM driver_race_scores WHERE driver_id = 3;
    
      SET v_norris_total = v_norris_initial + v_norris_total;
      SET v_piastri_total = v_piastri_initial + v_piastri_total;
      SET v_verstappen_total = v_verstappen_initial + v_verstappen_total;
    
      -- 更新最终积分到车手表
      UPDATE drivers SET final_points = v_norris_total WHERE driver_id = 1;
      UPDATE drivers SET final_points = v_piastri_total WHERE driver_id = 2;
      UPDATE drivers SET final_points = v_verstappen_total WHERE driver_id = 3;
    
      -- 重置冠军标记
      UPDATE drivers SET is_champion = 0;
    
      -- 判断冠军归属(因为F1的相关规则规则:积分最高的夺冠;如果积分相同则分站冠军数多者夺冠)
      IF v_norris_total > v_piastri_total AND v_norris_total > v_verstappen_total THEN
        SET v_champion_name = '诺里斯';
        SET v_champion_points = v_norris_total;
        SET v_key_reason = CONCAT('积分领先(诺里斯', v_norris_total, '分,皮亚斯特里', v_piastri_total, '分,维斯塔潘', v_verstappen_total, '分)');
      ELSEIF v_piastri_total > v_norris_total AND v_piastri_total > v_verstappen_total THEN
        SET v_champion_name = '皮亚斯特里';
        SET v_champion_points = v_piastri_total;
        SET v_key_reason = CONCAT('积分领先(皮亚斯特里', v_piastri_total, '分,诺里斯', v_norris_total, '分,维斯塔潘', v_verstappen_total, '分)');
      ELSEIF v_verstappen_total > v_norris_total AND v_verstappen_total > v_piastri_total THEN
        SET v_champion_name = '维斯塔潘';
        SET v_champion_points = v_verstappen_total;
        SET v_key_reason = CONCAT('积分领先(维斯塔潘', v_verstappen_total, '分,诺里斯', v_norris_total, '分,皮亚斯特里', v_piastri_total, '分)');
      ELSE
        -- 同分处理(按F1规则:分站冠军数多者夺冠)
        IF v_norris_total = v_piastri_total AND v_norris_total > v_verstappen_total THEN
          SET v_champion_name = '皮亚斯特里';
          SET v_champion_points = v_piastri_total;
          SET v_key_reason = CONCAT('诺里斯与皮亚斯特里同分(均', v_norris_total, '分),皮亚斯特里冠数更多(7>6)');
        ELSEIF v_norris_total = v_verstappen_total AND v_norris_total > v_piastri_total THEN
          SET v_champion_name = '诺里斯';
          SET v_champion_points = v_norris_total;
          SET v_key_reason = CONCAT('诺里斯与维斯塔潘同分(均', v_norris_total, '分),冠数相同(6=6),诺里斯优先');
        ELSEIF v_piastri_total = v_verstappen_total AND v_piastri_total > v_norris_total THEN
          SET v_champion_name = '皮亚斯特里';
          SET v_champion_points = v_piastri_total;
          SET v_key_reason = CONCAT('皮亚斯特里与维斯塔潘同分(均', v_piastri_total, '分),皮亚斯特里冠数更多(7>6)');
        ELSE
          SET v_champion_name = '皮亚斯特里';
          SET v_champion_points = v_piastri_total;
          SET v_key_reason = CONCAT('三人同分(均', v_norris_total, '分),皮亚斯特里冠数更多(7冠)');
        END IF;
      END IF;
    
      -- 标记冠军
      UPDATE drivers SET is_champion = 1 WHERE driver_name = v_champion_name;
    
      -- 写入模拟结果表(记录每次模拟的最终结果)
      INSERT INTO simulation_results (
        simulation_no, champion_name, champion_points, norris_final, piastri_final, verstappen_final, key_reason
      ) VALUES (
        p_simulation_no, v_champion_name, v_champion_points, v_norris_total, v_piastri_total, v_verstappen_total, v_key_reason
      );
    
      -- 输出本次模拟积分(简化展示)
      SELECT 
        '诺里斯' AS 车手姓名,
        v_norris_total AS 最终积分
      FROM DUAL
      UNION ALL
      SELECT 
        '皮亚斯特里' AS 车手姓名,
        v_piastri_total AS 最终积分
      FROM DUAL
      UNION ALL
      SELECT 
        '维斯塔潘' AS 车手姓名,
        v_verstappen_total AS 最终积分
      FROM DUAL;
    END //

存储过程3,随机生成比赛结果,来判断最终归属,现实中比赛情况太复杂了,我实在找不到一个有效办法来模拟,比如诺里斯的PU到底会不会爆缸,比如红牛会不会调车的时候反向升级,迈凯伦会不会又底板低于9毫米,所以只能用随机数来实现

    CREATE PROCEDURE batch_simulation(IN p_total_times INT)
    BEGIN
      DECLARE v_sim_no INT DEFAULT 1;
      -- 每站车手状态变量(退赛、PU更换、排名)
      DECLARE v_norris_retired INT;
      DECLARE v_norris_change_pu INT;
      DECLARE v_norris_rank INT;
      DECLARE v_piastri_retired INT;
      DECLARE v_piastri_change_pu INT;
      DECLARE v_piastri_rank INT;
      DECLARE v_verstappen_retired INT;
      DECLARE v_verstappen_change_pu INT;
      DECLARE v_verstappen_rank INT;
      DECLARE v_retire_reason VARCHAR(20);
      -- 统计变量(存储最终统计结果)
      DECLARE v_norris_win INT DEFAULT 0;
      DECLARE v_piastri_win INT DEFAULT 0;
      DECLARE v_verstappen_win INT DEFAULT 0;
      DECLARE v_norris_avg DECIMAL(10,2) DEFAULT 0;
      DECLARE v_piastri_avg DECIMAL(10,2) DEFAULT 0;
      DECLARE v_verstappen_avg DECIMAL(10,2) DEFAULT 0;
      DECLARE v_total_avg DECIMAL(10,2) DEFAULT 0;
    
      -- 1. 初始化:清空历史模拟结果
      DELETE FROM simulation_results;
    
      -- 2. 循环执行模拟(按指定次数重复模拟剩余赛事)
      WHILE v_sim_no <= p_total_times DO
        -- (1)重置本次模拟环境(确保每轮模拟独立)
        DELETE FROM driver_race_scores;
        DELETE FROM race_events;
        UPDATE drivers SET final_points = 0, is_champion = 0;
        UPDATE remaining_races SET is_completed = 0;
    
        -- (2)随机生成退赛原因(3种常见故障随机)
        SET v_retire_reason = CASE FLOOR(RAND()*3)+1 
          WHEN 1 THEN 'PU爆缸' 
          WHEN 2 THEN '机械故障' 
          WHEN 3 THEN '碰撞' 
        END;
    
        -- (3)模拟第1站:卡塔尔冲刺赛(10%退赛概率,5%PU更换概率)
        SET v_norris_retired = IF(RAND() < 0.1, 1, 0);
        SET v_norris_change_pu = IF(RAND() < 0.05, 1, 0);
        SET v_norris_rank = IF(v_norris_retired = 1, 0, FLOOR(RAND()*10)+1);
        SET v_piastri_retired = IF(RAND() < 0.1, 1, 0);
        SET v_piastri_change_pu = IF(RAND() < 0.05, 1, 0);
        SET v_piastri_rank = IF(v_piastri_retired = 1, 0, FLOOR(RAND()*10)+1);
        SET v_verstappen_retired = IF(RAND() < 0.1, 1, 0);
        SET v_verstappen_change_pu = IF(RAND() < 0.05, 1, 0);
        SET v_verstappen_rank = IF(v_verstappen_retired = 1, 0, FLOOR(RAND()*10)+1);
        CALL record_race_event(1, v_norris_retired, IF(v_norris_retired=1, v_retire_reason, '无'), v_norris_change_pu, v_norris_rank, v_piastri_retired, IF(v_piastri_retired=1, v_retire_reason, '无'), v_piastri_change_pu, v_piastri_rank, v_verstappen_retired, IF(v_verstappen_retired=1, v_retire_reason, '无'), v_verstappen_change_pu, v_verstappen_rank);
    
        -- (4)模拟第2站:卡塔尔正赛
        SET v_norris_retired = IF(RAND() < 0.1, 1, 0);
        SET v_norris_change_pu = IF(RAND() < 0.05, 1, 0);
        SET v_norris_rank = IF(v_norris_retired = 1, 0, FLOOR(RAND()*10)+1);
        SET v_piastri_retired = IF(RAND() < 0.1, 1, 0);
        SET v_piastri_change_pu = IF(RAND() < 0.05, 1, 0);
        SET v_piastri_rank = IF(v_piastri_retired = 1, 0, FLOOR(RAND()*10)+1);
        SET v_verstappen_retired = IF(RAND() < 0.1, 1, 0);
        SET v_verstappen_change_pu = IF(RAND() < 0.05, 1, 0);
        SET v_verstappen_rank = IF(v_verstappen_retired = 1, 0, FLOOR(RAND()*10)+1);
        CALL record_race_event(2, v_norris_retired, IF(v_norris_retired=1, v_retire_reason, '无'), v_norris_change_pu, v_norris_rank, v_piastri_retired, IF(v_piastri_retired=1, v_retire_reason, '无'), v_piastri_change_pu, v_piastri_rank, v_verstappen_retired, IF(v_verstappen_retired=1, v_retire_reason, '无'), v_verstappen_change_pu, v_verstappen_rank);
    
        -- (5)模拟第3站:阿布扎比正赛
        SET v_norris_retired = IF(RAND() < 0.1, 1, 0);
        SET v_norris_change_pu = IF(RAND() < 0.05, 1, 0);
        SET v_norris_rank = IF(v_norris_retired = 1, 0, FLOOR(RAND()*10)+1);
        SET v_piastri_retired = IF(RAND() < 0.1, 1, 0);
        SET v_piastri_change_pu = IF(RAND() < 0.05, 1, 0);
        SET v_piastri_rank = IF(v_piastri_retired = 1, 0, FLOOR(RAND()*10)+1);
        SET v_verstappen_retired = IF(RAND() < 0.1, 1, 0);
        SET v_verstappen_change_pu = IF(RAND() < 0.05, 1, 0);
        SET v_verstappen_rank = IF(v_verstappen_retired = 1, 0, FLOOR(RAND()*10)+1);
        CALL record_race_event(3, v_norris_retired, IF(v_norris_retired=1, v_retire_reason, '无'), v_norris_change_pu, v_norris_rank, v_piastri_retired, IF(v_piastri_retired=1, v_retire_reason, '无'), v_piastri_change_pu, v_piastri_rank, v_verstappen_retired, IF(v_verstappen_retired=1, v_retire_reason, '无'), v_verstappen_change_pu, v_verstappen_rank);
    
        -- (6)计算本次模拟的冠军归属
        CALL calculate_champion(v_sim_no);
    
        -- (7)模拟序号+1,进入下一轮
        SET v_sim_no = v_sim_no + 1;
      END WHILE;
    
      -- 3. 计算最终统计数据(夺冠次数以及平均积分)
      SELECT COUNT(CASE WHEN champion_name = '诺里斯' THEN 1 ELSE NULL END) INTO v_norris_win FROM simulation_results;
      SELECT COUNT(CASE WHEN champion_name = '皮亚斯特里' THEN 1 ELSE NULL END) INTO v_piastri_win FROM simulation_results;
      SELECT COUNT(CASE WHEN champion_name = '维斯塔潘' THEN 1 ELSE NULL END) INTO v_verstappen_win FROM simulation_results;
    
      SELECT ROUND(AVG(norris_final), 2) INTO v_norris_avg FROM simulation_results;
      SELECT ROUND(AVG(piastri_final), 2) INTO v_piastri_avg FROM simulation_results;
      SELECT ROUND(AVG(verstappen_final), 2) INTO v_verstappen_avg FROM simulation_results;
    
      -- 计算三车手平均积分均值
      SET v_total_avg = (v_norris_avg + v_piastri_avg + v_verstappen_avg) / 3;
      SET v_total_avg = ROUND(v_total_avg, 2);
    
      -- 4. 输出统计结果
      SELECT '诺里斯' AS 车手姓名, v_norris_win AS 夺冠次数, ROUND((v_norris_win / p_total_times) * 100, 2) AS 夺冠概率_百分比, v_norris_avg AS 平均最终积分 FROM DUAL;
      SELECT '皮亚斯特里' AS 车手姓名, v_piastri_win AS 夺冠次数, ROUND((v_piastri_win / p_total_times) * 100, 2) AS 夺冠概率_百分比, v_piastri_avg AS 平均最终积分 FROM DUAL;
      SELECT '维斯塔潘' AS 车手姓名, v_verstappen_win AS 夺冠次数, ROUND((v_verstappen_win / p_total_times) * 100, 2) AS 夺冠概率_百分比, v_verstappen_avg AS 平均最终积分 FROM DUAL;
      SELECT '合计' AS 车手姓名, p_total_times AS 夺冠次数, 100.00 AS 夺冠概率_百分比, v_total_avg AS 平均最终积分 FROM DUAL;
    END //
    
    DELIMITER ;
    

2.3 模拟结果

准备工作都完成以后,我们来使用第三个存储过程模拟一下,模拟次数1000次

    mysql> CALL batch_simulation(100);

结果如下:

    +-----------------+--------------+
    | 车手姓名        | 最终积分     |
    +-----------------+--------------+
    | 诺里斯          |          406 |
    | 皮亚斯特里      |          390 |
    | 维斯塔潘        |          386 |
    +-----------------+--------------+
    3 rows in set (18.22 sec)
    
    +-----------------+--------------+
    | 车手姓名        | 最终积分     |
    +-----------------+--------------+
    | 诺里斯          |          403 |
    | 皮亚斯特里      |          379 |
    | 维斯塔潘        |          399 |
    +-----------------+--------------+
    3 rows in set (18.22 sec)
    
    +-----------------+--------------+
    | 车手姓名        | 最终积分     |
    +-----------------+--------------+
    | 诺里斯          |          431 |
    | 皮亚斯特里      |          401 |
    | 维斯塔潘        |          385 |
    +-----------------+--------------+
    3 rows in set (18.22 sec)
    

结果是,1000次模拟,诺里斯夺冠893次,皮亚斯特里夺冠50次,维斯塔潘夺冠57次,这个结果算是意料之中,因为诺里斯手握24分的领先优势,以及我之前所说的,场上变量太多了,根本没法精准预测。

3. 测试结论

从这个实验,来对 seekdb 初探测试的结果做个小结,主要测试哪些功能点,以及实际反馈如何。

1.DDL 功能
CREATE DATABASECREATE TABLEDRO PTABLE/PROCEDUREALTER TABLE、约束定义等操作,与 MySQL 兼容性一致,但是缺少枚举类型,这是和 MySQL 不同的地方,所以一开始想要用 enum 来写一些字段改成了varchar+约束

2.DML 功能
NSERTDELETEUPDATE语句,这些与MySQLOceanBase保持一致,符合预期

3.存储过程
3个嵌套调用的存储过程里,含变量声明、循环、条件判断、函数调用,测试结果符合预期。

4.事务与数据一致性
每次模拟,包含了insert/update/delete,以及隐式事务,没有显式声明 TRANSACTION,依赖数据库自动事务提交,最终 ACID 都能实现,这部分能力与 MySQL 以及 OceanBase 一致。

5.查询与统计分析能力
存储过程中的SELECT INTO变量赋值、聚合查询、UNIONALL 简单合并查询,都能够通过,符合预期

6.资源使用情况

cpu 使用率是5.6,内存使用率是7.7%,实际使用情况低于1c2g

最终测试的结论是

  1. 基础兼容性,能够实现企业级SQL标准支持
  2. 数据可靠性:事务与约束都符合预期
  3. 复杂业务支撑:存储过程可用,性能不拖后腿
  4. 易用性与迁移成本:脚本无需修改仅微调字符集配置,就可以从OceanBase迁移至seekdb
  5. 多语言支持:多中文数据存储与输出无乱码,,能满足中文等非英语环境的业务需求,适配国内企业应用场景。
  6. 资源使用率:较少的资源即可完成稳定的功能支持。

麻雀虽小(相对于OceanBase),五脏俱全。除了不支持分布式,实际上大部分OceanBase的单机常用的功能都可以实现。

【有奖竞猜】
本文对 OceanBase seekdb 做了一次全功能探针测试。通过构建一套 F1 赛季末积分模拟系统,实测了 DDL、事务、复杂存储过程及资源开销。今晚开始看看 seekdb 预测的准不准,【有奖竞猜】前3位猜中者获得50积分

8 个赞

我的竞猜结果:
冠军:诺里斯
亚军:维斯塔潘
季军:皮亚斯特里

11 个赞

我的竞猜是:
冠军: 诺里斯
亚军: 皮亚斯特里
季军: 维斯塔潘

9 个赞

我的竞猜结果:
冠军:诺里斯
亚军:皮亚斯特里
季军:维斯塔潘

11 个赞

我的竞猜结果:
冠军:诺里斯
亚军:维斯塔潘
季军:皮亚斯特里

11 个赞

10 个赞

他们的速度太快了,别人更本没有机会

15 个赞

我的竞猜结果:
冠军:维斯塔潘
亚军:诺里斯
季军:皮亚斯特里

14 个赞

那俺增加名额,6个奖!!!

15 个赞

谁是高手告诉我

16 个赞

:hand_with_index_finger_and_thumb_crossed: :hand_with_index_finger_and_thumb_crossed: :hand_with_index_finger_and_thumb_crossed: :hand_with_index_finger_and_thumb_crossed: :hand_with_index_finger_and_thumb_crossed: :hand_with_index_finger_and_thumb_crossed: :hand_with_index_finger_and_thumb_crossed: :hand_with_index_finger_and_thumb_crossed:

13 个赞

我的竞猜结果:
冠军:诺里斯
亚军:维斯塔潘
季军:皮亚斯特里

11 个赞

我的竞猜是:
冠军: 诺里斯
亚军: 皮亚斯特里
季军: 维斯塔潘

8 个赞

我的竞猜结果:
冠军:维斯塔潘
亚军:诺里斯
季军:皮亚斯特里

5 个赞

:+1: :+1: :+1:

6 个赞

太好了

6 个赞

:grinning: :grinning: :grinning:

7 个赞

:+1: :+1: :+1:

6 个赞

6666

6 个赞