极简抖音项目——数据库添加索引增加查找速率 | 青训营笔记

262 阅读3分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第5篇笔记

本文对gorm使用索引的方法,和对应查找速度的变化进行了讨论,并就项目中添加索引的目的进行描述。

根据MYSQL的说法,索引可以提高MySQL的检索速度,但是会相应降低更新表的速度,例如对表进行UPDATE,INSERT,和DELETE,因为更新表时,mysql除了保存数据,还要保存索引文件。[1]

使用gorm添加索引

gorm中使用index来添加索引,值得注意的是,index需要放在gorm的第一位,不然不会被识别[2] 例如: 正确: VideoId int64 `gorm:"index:, not null"` //评论所在的视频ID

错误: VideoId int64 `gorm:"not null, index:"` //评论所在的视频ID

在mysql中使用SHOW INDEX FROM table_name\G可以很清楚的看出,第一种会正确建立索引

mysql> SHOW INDEX FROM students;
+----------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table    | Non_unique | Key_name              | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| students |          0 | PRIMARY               |            1 | id          | A         |       22795 |     NULL | NULL   |      | BTREE      |         |               |
| students |          1 | idx_students_video_id |            1 | video_id    | A         |           9 |     NULL | NULL   | YES  | BTREE      |         |               |
+----------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

而第二种并不会对video_id建立索引

mysql> SHOW INDEX FROM students;
+----------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table    | Non_unique | Key_name              | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| students |          0 | PRIMARY               |            1 | id          | A         |       22795 |     NULL | NULL   |      | BTREE      |         |               |
+----------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

查找速度测试

为了测试建立索引和不建立索引对查找速度的影响,本文创建了一个students表

type Student struct {
	Id        int64  `gorm:"primaryKey;autoIncrement"` //评论唯一标识符
	UserId    int64  `gorm:"not null"`                 //发起评论的用户ID
	VideoId   int64  `gorm:"index:, not null"`         //评论所在的视频ID
	ToUserId  int64  `gorm:"not null"`                 //视频发布者的ID
	Content   string `gorm:"not null"`                 //评论内容
	CreatedAt int64  `gorm:"autoCreateTime:milli"`     //评论创建时间
}

其中VideoIdToUserId保存相同的int64,参数由随机数生成,参数范围[1, 10]

这里对总共23332条数据进行查找,使用benchmark来测试查找速度

BenchmarkVideoSearch-4             21315           5608330 ns/op
BenchmarkTouseridSearch-4           8389          14197766 ns/op

其中BenchmarkVideoSearch代表对建立了索引的列进行查找,BenchmarkTouseridSearch对没有建立索引的列进行查找,结果发现速度差了三倍。

考虑到数据总量和命中数量对查找结果的影响,增加了数据条数,使用了73257条数据,并把参数范围调整到[1, 100]

BenchmarkVideoSearch-4             60788           2030153 ns/op
BenchmarkTouseridSearch-4           3118          38672250 ns/op

速度差了十九倍,说明所查找的数据条数和数据总量对于结果影响很大。

添加数据库索引

User结构

由于查找操作仅涉及ID和username,并且username是唯一的,所以本项目为username添加唯一索引 Name string `gorm:"uniqueIndex:, type:varchar(128);not null;index"` //用户名

Video结构

Video的查找操作涉及到上传视频用户user_id,上传时间created_at,所以为这两列添加索引

Favorite结构

Favorite的查找操作涉及到点赞的用户ID和点赞的视频ID,而且是联合查询,因为要先检查这个点赞操作是不是存在数据库中,如果已经存在,那么就不能重复插入。 所以需要下边的语句, database.MySqlDb.Table("favorites").Select("count(*)").Where("user_id = ? AND video_id = ?", userid, videoid).Scan(&nums)

因此为user_id和video_id创建联合索引

UserId   int64 `gorm:"index:idx_member, priority:1, not null"` //发起点赞操作的用户ID
VideoId  int64 `gorm:"index:idx_member, priority:2, not null"` //受到点赞的视频ID

Comment结构

由于需要根据video_id找到对应的comment,所以为video_idd添加了索引。

VideoId   int64  `gorm:"index:, not null"`  

Relation结构

需要user_id和to_user_id查找,所以都添加了索引

UserId   int64 `gorm:"index:, not null"` //发起关注者ID
ToUserId int64 `gorm:"index:, not null"` //被关注者的ID

结论

添加索引有利于数据库的查找,但是对于一些频繁查找操作来讲直接从数据库找还是很慢,考虑添加Redis缓冲。