暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

让seekdb告诉你,谁是今年的F1车手总冠军

原创 多明戈教你玩狼人杀 2025-11-27
800

还是回到上周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)mysql> select * from drivers;
+-----------+-----------------+----------------+-----------+--------------+-------------+---------------------+
| driver_id | driver_name     | initial_points | race_wins | final_points | is_champion | create_time         |
+-----------+-----------------+----------------+-----------+--------------+-------------+---------------------+
|         1 | 诺里斯          |            390 |         6 |          424 |           1 | 2025-11-27 11:03:17 |
|         2 | 皮亚斯特里      |            366 |         7 |          373 |           0 | 2025-11-27 11:03:17 |
|         3 | 维斯塔潘        |            366 |         6 |          398 |           0 | 2025-11-27 11:03:17 |
+-----------+-----------------+----------------+-----------+--------------+-------------+---------------------+
3 rows in set (0.00 sec)
mysql> select * from points_rule;
+---------+-----------+------+--------+
| rule_id | race_type | rank | points |
+---------+-----------+------+--------+
|       1 | 冲刺赛    |    1 |      8 |
|       2 | 冲刺赛    |    2 |      7 |
|       3 | 冲刺赛    |    3 |      6 |
|       4 | 冲刺赛    |    4 |      5 |
|       5 | 冲刺赛    |    5 |      4 |
|       6 | 冲刺赛    |    6 |      3 |
|       7 | 冲刺赛    |    7 |      2 |
|       8 | 冲刺赛    |    8 |      1 |
|       9 | 冲刺赛    |    9 |      0 |
|      10 | 冲刺赛    |   10 |      0 |
|      11 | 正赛      |    1 |     25 |
|      12 | 正赛      |    2 |     18 |
|      13 | 正赛      |    3 |     15 |
|      14 | 正赛      |    4 |     12 |
|      15 | 正赛      |    5 |     10 |
|      16 | 正赛      |    6 |      8 |
|      17 | 正赛      |    7 |      6 |
|      18 | 正赛      |    8 |      4 |
|      19 | 正赛      |    9 |      2 |
|      20 | 正赛      |   10 |      1 |
+---------+-----------+------+--------+
20 rows in set (0.01 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)


.....(次数太多,略) +--------------+--------------+------------------------+--------------------+ | 车手姓名 | 夺冠次数 | 夺冠概率_百分比 | 平均最终积分 | +--------------+--------------+------------------------+--------------------+ | 诺里斯 | 893 | 89.30 | 411.37 | +--------------+--------------+------------------------+--------------------+ 1 row in set (18.22 sec) +-----------------+--------------+------------------------+--------------------+ | 车手姓名 | 夺冠次数 | 夺冠概率_百分比 | 平均最终积分 | +-----------------+--------------+------------------------+--------------------+ | 皮亚斯特里 | 50 | 5.00 | 387.00 | +-----------------+--------------+------------------------+--------------------+ 1 row in set (18.22 sec) +--------------+--------------+------------------------+--------------------+ | 车手姓名 | 夺冠次数 | 夺冠概率_百分比 | 平均最终积分 | +--------------+--------------+------------------------+--------------------+ | 维斯塔潘 | 57 | 5.70 | 387.57 | +--------------+--------------+------------------------+--------------------+ 1 row in set (18.22 sec) +--------------+--------------+------------------------+--------------------+ | 车手姓名 | 夺冠次数 | 夺冠概率_百分比 | 平均最终积分 | +--------------+--------------+------------------------+--------------------+ | 合计 | 1000 | 100.00 | 395.31 | +--------------+--------------+------------------------+--------------------+ 1 row in set (18.22 sec) Query OK, 0 rows affected (18.22 sec)

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


3. 测试结论

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

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

2.DML功能
NSERT、DELETE、UPDATE语句,这些与MySQL和OceanBase保持一致,符合预期

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怎么取舍?

最后修改时间:2025-11-28 10:02:39
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论