面试题答案
一键面试表结构设计优化策略
- 字段类型选择:
- user_id:使用
INT UNSIGNED
类型,假设用户数量不会超过20亿左右,该类型可以节省空间且性能较好。对于game_id
同理,使用INT UNSIGNED
。 - record_id:若业务上记录ID自增且不会有并发插入冲突问题,使用
BIGINT UNSIGNED AUTO_INCREMENT
,因为游戏记录数量可能较大,BIGINT
能支持更大范围。 - username:根据实际情况,如果用户名长度基本固定且较短,如不超过20个字符,可使用
CHAR(20)
;若长度不固定,使用VARCHAR(255)
,VARCHAR
类型根据实际存储数据长度分配空间,节省空间但查询时可能稍慢。 - game_name:同样若长度基本固定,使用
CHAR
;否则用VARCHAR
。 - score:如果分数范围较小,如0 - 10000,可使用
SMALLINT UNSIGNED
,占用空间小;若分数范围大,使用INT UNSIGNED
。 - play_time:使用
DATETIME
类型记录游戏时间,精确到秒;如果只需要记录日期,使用DATE
类型,可节省空间。
- user_id:使用
- 索引设计:
- users表:
- 为主键
user_id
添加主键索引,这是默认且必须的,能加速对单个用户基本信息的查询。 - 若经常根据
username
查询用户,对username
添加普通索引,如CREATE INDEX idx_username ON users (username);
,提高按用户名查找用户的速度。
- 为主键
- games表:
- 对
game_id
添加主键索引。 - 若常根据
game_name
查找游戏,对game_name
添加普通索引,如CREATE INDEX idx_game_name ON games (game_name);
。
- 对
- game_records表:
- 对
record_id
添加主键索引。 - 对
user_id
添加索引,因为经常需要根据用户ID查询其游戏记录,如CREATE INDEX idx_user_id ON game_records (user_id);
。 - 对
game_id
添加索引,若需要统计某个游戏的所有记录等场景会用到,CREATE INDEX idx_game_id ON game_records (game_id);
。 - 组合索引,若经常根据
user_id
和play_time
查询用户在某个时间段内的游戏记录,可创建组合索引CREATE INDEX idx_user_time ON game_records (user_id, play_time);
,注意索引字段顺序,将选择性高的字段放在前面。
- 对
- users表:
- 表的分区:
- 按时间分区:由于
play_time
字段记录游戏时间,可按时间对game_records
表进行分区。例如按月份分区,每个月的数据存储在一个分区中。
这样在查询某个时间段的游戏记录时,数据库可以快速定位到相关分区,减少扫描的数据量,提高查询性能。对于历史数据的清理也更方便,直接删除对应的分区即可。CREATE TABLE game_records ( record_id BIGINT UNSIGNED AUTO_INCREMENT, user_id INT UNSIGNED, game_id INT UNSIGNED, score INT UNSIGNED, play_time DATETIME, PRIMARY KEY (record_id, play_time) ) PARTITION BY RANGE (YEAR(play_time) * 100 + MONTH(play_time)) ( PARTITION p0 VALUES LESS THAN (202301), PARTITION p1 VALUES LESS THAN (202302), PARTITION p2 VALUES LESS THAN (202303), -- 以此类推,根据实际需要划分足够多的分区 PARTITION pmax VALUES LESS THAN (MAXVALUE) );
- 按用户ID分区:如果用户量非常大,也可以考虑按
user_id
进行分区。例如按user_id
的取值范围进行分区,假设user_id
从1开始连续递增,可按每10000个用户为一个分区。
这种分区方式在查询单个用户或特定用户组的游戏记录时,能快速定位到对应的分区,提高查询效率。同时,在高并发写入时,不同用户的记录可以分散到不同分区,一定程度上减轻单个分区的写入压力。CREATE TABLE game_records ( record_id BIGINT UNSIGNED AUTO_INCREMENT, user_id INT UNSIGNED, game_id INT UNSIGNED, score INT UNSIGNED, play_time DATETIME, PRIMARY KEY (record_id, user_id) ) PARTITION BY RANGE (user_id) ( PARTITION p0 VALUES LESS THAN (10000), PARTITION p1 VALUES LESS THAN (20000), PARTITION p2 VALUES LESS THAN (30000), -- 以此类推,根据实际用户量划分足够多的分区 PARTITION pmax VALUES LESS THAN (MAXVALUE) );
- 按时间分区:由于