# 数据库设计文档 ## 概述 Material Texture 使用 PostgreSQL 数据库存储材质信息和绑定关系。 - **数据库**: PostgreSQL 15+ - **ORM**: GORM v1.31 - **特性**: 分区表、pg_trgm 模糊搜索 --- ## ER 图 ```mermaid erDiagram materials ||--o{ material_bindings : "has" materials { bigint id PK "自增主键" varchar name "材质名称" float diffuse_r "漫反射-红" float diffuse_g "漫反射-绿" float diffuse_b "漫反射-蓝" float alpha "透明度" float shininess "光泽度" float specular_r "高光-红" float specular_g "高光-绿" float specular_b "高光-蓝" float ambient_r "环境光-红" float ambient_g "环境光-绿" float ambient_b "环境光-蓝" float metallic "金属度(PBR)" float roughness "粗糙度(PBR)" float reflectance "反射率(PBR)" bigint version "乐观锁版本" timestamp created_at "创建时间" timestamp updated_at "更新时间" } material_bindings { bigint id PK "绑定ID" bigint material_id FK "材质ID" varchar group_id "叶子节点ID" timestamp created_at "创建时间" } ``` --- ## 表结构详情 ### 1. materials 表(材质表) 存储 3D 材质的属性信息,包括传统渲染属性和 PBR 属性。 | 字段 | 类型 | 约束 | 默认值 | 说明 | |------|------|------|--------|------| | id | BIGSERIAL | PRIMARY KEY | 自增 | 材质唯一标识 | | name | VARCHAR(255) | NOT NULL | - | 材质名称 | | diffuse_r | FLOAT | NOT NULL | 0 | 漫反射颜色-红 (0-255) | | diffuse_g | FLOAT | NOT NULL | 0 | 漫反射颜色-绿 (0-255) | | diffuse_b | FLOAT | NOT NULL | 0 | 漫反射颜色-蓝 (0-255) | | alpha | FLOAT | NOT NULL | 1 | 透明度 (0-255) | | shininess | FLOAT | NOT NULL | 0 | 光泽度/高光强度 | | specular_r | FLOAT | NOT NULL | 0 | 高光颜色-红 (0-255) | | specular_g | FLOAT | NOT NULL | 0 | 高光颜色-绿 (0-255) | | specular_b | FLOAT | NOT NULL | 0 | 高光颜色-蓝 (0-255) | | ambient_r | FLOAT | NOT NULL | 0 | 环境光颜色-红 (0-255) | | ambient_g | FLOAT | NOT NULL | 0 | 环境光颜色-绿 (0-255) | | ambient_b | FLOAT | NOT NULL | 0 | 环境光颜色-蓝 (0-255) | | metallic | FLOAT | NOT NULL | 0 | PBR 金属度 (0-1) | | roughness | FLOAT | NOT NULL | 0.5 | PBR 粗糙度 (0-1) | | reflectance | FLOAT | NOT NULL | 0.5 | PBR 反射率 (0-1) | | version | BIGINT | NOT NULL | 0 | 乐观锁版本号 | | created_at | TIMESTAMP | - | CURRENT_TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | - | CURRENT_TIMESTAMP | 更新时间 | #### 索引 | 索引名 | 类型 | 字段 | 用途 | |--------|------|------|------| | materials_pkey | PRIMARY KEY | id | 主键 | | idx_materials_name_trgm | GIN | name | 模糊搜索 (ILIKE '%keyword%') | | idx_materials_created_at | BTREE | created_at DESC | 按创建时间排序 | | idx_materials_updated_at | BTREE | updated_at DESC | 按更新时间排序 | --- ### 2. material_bindings 表(材质绑定表) 存储材质与叶子节点(group_id)的多对多绑定关系。 **重要**: 此表使用 HASH 分区,按 `material_id` 分为 16 个分区,支持亿级数据规模。 | 字段 | 类型 | 约束 | 默认值 | 说明 | |------|------|------|--------|------| | id | BIGSERIAL | 复合主键 | 自增 | 绑定记录ID | | material_id | BIGINT | NOT NULL, FK | - | 关联的材质ID | | group_id | VARCHAR(255) | NOT NULL | - | 叶子节点ID | | created_at | TIMESTAMP | - | CURRENT_TIMESTAMP | 创建时间 | #### 约束 - **主键**: `(material_id, id)` - 复合主键,支持分区 - **外键**: `material_id` → `materials(id)` ON DELETE CASCADE - **唯一约束**: `(material_id, group_id)` - 防止重复绑定 #### 分区策略 ``` material_bindings (父表) ├── material_bindings_p0 (material_id % 16 = 0) ├── material_bindings_p1 (material_id % 16 = 1) ├── material_bindings_p2 (material_id % 16 = 2) │ ... └── material_bindings_p15 (material_id % 16 = 15) ``` #### 每个分区的索引 | 索引名 | 类型 | 字段 | 用途 | |--------|------|------|------| | idx_bindings_pX_unique | UNIQUE | (material_id, group_id) | 防止重复绑定 | | idx_bindings_pX_group | BTREE | group_id | 按 group_id 查询 | --- ## 迁移文件 | 文件 | 版本 | 说明 | |------|------|------| | 001_init.sql | v1.0 | 初始化基础表结构 | | 002_partition_bindings.sql | v1.1 | 将 bindings 表转为 16 分区 | | 003_add_indexes.sql | v1.2 | 添加 pg_trgm 和时间索引 | | 004_add_version_column.sql | v1.3 | 添加乐观锁版本字段 | ### 执行迁移 ```bash # 进入数据库容器 docker compose exec db psql -U postgres -d material_db # 或直接执行 psql -h localhost -p 5433 -U postgres -d material_db -f migrations/001_init.sql ``` --- ## 性能优化说明 ### 1. 分区表 - **目的**: 支持亿级 binding 记录 - **策略**: HASH 分区,按 material_id 分 16 片 - **优势**: - 单分区最大约 600-1000 万行 - 查询时自动路由到相关分区 - 可在线添加新分区 ### 2. pg_trgm 扩展 ```sql CREATE EXTENSION IF NOT EXISTS pg_trgm; ``` - **目的**: 支持模糊搜索 `ILIKE '%keyword%'` - **原理**: 将字符串拆分为三元组 (trigram),建立 GIN 倒排索引 ### 3. 连接池配置 | 参数 | 值 | 说明 | |------|-----|------| | MaxIdleConns | 50 | 最大空闲连接数 | | MaxOpenConns | 200 | 最大连接数 | | ConnMaxLifetime | 5min | 连接最大生命周期 | | ConnMaxIdleTime | 2min | 空闲连接最大生命周期 | --- ## 数据示例 ### Material 示例 ```json { "id": 4, "name": "红色金属", "diffuse_r": 255, "diffuse_g": 0, "diffuse_b": 0, "alpha": 255, "shininess": 80, "specular_r": 255, "specular_g": 200, "specular_b": 200, "ambient_r": 50, "ambient_g": 0, "ambient_b": 0, "metallic": 0.9, "roughness": 0.2, "reflectance": 0.8, "version": 0, "created_at": "2025-12-09T09:53:33Z", "updated_at": "2025-12-09T09:53:33Z" } ``` ### Binding 示例 ```json { "id": 1, "material_id": 4, "group_id": "20251021105716587535", "created_at": "2025-12-09T10:00:00Z" } ``` --- ## 常用查询 ### 1. 按名称模糊搜索材质 ```sql SELECT * FROM materials WHERE name ILIKE '%金属%' ORDER BY id DESC LIMIT 20; ``` ### 2. 获取材质关联的所有 group_id ```sql SELECT group_id FROM material_bindings WHERE material_id = 4 ORDER BY created_at DESC LIMIT 20 OFFSET 0; ``` ### 3. 根据 group_id 批量查询材质 ```sql SELECT mb.group_id, m.* FROM material_bindings mb JOIN materials m ON mb.material_id = m.id WHERE mb.group_id IN ('group1', 'group2', 'group3'); ``` ### 4. 统计各分区数据量 ```sql SELECT tableoid::regclass AS partition, COUNT(*) FROM material_bindings GROUP BY tableoid; ```