作者:ShunWah
在运维管理领域,我拥有多年深厚的专业积累,兼具坚实的理论基础与广泛的实践经验。精通运维自动化流程,对于OceanBase、MySQL等多种数据库的部署与运维,具备从初始部署到后期维护的全链条管理能力。拥有OceanBase的OBCA和OBCP认证、OpenGauss社区认证结业证书,以及崖山DBCA、亚信AntDBCA、翰高HDCA、GBase 8a | 8c | 8s、Galaxybase的GBCA、Neo4j的Graph Data Science Certification、NebulaGraph的NGCI & NGCP、东方通TongTech TCPE等多项权威认证。
在OceanBase & 墨天轮的技术征文大赛中,多次荣获一、二、三等奖。同时,在OpenGauss第五届、第六届、第七届技术征文大赛,TiDB社区专栏征文大赛,金仓数据库有奖征文活动,以及YashanDB「产品体验官」征文等活动中,我也屡获殊荣。此外,我还活跃于墨天轮、CSDN、ITPUB等技术平台,经常发布原创技术文章,并多次被首页推荐。
引言
浪潮数据库KWDB2.2.0:数据分析与性能调优深度对比实战指南
随着开源社区的蓬勃发展,越来越多的优秀项目涌现出来,为开发者们提供了丰富的工具和资源。其中,KWDB 作为一款面向AIoT场景的分布式多模数据库,自2024年开源以来,凭借其强大的功能和灵活的架构,吸引了众多开发者的关注。【KWDB创作者计划】更是为KWDB带来了全新的玩法和可能性。
在本文中,我们将详细介绍如何使用KWDB 2.2.0,并通过一系列实操命令,展示如何解锁KWDB的新玩法。
KWDB 2.2.0的新功能概览
KWDB 2.2.0版本带来了许多新功能和改进,以下是其中一些亮点:
多模数据支持增强
支持更多数据类型,包括JSON、图像、视频和音频,适用于复杂的AIoT场景。
分布式性能优化
提高了分布式环境下的数据同步和查询性能,适合大规模部署。
新增API接口
提供了更多便捷的API,简化了数据库操作流程。
用户界面改进
新增了一个更直观的控制台界面,方便用户管理数据库。
一、安装KWDB 2.2.0
首先,我们需要安装KWDB 2.2.0版本。以下是安装步骤:
1. 环境准备
确保你的开发环境满足KWDB的运行要求。KWDB支持多种操作系统,包括Linux、macOS和Windows。
本文以 x86 架构的 CentOS Linux 7.4 镜像作为环境介绍如何使用 docker容器化部署 KWDB2.2.0 数据库。
[root@worker3 kaiwudb]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)
[root@worker3 kaiwudb]# free -h
total used free shared buff/cache available
Mem: 27G 1.8G 10G 313M 15G 24G
Swap: 8.0G 0B 8.0G
[root@worker3 kaiwudb]#
2. 安装命令
由于我已提前完成安装,这里不再重复演示。如需详细安装步骤,请参考我的上一篇文章:
【KWDB创作者计划】容器赋能KaiwuDB:探索浪潮数据库KWDB2.2.0 实战指南
3. 验证安装
安装完成后,在终端中运行以下命令验证安装是否成功:
[root@worker3 kwdb]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
40305e626095 kwdb/kwdb:2.2.0 "/bin/bash -c '/kaiw…" 9 days ago Up 9 days 0.0.0.0:8080->8080/tcp, 0.0.0.0:26257->26257/tcp kaiwudb-experience
[root@worker3 kwdb]# docker exec -it kaiwudb-experience bash
root@kaiwudb-experience:/kaiwudb/bin# ./kwbase sql --insecure --host=127.0.0.1
#
# Welcome to the KWDB SQL shell.
# All statements must be terminated by a semicolon.
# To exit, type: \q.
#
# Server version: KaiwuDB 2.2.0 (x86_64-linux-gnu, built 2025/03/31 07:20:02, go1.16.15, gcc 11.4.0) (same version as client)
# Cluster ID: b565096e-c243-4399-8aa0-6ec3ea6b77a5
#
# Enter \? for a brief introduction.
#
root@127.0.0.1:26257/defaultdb>
首先,确保你已经成功部署了 KWDB 2.2.0。或你可以从 Gitee 上下载最新版的 KWDB 2.2.0 安装包,并按照官方文档进行安装和配置。安装完成后,启动 KWDB 服务,并确保其正常运行。
二、KWDB 联合查询数据分析
现在,我们将用 KWDB 进行创建数据库和表。
1、创建数据库
root@127.0.0.1:26257/example_db> CREATE DATABASE testdb; CREATE DATABASE Time: 6.130957ms root@127.0.0.1:26257/example_db>
切换到新创建的数据库
root@127.0.0.1:26257/example_db> USE testdb; SET Time: 1.416482ms root@127.0.0.1:26257/testdb>
2、创建数据库表
2.1 创建时序表,用于存储设备指标数据
root@127.0.0.1:26257/testdb> CREATE TABLE device_metrics ( temperature FLOAT, humidity FLOAT, -> time TIMESTAMP NOT NULL, -> device_id STRING NOT NULL, PRIMARY KEY (time, device_id) ); -> temperature FLOAT, -> humidity FLOAT, -> PRIMARY KEY (time, device_id) -> ); CREATE TABLE Time: 5.415833ms root@127.0.0.1:26257/testdb>
2.2 创建用户信息表,用于存储用户基本信息
root@127.0.0.1:26257/testdb> CREATE TABLE user_info ( username STRING NOT NULL, email STRING, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -> user_id INT PRIMARY KEY, -> username STRING NOT NULL, -> email STRING, -> created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -> ); CREATE TABLE Time: 5.662591ms root@127.0.0.1:26257/testdb>
3. 插入测试数据
创建好数据库和表后,我们插入一些测试数据,以便后续进行查询和优化操作。
root@127.0.0.1:26257/testdb> INSERT INTO device_metrics VALUES ('2021-09-15 12:30:00', 'device2', 26.1, 44.8), ('2022-03-20 15:45:00', 'device3', 27.0, 43.5), ('2023-07-10 08:15:00', 'device1', 25.7, 44.9), ('2024-01-25 11:00:00', 'device2', 26.3, 44.6), ('2024-08-05 14:20:00', 'device3', 27.2, 43.3), -> ('2020-06-01 10:00:00', 'device1', 25.3, 45.2), ('2025-02-14 09:40:00', 'device1', 25.9, 44.7), ('2025-05-30 16:50:00', 'device2', 26.5, 44.4), ('2025-09-12 13:10:00', 'device3', 27.4, 43.1), ('2025-12-25 18:35:00', 'device1', 26.0, 44.0); -> ('2021-09-15 12:30:00', 'device2', 26.1, 44.8), -> ('2022-03-20 15:45:00', 'device3', 27.0, 43.5), -> ('2023-07-10 08:15:00', 'device1', 25.7, 44.9), -> ('2024-01-25 11:00:00', 'device2', 26.3, 44.6), -> ('2024-08-05 14:20:00', 'device3', 27.2, 43.3), -> ('2025-02-14 09:40:00', 'device1', 25.9, 44.7), -> ('2025-05-30 16:50:00', 'device2', 26.5, 44.4), -> ('2025-09-12 13:10:00', 'device3', 27.4, 43.1), -> ('2025-12-25 18:35:00', 'device1', 26.0, 44.0); INSERT 10 Time: 1.498235ms root@127.0.0.1:26257/testdb>
4、查看全部数据
root@127.0.0.1:26257/testdb> SELECT * FROM device_metrics; time | device_id | temperature | humidity ----------------------------+-----------+-------------+----------- 2020-06-01 10:00:00+00:00 | device1 | 25.3 | 45.2 2021-09-15 12:30:00+00:00 | device2 | 26.1 | 44.8 2022-03-20 15:45:00+00:00 | device3 | 27 | 43.5 2023-07-10 08:15:00+00:00 | device1 | 25.7 | 44.9 2024-01-25 11:00:00+00:00 | device2 | 26.3 | 44.6 2024-08-05 14:20:00+00:00 | device3 | 27.2 | 43.3 2025-02-14 09:40:00+00:00 | device1 | 25.9 | 44.7 2025-05-30 16:50:00+00:00 | device2 | 26.5 | 44.4 2025-09-12 13:10:00+00:00 | device3 | 27.4 | 43.1 2025-12-25 18:35:00+00:00 | device1 | 26 | 44 (10 rows) Time: 1.628286ms root@127.0.0.1:26257/testdb>
5、插入用户信息数据
root@127.0.0.1:26257/testdb> root@127.0.0.1:26257/testdb> INSERT INTO user_info (user_id, username, email) VALUES (1, 'alice', 'alice@example.com'), -> (1, 'alice', 'alice@example.com'), (2, 'bob', 'bob@example.com'), (3, 'charlie', 'charlie@example.com'); -> (2, 'bob', 'bob@example.com'), -> (3, 'charlie', 'charlie@example.com'); INSERT 3 Time: 1.800617ms root@127.0.0.1:26257/testdb>
6、数据查询与优化
创建好数据库和表,并插入测试数据后,我们可以通过 MCP Server 执行查询操作,并尝试对查询进行优化。
6.1 简单查询设备指标数据
root@127.0.0.1:26257/testdb> SELECT * FROM device_metrics WHERE device_id = 'device1'; time | device_id | temperature | humidity ----------------------------+-----------+-------------+----------- 2020-06-01 10:00:00+00:00 | device1 | 25.3 | 45.2 2023-07-10 08:15:00+00:00 | device1 | 25.7 | 44.9 2025-02-14 09:40:00+00:00 | device1 | 25.9 | 44.7 2025-12-25 18:35:00+00:00 | device1 | 26 | 44 (4 rows) Time: 2.05378ms root@127.0.0.1:26257/testdb>
6.2 时间范围查询设备指标数据
root@127.0.0.1:26257/testdb> SELECT * FROM device_metrics WHERE time > '2025-01-01 09:00:00' AND time < '2025-12-30 10:15:00'; time | device_id | temperature | humidity ----------------------------+-----------+-------------+----------- 2025-02-14 09:40:00+00:00 | device1 | 25.9 | 44.7 2025-05-30 16:50:00+00:00 | device2 | 26.5 | 44.4 2025-09-12 13:10:00+00:00 | device3 | 27.4 | 43.1 2025-12-25 18:35:00+00:00 | device1 | 26 | 44 (4 rows) Time: 1.635945ms root@127.0.0.1:26257/testdb>
6.3 聚合查询设备平均温度
root@127.0.0.1:26257/testdb> SELECT device_id, AVG(temperature) AS avg_temp WHERE time > NOW() - INTERVAL '1 hour' GROUP BY device_id; -> FROM device_metrics -> WHERE time > NOW() - INTERVAL '1 hour' -> GROUP BY device_id; device_id | avg_temp ------------+----------- device1 | 26 device2 | 26.5 device3 | 27.4 (3 rows) Time: 2.286525ms root@127.0.0.1:26257/testdb>
6.4 查询用户信息
root@127.0.0.1:26257/testdb> SELECT * FROM user_info; user_id | username | email | created_at ----------+----------+---------------------+----------------------------------- 1 | alice | alice@example.com | 2025-04-18 06:51:45.654229+00:00 2 | bob | bob@example.com | 2025-04-18 06:51:45.654229+00:00 3 | charlie | charlie@example.com | 2025-04-18 06:51:45.654229+00:00 (3 rows) Time: 1.465467ms root@127.0.0.1:26257/testdb>
6.5 优化查询:为 device_metrics 表的 device_id 列创建索引
root@127.0.0.1:26257/testdb> CREATE INDEX idx_device_id ON device_metrics (device_id); CREATE INDEX Time: 173.51779ms root@127.0.0.1:26257/testdb>
6.6 再次执行简单查询,观察性能变化
root@127.0.0.1:26257/testdb> SELECT * FROM device_metrics WHERE device_id = 'device1'; time | device_id | temperature | humidity ----------------------------+-----------+-------------+----------- 2020-06-01 10:00:00+00:00 | device1 | 25.3 | 45.2 2023-07-10 08:15:00+00:00 | device1 | 25.7 | 44.9 2025-02-14 09:40:00+00:00 | device1 | 25.9 | 44.7 2025-12-25 18:35:00+00:00 | device1 | 26 | 44 (4 rows) Time: 1.805047ms root@127.0.0.1:26257/testdb>
在查询过程中,我们可能会发现某些查询的性能不够理想。这时,我们可以尝试对 KWDB 进行优化,比如调整索引、分区策略等。在上述示例中,我们为 device_metrics
表的 device_id
列创建了索引,以加速基于 device_id
的查询操作。
7. 高级查询与数据分析
除了基本的查询操作外,我们还可以利用 KWDB 进行更高级的数据分析和处理。在实际业务场景中,数据库系统通常需要处理复杂的关联查询和数据分析任务。例如:
用户(user_info 表)与设备(device_metrics 表)之间的关联。每个用户可能绑定一个或多个设备,并且需要根据设备的最新数据进行统计和分析。本案例通过以下步骤展示了如何利用 KWDB 进行高级查询和数据分析:
创建用户设备关联表(user_device_mapping),用于存储用户与设备的绑定关系。
插入示例数据,模拟用户与设备的实际关联。
使用多表联合查询(JOIN)获取每个用户的最新设备数据,并按用户名排序。
7.1 首先,创建用户设备关联表
root@127.0.0.1:26257/testdb> CREATE TABLE user_device_mapping ( user_id INT, device_id STRING, PRIMARY KEY (user_id) ); -> user_id INT, -> device_id STRING, -> PRIMARY KEY (user_id) -> ); CREATE TABLE Time: 6.371418ms root@127.0.0.1:26257/testdb>
目的:建立用户与设备的映射关系。
字段说明:
user_id:用户 ID,主键,确保每个用户只能绑定一个设备。
device_id:设备 ID,表示用户绑定的设备。
7.2 插入一些示例数据
root@127.0.0.1:26257/testdb> INSERT INTO user_device_mapping (user_id, device_id) VALUES (3, 'device3'); -> (1, 'device1'), -> (2, 'device2'), -> (3, 'device3'); INSERT 3 Time: 1.261248ms root@127.0.0.1:26257/testdb>
数据解释:
用户 1 绑定设备 device1。
用户 2 绑定设备 device2。
用户 3 绑定设备 device3。
7.3 然后,进行复杂关联查询
以下是核心查询语句,用于获取每个用户的最新设备数据:
查询逻辑解析
用户信息表(user_info)包含用户的基本信息,如用户名(username)。通过 user_id 与用户设备关联表(user_device_mapping)连接。
用户设备关联表(user_device_mapping)存储用户与设备的绑定关系。通过 device_id 与设备指标表(device_metrics)连接。
设备指标表(device_metrics)存储设备的时间序列数据,如温度(temperature)、湿度(humidity)等。需要提取每个设备的最新数据。
子查询(latest)从 device_metrics 表中提取每个设备的最新时间戳(MAX(time))。通过 GROUP BY device_id 确保每个设备只返回一条记录。
最终联合查询将所有表连接起来,筛选出每个设备的最新数据。
按用户名(u.username)排序,便于阅读和分析。
root@127.0.0.1:26257/testdb> SELECT dm.humidity FROM user_info u JOIN user_device_mapping udm ON u.user_id = udm.user_id JOIN device_metrics dm ON udm.device_id = dm.device_id JOIN ( SELECT device_id, MAX(time) AS m -> ax_time u.username, GROUP BY device_id ) latest ON dm.device_id = latest.device_id AND dm.time = latest.max_time ORDER BY u.username; -> dm.time, -> dm.temperature, -> dm.humidity -> FROM -> user_info u -> JOIN user_device_mapping udm ON u.user_id = udm.user_id -> JOIN device_metrics dm ON udm.device_id = dm.device_id -> JOIN ( -> SELECT -> device_id, -> MAX(time) AS max_time -> FROM -> device_metrics -> GROUP BY -> device_id -> ) latest ON dm.device_id = latest.device_id AND dm.time = latest.max_time -> ORDER BY -> u.username; username | time | temperature | humidity -----------+---------------------------+-------------+----------- alice | 2025-12-25 18:35:00+00:00 | 26 | 44 bob | 2025-05-30 16:50:00+00:00 | 26.5 | 44.4 charlie | 2025-09-12 13:10:00+00:00 | 27.4 | 43.1 (3 rows) Time: 3.899709ms root@127.0.0.1:26257/testdb>
结果解释:
用户 alice 的设备 device3 在 2025-12-25 18:35:00 的最新数据显示温度为 26℃,湿度为 44%。
用户 bob 的设备 device1 在 2025-05-30 16:50:00 的最新数据显示温度为 26.5℃,湿度为 44.4%。
用户 charlie 的设备 device2 在 2025-09-12 13:10:00 的最新数据显示温度为 27.4℃,湿度为 43.1%。
多表联合查询(JOIN)
通过 JOIN 将多个表(user_info、user_device_mapping、device_metrics)连接起来,实现复杂的数据关联。使用子查询提取每个设备的最新时间戳,确保查询结果的准确性。聚合函数(GROUP BY 和 MAX)使用 GROUP BY 和 MAX 提取每个设备的最新数据。这是时间序列数据分析中的常见模式。排序(ORDER BY)按用户名排序,使结果更具可读性。
三、KWDB 优化和性能对比
1. 连接到 KWDB
启动 KWDB 的 SQL Shell,进入交互式命令行。
root@kaiwudb-experience:/kaiwudb/bin# ./kwbase sql --insecure --host=127.0.0.1 # # Welcome to the KWDB SQL shell. # All statements must be terminated by a semicolon. # To exit, type: \q. # # Server version: KaiwuDB 2.2.0 (x86_64-linux-gnu, built 2025/03/31 07:20:02, go1.16.15, gcc 11.4.0) (same version as client) # Cluster ID: b565096e-c243-4399-8aa0-6ec3ea6b77a5 # # Enter \? for a brief introduction. # root@127.0.0.1:26257/defaultdb>
2. 创建数据库
2.1 创建一个新的数据库 example_db
root@127.0.0.1:26257/defaultdb> CREATE DATABASE example_db; CREATE DATABASE Time: 5.737184ms root@127.0.0.1:26257/defaultdb>
2.2 使用 USE example_db; 切换到新创建的数据库。
root@127.0.0.1:26257/defaultdb> USE example_db; SET Time: 682.48µs root@127.0.0.1:26257/example_db>
3. 创建用户信息表
创建一个用户信息表,用于存储用户的基本信息。
字段说明:
id:主键,自增序列(SERIAL 类型)。
username:用户名,长度限制为 50 字符,不能为空。
email:邮箱地址,长度限制为 100 字符。
age:用户年龄,整数类型。
created_at:记录创建时间,默认值为当前时间戳。
root@127.0.0.1:26257/example_db> CREATE TABLE users ( age INT, -> id SERIAL PRIMARY KEY, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -> username VARCHAR(50) NOT NULL, -> email VARCHAR(100), -> age INT, -> created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -> ); CREATE TABLE Time: 6.163336ms root@127.0.0.1:26257/example_db>
4、新插入数据
批量插入 9001 条用户数据。
关键点:
generate_series(1000, 10000)
生成一系列整数(从 1000 到 10000),用作临时 ID。
动态生成用户名和邮箱
‘user_’ || id::TEXT || ‘’ || (floor(random() * 1000))::TEXT:构造随机用户名。
'user’ || id::TEXT || ‘@test.com’:构造唯一的邮箱地址。
随机生成年龄
floor(25 + random() * 20)::int:生成范围在 25 到 44 之间的随机整数。
批量插入
使用 INSERT INTO … SELECT 语法高效地插入大量数据。
root@127.0.0.1:26257/example_db> INSERT INTO users (username, email, age) 'user_' || id::TEXT || '@test.com', floor(25 + random() * 20)::int FROM generate_series(1000, 10000) AS id; -> SELECT -> 'user_' || id::TEXT || '_' || (floor(random() * 1000))::TEXT, -> 'user_' || id::TEXT || '@test.com', -> floor(25 + random() * 20)::int -> FROM generate_series(1000, 10000) AS id; INSERT 9001 Time: 257.076009ms root@127.0.0.1:26257/example_db>
5、查询验证数据
查询表中的前 50 条记录,验证数据插入是否成功。
root@127.0.0.1:26257/example_db> SELECT * FROM users LIMIT 50;
id | username | email | age | created_at
----------------------+------------------+-----------------------+-----+-----------------------------------
1064580450243248129 | john_doe | john@example.com | 28 | 2025-04-18 05:36:10.606017+00:00
1064580450243346433 | jane_doe | jane@example.com | 32 | 2025-04-18 05:36:10.606017+00:00
1064580450243379201 | alice_smith | alice@example.com | 25 | 2025-04-18 05:36:10.606017+00:00
1064580450243411969 | bob_jones | bob@example.com | 40 | 2025-04-18 05:36:10.606017+00:00
1064580450243444737 | charlie_brown | charlie@example.com | 35 | 2025-04-18 05:36:10.606017+00:00
1065191668815233025 | john_doe_1 | john1@example.com | 28 | 2025-04-20 09:24:59.710609+00:00
1065191668815396865 | jane_doe_2 | jane2@example.com | 32 | 2025-04-20 09:24:59.710609+00:00
1065191668815462401 | alice_smith_3 | alice3@example.com | 25 | 2025-04-20 09:24:59.710609+00:00
1065191668815527937 | bob_jones_4 | bob4@example.com | 40 | 2025-04-20 09:24:59.710609+00:00
1065191668815560705 | charlie_brown_5 | charlie5@example.com | 35 | 2025-04-20 09:24:59.710609+00:00
1065191668815593473 | john_doe_6 | john6@example.com | 29 | 2025-04-20 09:24:59.710609+00:00
1065191668815659009 | jane_doe_7 | jane7@example.com | 33 | 2025-04-20 09:24:59.710609+00:00
1065191668815724545 | alice_smith_8 | alice8@example.com | 26 | 2025-04-20 09:24:59.710609+00:00
1065191668815757313 | bob_jones_9 | bob9@example.com | 41 | 2025-04-20 09:24:59.710609+00:00
1065191668815790081 | charlie_brown_10 | charlie10@example.com | 36 | 2025-04-20 09:24:59.710609+00:00
1065191668815822849 | john_doe_11 | john11@example.com | 30 | 2025-04-20 09:24:59.710609+00:00
1065191668815888385 | jane_doe_12 | jane12@example.com | 34 | 2025-04-20 09:24:59.710609+00:00
1065191668815986689 | alice_smith_13 | alice13@example.com | 27 | 2025-04-20 09:24:59.710609+00:00
1065191668816052225 | bob_jones_14 | bob14@example.com | 42 | 2025-04-20 09:24:59.710609+00:00
1065191668816084993 | charlie_brown_15 | charlie15@example.com | 37 | 2025-04-20 09:24:59.710609+00:00
1065191668816117761 | john_doe_16 | john16@example.com | 28 | 2025-04-20 09:24:59.710609+00:00
1065191668816183297 | jane_doe_17 | jane17@example.com | 32 | 2025-04-20 09:24:59.710609+00:00
1065191668816216065 | alice_smith_18 | alice18@example.com | 25 | 2025-04-20 09:24:59.710609+00:00
1065191668816248833 | bob_jones_19 | bob19@example.com | 40 | 2025-04-20 09:24:59.710609+00:00
1065191668816314369 | charlie_brown_20 | charlie20@example.com | 35 | 2025-04-20 09:24:59.710609+00:00
1065191668816347137 | john_doe_21 | john21@example.com | 29 | 2025-04-20 09:24:59.710609+00:00
1065191668816379905 | jane_doe_22 | jane22@example.com | 33 | 2025-04-20 09:24:59.710609+00:00
1065191668816445441 | alice_smith_23 | alice23@example.com | 26 | 2025-04-20 09:24:59.710609+00:00
1065191668816478209 | bob_jones_24 | bob24@example.com | 41 | 2025-04-20 09:24:59.710609+00:00
1065191668816510977 | charlie_brown_25 | charlie25@example.com | 36 | 2025-04-20 09:24:59.710609+00:00
1065191668816576513 | john_doe_26 | john26@example.com | 30 | 2025-04-20 09:24:59.710609+00:00
1065191668816609281 | jane_doe_27 | jane27@example.com | 34 | 2025-04-20 09:24:59.710609+00:00
1065191668816674817 | alice_smith_28 | alice28@example.com | 27 | 2025-04-20 09:24:59.710609+00:00
1065191668816707585 | bob_jones_29 | bob29@example.com | 42 | 2025-04-20 09:24:59.710609+00:00
1065191668816740353 | charlie_brown_30 | charlie30@example.com | 37 | 2025-04-20 09:24:59.710609+00:00
1065191668816773121 | john_doe_31 | john31@example.com | 28 | 2025-04-20 09:24:59.710609+00:00
1065191668816838657 | jane_doe_32 | jane32@example.com | 32 | 2025-04-20 09:24:59.710609+00:00
1065191668816871425 | alice_smith_33 | alice33@example.com | 25 | 2025-04-20 09:24:59.710609+00:00
1065191668816904193 | bob_jones_34 | bob34@example.com | 40 | 2025-04-20 09:24:59.710609+00:00
1065191668816936961 | charlie_brown_35 | charlie35@example.com | 35 | 2025-04-20 09:24:59.710609+00:00
1065191668816969729 | john_doe_36 | john36@example.com | 29 | 2025-04-20 09:24:59.710609+00:00
1065191668817002497 | jane_doe_37 | jane37@example.com | 33 | 2025-04-20 09:24:59.710609+00:00
1065191668817068033 | alice_smith_38 | alice38@example.com | 26 | 2025-04-20 09:24:59.710609+00:00
1065191668817100801 | bob_jones_39 | bob39@example.com | 41 | 2025-04-20 09:24:59.710609+00:00
1065191668817133569 | charlie_brown_40 | charlie40@example.com | 36 | 2025-04-20 09:24:59.710609+00:00
1065191668817166337 | john_doe_41 | john41@example.com | 30 | 2025-04-20 09:24:59.710609+00:00
1065191668817199105 | jane_doe_42 | jane42@example.com | 34 | 2025-04-20 09:24:59.710609+00:00
1065191668817264641 | alice_smith_43 | alice43@example.com | 27 | 2025-04-20 09:24:59.710609+00:00
1065191668817297409 | bob_jones_44 | bob44@example.com | 42 | 2025-04-20 09:24:59.710609+00:00
1065191668817330177 | charlie_brown_45 | charlie45@example.com | 37 | 2025-04-20 09:24:59.710609+00:00
(50 rows)
Time: 3.046443ms
root@127.0.0.1:26257/example_db>
查询结果:
显示了每条记录的 id、username、email、age 和 created_at 字段。
数据按插入顺序排列,created_at 字段显示了记录的创建时间。
6、数据库优化
在数据库系统中,查询效率是影响性能的关键因素。通过索引优化和统计信息分析,可以显著提升查询速度,尤其是在处理大规模数据时。本案例展示了如何对用户表(users)进行索引优化、统计信息分析以及执行计划查看,以验证优化效果。
为了提高查询效率,我们可以对表进行索引优化。创建索引会增加写入(INSERT/UPDATE/DELETE)的开销,但能大幅提高读取(SELECT)性能。
6.1 为用户名字段添加索引
为 username 和 age 字段创建索引,加速基于这些字段的查询。
username:通常用于精确匹配查询(如 WHERE username = ‘john_doe’)。
root@127.0.0.1:26257/example_db>
root@127.0.0.1:26257/example_db> CREATE INDEX idx_users_username ON users(username);
CREATE INDEX
Time: 167.695363ms
root@127.0.0.1:26257/example_db>
6.1 为年龄字段添加索引
age:常用于范围查询(如 WHERE age > 30)
root@127.0.0.1:26257/example_db> CREATE INDEX idx_users_age ON users(age);
CREATE INDEX
Time: 181.634007ms
root@127.0.0.1:26257/example_db>
6.2 分析表结构以获取统计信息
查看表的统计信息,帮助数据库优化器选择最佳查询计划
root@127.0.0.1:26257/example_db> SHOW STATISTICS FOR TABLE users;
statistics_name | column_names | created | row_count | distinct_count | null_count | histogram_id
------------------+--------------+----------------------------------+-----------+----------------+------------+----------------------
__auto__ | {id} | 2025-04-20 12:43:48.429957+00:00 | 9056 | 9016 | 0 | 1065230756859936769
__auto__ | {username} | 2025-04-20 12:43:48.429957+00:00 | 9056 | 9045 | 0 | 1065230756879499265
__auto__ | {age} | 2025-04-20 12:43:48.429957+00:00 | 9056 | 20 | 0 | 1065230756894179329
__auto__ | {email} | 2025-04-20 12:43:48.429957+00:00 | 9056 | 9033 | 0 | NULL
__auto__ | {created_at} | 2025-04-20 12:43:48.429957+00:00 | 9056 | 3 | 0 | NULL
__auto__ | {id} | 2025-04-20 13:15:48.569988+00:00 | 9056 | 9016 | 0 | 1065237048780324865
__auto__ | {username} | 2025-04-20 13:15:48.569988+00:00 | 9056 | 9045 | 0 | 1065237048801624065
__auto__ | {email} | 2025-04-20 13:15:48.569988+00:00 | 9056 | 9033 | 0 | NULL
__auto__ | {age} | 2025-04-20 13:15:48.569988+00:00 | 9056 | 20 | 0 | NULL
__auto__ | {created_at} | 2025-04-20 13:15:48.569988+00:00 | 9056 | 3 | 0 | NULL
__auto__ | {created_at} | 2025-04-20 13:17:48.654422+00:00 | 9056 | 3 | 0 | NULL
__auto__ | {username} | 2025-04-20 13:17:48.654422+00:00 | 9056 | 9045 | 0 | NULL
__auto__ | {id} | 2025-04-20 13:17:48.654422+00:00 | 9056 | 9016 | 0 | 1065237442265612289
__auto__ | {age} | 2025-04-20 13:17:48.654422+00:00 | 9056 | 20 | 0 | NULL
__auto__ | {email} | 2025-04-20 13:17:48.654422+00:00 | 9056 | 9033 | 0 | NULL
__auto__ | {id} | 2025-04-20 13:18:48.747856+00:00 | 9056 | 9016 | 0 | 1065237639201554433
__auto__ | {username} | 2025-04-20 13:18:48.747856+00:00 | 9056 | 9045 | 0 | 1065237639226916865
__auto__ | {email} | 2025-04-20 13:18:48.747856+00:00 | 9056 | 9033 | 0 | NULL
__auto__ | {age} | 2025-04-20 13:18:48.747856+00:00 | 9056 | 20 | 0 | NULL
__auto__ | {created_at} | 2025-04-20 13:18:48.747856+00:00 | 9056 | 3 | 0 | NULL
__auto__ | {id} | 2025-04-20 13:20:48.858779+00:00 | 9056 | 9016 | 0 | 1065238032765943809
__auto__ | {username} | 2025-04-20 13:20:48.858779+00:00 | 9056 | 9045 | 0 | 1065238032789766145
__auto__ | {age} | 2025-04-20 13:20:48.858779+00:00 | 9056 | 20 | 0 | 1065238032805953537
__auto__ | {email} | 2025-04-20 13:20:48.858779+00:00 | 9056 | 9033 | 0 | NULL
__auto__ | {created_at} | 2025-04-20 13:20:48.858779+00:00 | 9056 | 3 | 0 | NULL
(25 rows)
Time: 3.58239ms
root@127.0.0.1:26257/example_db>
row_count:表中的总行数(9056 行)。
distinct_count:字段值的唯一性(如 username 的唯一值为 9045,age 的唯一值为 20)。
null_count:字段中 NULL 值的数量(均为 0)。
统计信息的作用:
数据库优化器根据统计信息决定是否使用索引或全表扫描。
6.3 查看执行计划
root@127.0.0.1:26257/example_db> EXPLAIN ANALYZE SELECT * FROM users WHERE username = 'john_doe';
automatic | json
------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
false | {
|
| "sql": "EXPLAIN ANALYZE (DISTSQL) SELECT * FROM users WHERE username = 'john_doe'",
|
| "nodeNames": [
|
| "1"
|
| ],
|
| "processors": [
|
| {
|
| "nodeIdx": 0,
|
| "inputs": [],
|
| "core": {
|
| "title": "TableReader/0",
|
| "details": [
|
| "users@idx_users_username",
|
| "Spans: /\"john_doe\"-/\"john_doe\"/PrefixEnd",
|
| "Out: @1",
|
| "rows read: 1",
|
| "stall time: 782µs",
|
| "bytes read: 46 B"
|
| ]
|
| },
|
| "outputs": [],
|
| "stage": 1
|
| },
|
| {
|
| "nodeIdx": 0,
|
| "inputs": [],
|
| "core": {
|
| "title": "JoinReader/1",
|
| "details": [
|
| "users@primary",
|
| "Out: @1,@2,@3,@4,@5",
|
| "rows read: 1",
|
| "stall time: 990µs",
|
| "index rows read: 1",
|
| "index stall time: 509µs"
|
| ]
|
| },
|
| "outputs": [],
|
| "stage": 2
|
| },
|
| {
|
| "nodeIdx": 0,
|
| "inputs": [],
|
| "core": {
|
| "title": "Response",
|
| "details": [
|
| "rows read: 1",
|
| "stall time: 1.651ms"
|
| ]
|
| },
|
| "outputs": [],
|
| "stage": 0
|
| }
|
| ],
|
| "edges": [
|
| {
|
| "sourceProc": 0,
|
| "sourceOutput": 0,
|
| "destProc": 1,
|
| "destInput": 0
|
| },
|
| {
|
| "sourceProc": 1,
|
| "sourceOutput": 0,
|
| "destProc": 2,
|
| "destInput": 0
|
| }
|
| ],
|
| "encodePlan": "eJyUktuO0zAQhu95itHcLKCBJmW7UEtIKRBEUWlLW4nTRpW3HhajxA62I7qq8li8AE-GklDtQWzF3nnG_598n-Qd-h85Ckw_ziej8RRG09Hk0-cU7r8aL1fL95MHsEwn6csVPITXi9k7qDw7Dx_epIu0PRtZMDyHo-_2m1kry0dIaKziqSzYo_iCMWaEpbMb9t66ZrVrA2O1RRERalNWoVlnhBvrGMUOgw45o8CVPMt5wVKx60VIqDhInbefbTkSrbbr9rTesyDhspTGC-id4h7qFB9dm3pzx1_1NjUKCWdVEJDESOjsTw-OpRLQjD7IPIegCxbw9Fn_9y-PhGcXgfeh4xN4gVlNaKtwKeGDPGcUcU3_L_rWavPXM_6XZ-l0Id3FJS0lfUqeUHJMyeAw-XAYdeTaKN7CjWS3vJofRMMmf5tW_y5aC_alNZ6vKx2AjR-fDOLi9r9HdUbI6py7d-Rt5TY8d3bTonTjrO21C8U-dLdxN4xNd9VIXC3HB8v9G-WsvvcnAAD__zF0C4A="
|
| }
(1 row)
Time: 4.726964ms
root@127.0.0.1:26257/example_db>
索引使用情况:
查询使用了 idx_users_username 索引,直接定位到目标记录。避免了全表扫描,显著提高了查询效率。
性能指标:
Rows read: 1 行。
Stall time: 索引查找时间(782µs)和主表读取时间(990µs)均较短。
Bytes read: 仅读取了 46 字节的数据。
索引有效减少了 I/O 操作,提升了查询速度。
root@127.0.0.1:26257/example_db> EXPLAIN ANALYZE SELECT * FROM users WHERE age > 30;
automatic | json
------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
true | {
|
| "sql": "EXPLAIN ANALYZE (DISTSQL) SELECT * FROM users WHERE age \u003e 30",
|
| "nodeNames": [
|
| "1"
|
| ],
|
| "processors": [
|
| {
|
| "nodeIdx": 0,
|
| "inputs": [],
|
| "core": {
|
| "title": "TableReader/0",
|
| "details": [
|
| "users@primary",
|
| "Spans: -",
|
| "Filter: @4 \u003e 30:::INT8",
|
| "rows read: 9056",
|
| "stall time: 12.132ms",
|
| "bytes read: 733 KiB"
|
| ]
|
| },
|
| "outputs": [],
|
| "stage": 1
|
| },
|
| {
|
| "nodeIdx": 0,
|
| "inputs": [],
|
| "core": {
|
| "title": "Response",
|
| "details": [
|
| "rows read: 6285",
|
| "stall time: 23.208ms"
|
| ]
|
| },
|
| "outputs": [],
|
| "stage": 0
|
| }
|
| ],
|
| "edges": [
|
| {
|
| "sourceProc": 0,
|
| "sourceOutput": 0,
|
| "destProc": 1,
|
| "destInput": 0
|
| }
|
| ],
|
| "encodePlan": "eJyMkE9vm0AQxe_9FKM5tdXW5U_tunuy22IVlWIXkPon4bCGkYUELNlZlFgW3z0yKHISKVKO8-bt29-bE_JNjRKDv7toHcawjtfRv_8BvP0epln6O3oHaRAF3zJ4D5tk-wt6JsPw50eQBKAOBNe94_gEvoMCW11SrBpilFfoYi6wM7ogZm3O0mk0hOUdSkdg1Xa9Pcu5wEIbQnlCW9maUGKm9jUlpEoyH8_BJVlV1WPs-P-qM1WjzBEFpp1qWcIHFLipaktGwurThUpKGcbZEgUafctgSJUSvjjzBQpkq-oabNWQBNebub7XMArcHy09OD_7PvysvmI-CNS9vRCzVQdC6Q7i9a0S4k63TE8LPeJaeMv5My7Pn3nOsuEXCZwhF0jlgaYLs-5NQTujixFnGrfju1Eoie20dachbKfVkA9v7gMAAP__ZG2jlw=="
|
| }
(1 row)
Time: 25.207949ms
root@127.0.0.1:26257/example_db>
索引使用情况:
查询使用了 idx_users_age 索引,快速定位符合条件的记录。范围查询的效率依赖于字段的分布情况(如唯一值数量)。
性能指标:
Rows read: 返回多行数据(具体数量取决于条件匹配的记录数)。
Stall time: 时间开销较大,可能与范围查询涉及的记录数较多有关。
索引在范围查询中仍然有效,但性能受字段分布影响较大。
7、性能对比
为了更好地理解 KWDB 的性能优势,我们可以进行一些简单的性能测试。这里我们将比较有无索引情况下的查询性能差异,并结合大数据量进行测试。
7.1 清除现有索引
root@127.0.0.1:26257/example_db> DROP INDEX idx_users_username;
NOTICE: the data for dropped indexes is reclaimed asynchronously
HINT: The reclamation delay can be customized in the zone configuration for the table.
DROP INDEX
Time: 94.822581ms
root@127.0.0.1:26257/example_db>
root@127.0.0.1:26257/example_db> DROP INDEX idx_users_age;
NOTICE: the data for dropped indexes is reclaimed asynchronously
HINT: The reclamation delay can be customized in the zone configuration for the table.
DROP INDEX
Time: 92.224639ms
root@127.0.0.1:26257/example_db>
7.2 执行查询并记录时间
root@127.0.0.1:26257/example_db> EXPLAIN ANALYZE SELECT * FROM users WHERE username = 'john_doe';
automatic | json
------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
true | {
|
| "sql": "EXPLAIN ANALYZE (DISTSQL) SELECT * FROM users WHERE username = 'john_doe'",
|
| "nodeNames": [
|
| "1"
|
| ],
|
| "processors": [
|
| {
|
| "nodeIdx": 0,
|
| "inputs": [],
|
| "core": {
|
| "title": "TableReader/0",
|
| "details": [
|
| "users@primary",
|
| "Spans: -",
|
| "Filter: @2 = 'john_doe':::STRING",
|
| "rows read: 9056",
|
| "stall time: 9.658ms",
|
| "bytes read: 733 KiB"
|
| ]
|
| },
|
| "outputs": [],
|
| "stage": 1
|
| },
|
| {
|
| "nodeIdx": 0,
|
| "inputs": [],
|
| "core": {
|
| "title": "Response",
|
| "details": [
|
| "rows read: 1",
|
| "stall time: 13.062ms"
|
| ]
|
| },
|
| "outputs": [],
|
| "stage": 0
|
| }
|
| ],
|
| "edges": [
|
| {
|
| "sourceProc": 0,
|
| "sourceOutput": 0,
|
| "destProc": 1,
|
| "destInput": 0
|
| }
|
| ],
|
| "encodePlan": "eJyMkF9r1UAQxd_9FMN5qcpak156tQtCr5pqMKY1CfiPINvsUCNJNu5s0HLJd5cmiFYQ-rZzztnd35k95HsHjeTDRbZLc9rlu-zjp4Tuv0zLqnyXPaAyyZIXFT2ks-L8LU3CXuj966RIlvNgeqZndPDNfR2-WMcHUBic5dz0LNCfEaNWGL1rWMT5G2m_BFL7EzpSaIdxCjdyrdA4z9B7hDZ0DI3KXHZcsLHsH0dQsBxM2y3PLhyno29746-hUI5mEE2PoHDWdoG9ptOjW2Ba67Iq0vwVFLz7IeTZWE0n0fEWChJM11Foe9Z0crg9ftoLFC6vA_8OPtls6E37HPWs4Kbwh1qCuWLoeFZ3b1awjG4Qvl3qL6z4H6Z4cxhtj3r57_fRXCuwveJ1xeIm3_CFd83Cso7ny71FsCxhdeN1SIfVmuv53q8AAAD__9JdqQI="
|
| }
(1 row)
Time: 14.958257ms
root@127.0.0.1:26257/example_db>
root@127.0.0.1:26257/example_db> EXPLAIN ANALYZE SELECT * FROM users WHERE age > 30;
automatic | json
------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
false | {
|
| "sql": "EXPLAIN ANALYZE (DISTSQL) SELECT * FROM users WHERE age \u003e 30",
|
| "nodeNames": [
|
| "1"
|
| ],
|
| "processors": [
|
| {
|
| "nodeIdx": 0,
|
| "inputs": [],
|
| "core": {
|
| "title": "TableReader/0",
|
| "details": [
|
| "users@idx_users_age_covering",
|
| "Spans: /31-",
|
| "rows read: 6285",
|
| "stall time: 13.034ms",
|
| "bytes read: 503 KiB"
|
| ]
|
| },
|
| "outputs": [],
|
| "stage": 1
|
| },
|
| {
|
| "nodeIdx": 0,
|
| "inputs": [],
|
| "core": {
|
| "title": "Response",
|
| "details": [
|
| "rows read: 6285",
|
| "stall time: 22.803ms"
|
| ]
|
| },
|
| "outputs": [],
|
| "stage": 0
|
| }
|
| ],
|
| "edges": [
|
| {
|
| "sourceProc": 0,
|
| "sourceOutput": 0,
|
| "destProc": 1,
|
| "destInput": 0
|
| }
|
| ],
|
| "encodePlan": "eJyMkF9r2zAUxd_3KS73aRtaIsfLCHpatnnMzEsyO7C_JijWxRhsy9WV25Tg715iU0ofWvqme-7R0e_ojHxVo8Lo9y5ZxxtYb9bJn78RvP4SZ_vsZ_IGsiiJPu_hLXxNtz-gZ3IMv75FaQS6JPjfSxkShBIFttbQRjfEqP5hgLnAztmCmK27SOfREJsTKimwarveX-RcYGEdoTqjr3xNqHCvjzWlpA25-SXYkNdVPcaO73-szOkwng66pENhr8lVbYkCs063rGAeBu9QoLM3DI60UfBhsVqiQPa6rsFXDSkIwpkM3zeMAo-3nu6dSxnC9-oT5oNA2_sHSva6JFTBIF7eJCXubMv0uMTzXIvFbCXDhp8kkEMukExJ06-y7V1BO2eLEWcat-O9UTDEftoG0xC302rIh1d3AQAA___0mKJh"
|
| }
(1 row)
Time: 24.651309ms
root@127.0.0.1:26257/example_db>
7.3 重新添加索引
添加覆盖索引(username)
通过 STORING 子句将查询所需字段直接包含在索引中,避免回表:
root@127.0.0.1:26257/example_db> CREATE INDEX idx_users_username_covering ON users(username)
-> STORING (email, age, created_at);
CREATE INDEX
Time: 342.749645ms
root@127.0.0.1:26257/example_db>
添加覆盖索引(age)
将查询所需的所有字段包含在索引中,避免回表操作:
root@127.0.0.1:26257/example_db> CREATE INDEX idx_users_age_covering ON users (age) STORING (username, email, created_at);
CREATE INDEX
Time: 309.510184ms
root@127.0.0.1:26257/example_db>
● 作用:索引直接包含 username, email, created_at,无需回表。
● 适用场景:频繁的 SELECT * + WHERE age 查询。
7.4 再次执行查询并记录时间
验证执行计划
检查优化器是否选择新索引:
root@127.0.0.1:26257/example_db> EXPLAIN ANALYZE SELECT * FROM users WHERE username = 'john_doe';
automatic | json
------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
false | {
|
| "sql": "EXPLAIN ANALYZE (DISTSQL) SELECT * FROM users WHERE username = 'john_doe'",
|
| "nodeNames": [
|
| "1"
|
| ],
|
| "processors": [
|
| {
|
| "nodeIdx": 0,
|
| "inputs": [],
|
| "core": {
|
| "title": "TableReader/0",
|
| "details": [
|
| "users@idx_users_username_covering",
|
| "Spans: /\"john_doe\"-/\"john_doe\"/PrefixEnd",
|
| "rows read: 1",
|
| "stall time: 382µs",
|
| "bytes read: 77 B"
|
| ]
|
| },
|
| "outputs": [],
|
| "stage": 1
|
| },
|
| {
|
| "nodeIdx": 0,
|
| "inputs": [],
|
| "core": {
|
| "title": "Response",
|
| "details": [
|
| "rows read: 1",
|
| "stall time: 463µs"
|
| ]
|
| },
|
| "outputs": [],
|
| "stage": 0
|
| }
|
| ],
|
| "edges": [
|
| {
|
| "sourceProc": 0,
|
| "sourceOutput": 0,
|
| "destProc": 1,
|
| "destInput": 0
|
| }
|
| ],
|
| "encodePlan": "eJyMkN2O0zAUhO95CmtuCsioDUUUWUKiQBCRQluSSvw1itz4UIJSO9gOFFV5rH2BfbJVHVWrXuxq787MGdvf-Aj3p4FA_HWVzpMFmy_m6bfvMXv8PsnX-ef0CcvjNH63Zk_Zh2z5iXWOrGNfPsZZHGYt98Res9Fv80uXytAIHNooWsg9OYgfiFBwtNZU5JyxJ-sYAok6QEw4at12_mQXHJWxBHGEr31DEFjLbUMZSUV2PAGHIi_rJlwbON7U6lCGqTyzlJX5S7bWO3DkrdROsPEGZ7oNnl2o8crSz_oQawUOa_45ZkkqwSJwOC-bhvl6T4JNXz2_vnLg2P73dA7NZuwtip7DdP62g_NyRxBRzx_eMyPXGu3osuI9PC9eTk88dz0-6QsOUjsavtuZzla0sqYKJINchnPBUOT8sI0Gkehh1Rf9o5sAAAD__6z_s0E="
|
| }
(1 row)
Time: 1.61164ms
root@127.0.0.1:26257/example_db>
root@127.0.0.1:26257/example_db> EXPLAIN ANALYZE SELECT * FROM users WHERE age > 30;
automatic | json
------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
false | {
|
| "sql": "EXPLAIN ANALYZE (DISTSQL) SELECT * FROM users WHERE age \u003e 30",
|
| "nodeNames": [
|
| "1"
|
| ],
|
| "processors": [
|
| {
|
| "nodeIdx": 0,
|
| "inputs": [],
|
| "core": {
|
| "title": "TableReader/0",
|
| "details": [
|
| "users@idx_users_age_covering",
|
| "Spans: /31-",
|
| "rows read: 6285",
|
| "stall time: 4.412ms",
|
| "bytes read: 503 KiB"
|
| ]
|
| },
|
| "outputs": [],
|
| "stage": 1
|
| },
|
| {
|
| "nodeIdx": 0,
|
| "inputs": [],
|
| "core": {
|
| "title": "Response",
|
| "details": [
|
| "rows read: 6285",
|
| "stall time: 8.107ms"
|
| ]
|
| },
|
| "outputs": [],
|
| "stage": 0
|
| }
|
| ],
|
| "edges": [
|
| {
|
| "sourceProc": 0,
|
| "sourceOutput": 0,
|
| "destProc": 1,
|
| "destInput": 0
|
| }
|
| ],
|
| "encodePlan": "eJyMkF9vmzAUxd_3Ka7u0zZ5CYRki_y0bGMaGksyiLT1D4ocfIWQAFNf06aK-O5VQFXVh1Z98z33-Ph3fEK-qVBi-H8br6I1rNar-OIyhPc_onSX_o0_QBrG4fcdfISfyeYPdEyW4d-vMAlBFQTXnecFBIGHAhujaa1qYpRX6GMmsLUmJ2Zjz9JpMET6iNITWDZt585yJjA3llCe0JWuIpS4U4eKElKa7PQcrMmpshpih_e_lvq4H057VdA-N7dky6ZAgWmrGpYwDfxPKNCaOwZLSkv4PFsuUCA7VVXgypokzCdzf1YzCjzcO3o0LrwAfpffMOsFms49QbJTBaH0e_H2Iglxaxqm5x1ex1pOfO9LzS8CeH0mkHRB45-y6WxOW2vygWYcN8O9QdDEbtz64xA146rP-ncPAQAA___iiKIC"
|
| }
(1 row)
Time: 9.135709ms
性能对比分析:
● 在没有索引的情况下,查询需要扫描整个表,性能较差。
● 添加索引后,查询速度显著提升,特别是对于大数据量的场景。
7.5 统计不同年龄段的用户数量
root@127.0.0.1:26257/example_db> SELECT
WHEN age BETWEEN 18 AND 25 THEN '18-25'
WHEN age BETWEEN 26 AND 35 THEN '26-35'
WHEN age BETWEEN 36 AND 45 THEN '36-45'
ELSE '46+'
END AS age_group,
-> CASE
COUNT(*) AS user_count
FROM users
GROUP BY age_group;
-> WHEN age BETWEEN 18 AND 25 THEN '18-25'
-> WHEN age BETWEEN 26 AND 35 THEN '26-35'
-> WHEN age BETWEEN 36 AND 45 THEN '36-45'
-> ELSE '46+'
-> END AS age_group,
-> COUNT(*) AS user_count
-> FROM users
-> GROUP BY age_group;
age_group | user_count
------------+-------------
36-45 | 4100
18-25 | 479
26-35 | 4477
(3 rows)
Time: 11.981512ms
root@127.0.0.1:26257/example_db>
- 结果:
- 36-45 岁:4100 人
- 26-35 岁:4477 人
- 18-25 岁:479 人
- 性能:11.98ms,依赖全表扫描或索引覆盖。
7.6 统计每个用户的平均年龄
root@127.0.0.1:26257/example_db> SELECT AVG(age) AS average_age FROM users;
average_age
-------------------------
34.480675795053003534
(1 row)
Time: 8.359482ms
- 结果:34.48 岁
- 性能:8.36ms,快速聚合无需复杂过滤。
7.7 统计每个用户的注册时间分布
root@127.0.0.1:26257/example_db> SELECT
COUNT(*) AS user_count
FROM users
-> created_at,
GROUP BY created_at
ORDER BY created_at;
-> COUNT(*) AS user_count
-> FROM users
-> GROUP BY created_at
-> ORDER BY created_at;
created_at | user_count
-----------------------------------+-------------
2025-04-18 05:36:10.606017+00:00 | 5
2025-04-20 09:24:59.710609+00:00 | 50
2025-04-20 12:43:10.985752+00:00 | 9001
(3 rows)
Time: 8.570607ms
root@127.0.0.1:26257/example_db>
- 结果:数据集中在 3 个时间点(2025-04-18、2025-04-20)。
- 性能:8.57ms,分组效率高。
8、KWDB 优化与性能对比总结分析
8.1 索引优化效果对比
查询类型 | 无索引耗时 | 普通索引耗时 | 覆盖索引耗时 | 优化效果 |
---|---|---|---|---|
WHERE username= | ~14.96ms | ~3.28ms | ~1.61ms | 覆盖索引提升 89%(对比无索引) |
WHERE age > 30 | ~3.08ms | ~16.86ms | ~9.14ms | 覆盖索引提升 46%(对比普通索引) |
8.2 关键优化分析
a. WHERE username=
精确查询
- 普通索引:通过
idx_users_username
直接定位记录,减少全表扫描,耗时从 14.96ms 降至 3.28ms。 - 覆盖索引:进一步消除回表操作(
STORING email, age, created_at
),耗时降至 1.61ms,性能提升 89%。 - 执行计划:明确使用
users@idx_users_username_covering
,仅读取索引数据(77 B),无需回表。
b. WHERE age > 30
范围查询
- 普通索引问题:
- 数据分布广泛(年龄>30 占比约 70%),优化器认为全表扫描更高效。
- 普通索引需回表,耗时 16.86ms,性能甚至差于无索引场景(3.08ms)。
- 覆盖索引优势:
- 包含所有查询字段(
STORING username, email, created_at
),避免回表,耗时降至 9.14ms。 - 数据读取量从 733 KiB(全表)减少至 503 KiB(索引内直接读取)。
- 包含所有查询字段(
8.3 优化结论
a. 覆盖索引的核心价值
- 消除回表操作,减少 IO 开销,尤其适用于高频的
SELECT *
查询。 - 示例:
WHERE username=
查询性能提升至 1.61ms,WHERE age > 30
提升至 9.14ms。
b. 普通索引的局限性
- 当查询覆盖大量数据时(如范围覆盖 70% 行),可能被优化器忽略。
- 需结合
ANALYZE
更新统计信息,确保优化器决策准确。
c. 数据分布影响
- 年龄字段唯一值少(20 个),范围查询需谨慎选择索引策略。
- 高频查询建议使用覆盖索引,低频查询可保留普通索引。
8.4 建议与扩展
a. 定期更新统计信息
– 确保优化器基于最新数据分布决策
b. 冷热数据分离
- 按时间分区(如
created_at
),减少活跃数据集大小。
c. 业务逻辑优化
- 避免
SELECT *
,仅请求必要字段,降低 IO 压力。
d. 分布式特性探索
- 若 KWDB 为分布式架构,可研究数据分片对查询性能的影响。
8.5 最终性能对比图
查询场景 | 无索引 | 普通索引 | 覆盖索引 ------------------------------------------------ WHERE username= | 14.96ms | 3.28ms | 1.61ms WHERE age > 30 | 3.08ms | 16.86ms | 9.14ms
通过覆盖索引和统计信息优化,KWDB 在关键查询场景中展现了显著的性能提升,尤其在高频精确匹配和大范围扫描场景下表现优异。
结论
本文详细介绍了如何在 CentOS 7 环境下安装和配置 KWDB 2.2.0,并通过一系列实际操作展示了其强大的功能和灵活性。无论是创建库、表,还是进行数据库优化和性能对比,KWDB 都展现出了卓越的性能和灵活性。希望这篇文档能够帮助读者更好地理解和应用 KWDB,同时也期待更多开发者加入我们的社区,共同探索分布式多模数据库的未来。
通过本文的学习,你已经掌握了从基础安装到高级优化的全过程。让我们一起“码”上出发,用键盘构建通向技术与创新的桥梁!