这是我参与「第三届青训营 -后端场」笔记创作活动的第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"` //评论创建时间
}
其中VideoId 和 ToUserId保存相同的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缓冲。