还是回到上周 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 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
最终测试的结论是
- 基础兼容性,能够实现企业级SQL标准支持
- 数据可靠性:事务与约束都符合预期
- 复杂业务支撑:存储过程可用,性能不拖后腿
- 易用性与迁移成本:脚本无需修改仅微调字符集配置,就可以从OceanBase迁移至seekdb
- 多语言支持:多中文数据存储与输出无乱码,,能满足中文等非英语环境的业务需求,适配国内企业应用场景。
- 资源使用率:较少的资源即可完成稳定的功能支持。
麻雀虽小(相对于OceanBase),五脏俱全。除了不支持分布式,实际上大部分OceanBase的单机常用的功能都可以实现。
【有奖竞猜】
本文对 OceanBase seekdb 做了一次全功能探针测试。通过构建一套 F1 赛季末积分模拟系统,实测了 DDL、事务、复杂存储过程及资源开销。今晚开始看看 seekdb 预测的准不准,【有奖竞猜】前3位猜中者获得50积分
