StackExchange 克隆:我应该在哪里添加索引?

Hop*_*eam 4 mysql index database-design index-tuning

我正在创建一个开源堆栈交换克隆,以下是我的架构。我应该在什么上添加索引才能使其最佳?

这是Rails 格式的架构(下面也有 SQL 格式):

  create_table "comments", force: true do |t|
    t.integer  "id"
    t.integer  "post_id",                null: false
    t.integer  "user_id",                null: false
    t.text     "body",                   null: false
    t.integer  "score",      default: 0, null: false
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "post_types", force: true do |t|
    t.integer  "id"
    t.string "name", null: false
  end

  create_table "posts", force: true do |t|
    t.integer  "id"
    t.integer  "post_type_id",       limit: 2,               null: false
    t.integer  "accepted_answer_id"
    t.integer  "parent_id"
    t.integer  "user_id",                                    null: false
    t.text     "title",              limit: 255,             null: false
    t.text     "body",                                       null: false
    t.integer  "score",                          default: 0, null: false
    t.integer  "views",                          default: 1, null: false
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "posts_tags", force: true do |t|
    t.integer  "id"
    t.integer "post_id", null: false
    t.integer "tag_id",  null: false
  end

  create_table "tag_synonyms", force: true do |t|
    t.integer  "id"
    t.string "source_tag", null: false
    t.string "synonym",    null: false
  end

  create_table "tags", force: true do |t|
    t.integer  "id"
    t.string "name", null: false
  end

  create_table "users", force: true do |t|
    t.integer  "id"
    t.string   "first_name",   limit: 50
    t.string   "last_name",    limit: 50
    t.string   "display_name", limit: 100,             null: false
    t.string   "email",        limit: 100,             null: false
    t.string   "password",                             null: false
    t.string   "salt",                                 null: false
    t.string   "about_me"
    t.string   "website_url"
    t.string   "location",     limit: 100
    t.integer  "karma",                    default: 0, null: false
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "vote_types", force: true do |t|
    t.integer  "id"
    t.string "name", null: false
  end

  create_table "votes", force: true do |t|
    t.integer  "id"
    t.integer  "post_id",      null: false
    t.integer  "vote_type_id", null: false
    t.integer  "user_id",      null: false
    t.datetime "created_at"
    t.datetime "updated_at"
  end
Run Code Online (Sandbox Code Playgroud)

这也是 SQL 中的原始结构:

CREATE TABLE `comments` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `post_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `body` text NOT NULL,
  `score` int(11) NOT NULL DEFAULT '0',
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `post_types` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `posts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `post_type_id` smallint(6) NOT NULL,
  `accepted_answer_id` int(11) DEFAULT NULL,
  `parent_id` int(11) DEFAULT NULL,
  `user_id` int(11) NOT NULL,
  `title` tinytext NOT NULL,
  `body` text NOT NULL,
  `score` int(11) NOT NULL DEFAULT '0',
  `views` int(11) NOT NULL DEFAULT '1',
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `posts_tags` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `post_id` int(11) NOT NULL,
  `tag_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `tag_synonyms` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `source_tag` varchar(255) NOT NULL,
  `synonym` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `tags` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `first_name` varchar(50) DEFAULT NULL,
  `last_name` varchar(50) DEFAULT NULL,
  `display_name` varchar(100) NOT NULL,
  `email` varchar(100) NOT NULL,
  `password` varchar(255) NOT NULL,
  `salt` varchar(255) NOT NULL,
  `about_me` varchar(255) DEFAULT NULL,
  `website_url` varchar(255) DEFAULT NULL,
  `location` varchar(100) DEFAULT NULL,
  `karma` int(11) NOT NULL DEFAULT '0',
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `vote_types` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `votes` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `post_id` int(11) NOT NULL,
  `vote_type_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
);
Run Code Online (Sandbox Code Playgroud)

小智 6

让我们来看看这里的一些事情......(现在你实际上显示了数据库结构,而不仅仅是 Rails 的“视图”,我们可以看到发生了什么......)

“关系数据库”是关于“关系”的。关系通过让查询“连接”两个或多个表来表达。联接要求两个表上的列都匹配。例如,post_idcomment表匹配idposts表。

如果您有一些评论并想找到它们所在帖子的详细信息,那么您需要从posts表中选择comment_id某个(一组)值。

当您选择一列时,您通常(通常)希望对该列进行索引。

因此,对于您的每个“主键”列,您还将自动拥有一个索引。您还需要索引关系的“另一面”。

评论表

created_at不应该为空。可空列通常对性能的影响很小。所有评论都已创建,因此都应该有一个日期,并且不需要为空。

如果您执行选择特定帖子评论的查询,那么您需要在post_id.

我怀疑您可能偶尔也会查询给定用户的所有帖子,这意味着您可能需要另一个索引 user_id

post_types

这里没有问题。

帖子

您将需要以下索引:

  • 如果您想为给定的父母选择帖子, parent_id
  • 如果要为给定用户选择帖子,则 user_id
  • 如果您想为给定类型选择帖子,那么 post_type_id
  • 您还需要索引标题,因为这可能会使搜索更容易。
  • 查看正文的全文索引。

应该created_date可以为空吗?

后标签

您将在此处需要两个索引,并且出于性能原因,您可能希望将它们复制。解释为什么超出了这个答案,但寻找“索引覆盖率”

  • 索引tag_idpost_id
  • 索引post_idtag_id

标签同义词

source_tag应该source_id并且应该是一个整数。还带有索引。 synonym应该synonym_id并且应该是一个整数。它也应该有一个索引。

标签

美好的

用户

推荐一个索引:

  • display_name - 所以人们可以很容易地找到自己(希望你有足够的用户来需要它)。

(你确定你不介意没有名字的用户吗)

应该created_date可以为空吗?

Vote_Types

美好的

投票

vote_type_idpost_id并且user_id每个人都应该有自己的索引。

应该created_date可以为空吗?

结论

现在您对应该从哪些索引开始有了一些建议,下一步是监控您的实际性能较差的地方,并针对这些区域进行额外优化。为此,您需要实际运行应用程序,找出实际查询的样子,然后运行这些查询以查看实际执行计划是什么,以及这些计划在何处需要通过添加索引来获得帮助。

- 您的数据库中没有任何主键。主键是数据库参照完整性的一部分,确保您和您的程序做“正确的事”。此外,主键是作为索引实现的,因此它们将确保对表的主键相关访问速度很快。- 你post_id的帖子表上没有一列????真的吗?这没有任何意义.... 除非parent_id应该是唯一标识符..... - 同样,你没有user_idusers桌子上。是什么赋予了?

因此,您没有键,因此,您错过了通常最关键的索引。为每张桌子设置一把钥匙,您将在那里完成大部分工作。

大多数数据库现在都包含可以根据您经常运行的查询为您推荐索引的工具。