适当的索引(或删除)以优化大型数据集表

Posted

技术标签:

【中文标题】适当的索引(或删除)以优化大型数据集表【英文标题】:proper index (or removal) to optimize a large data set table 【发布时间】:2012-06-20 16:30:57 【问题描述】:

我们有一个“访问者”跟踪模式正在进行中 - 当它被推送时,似乎对数据库服务器造成了一些压力。

VISITORS 表通过 HASH 标识唯一用户(当前记录 310,000 条)。对散列执行搜索,如果未找到,则添加。以下两个表需要ID

CREATE TABLE  visitors (
    id int(10) UNSIGNED NOT NULL auto_increment,
    ip varchar(25) NOT NULL,
    hash varchar(64) NOT NULL,
    first_visit varchar(32) NOT NULL,
    created_at datetime NOT NULL default '0000-00-00 00:00:00',
    PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;   

ALTER TABLE visitors ADD UNIQUE INDEX (hash);
ALTER TABLE visitors ADD INDEX (created_at);

VISITOR_VISITS 表仅在我们可以识别一些推荐来源时识别用户何时访问(当前计数为 142,000)。执行搜索以查找 visitor_id、type 和 visit_date。如果没有找到 - 它被添加。下表中使用了 ID。

CREATE TABLE  visitor_visits (
    id int(10) UNSIGNED NOT NULL auto_increment,
    visitor_id int(10) UNSIGNED NOT NULL,
    source varchar(64) NULL DEFAULT NULL DEFAULT NULL,
    medium varchar(64) NULL DEFAULT NULL,
    campaign varchar(256) NULL DEFAULT NULL,
    page varchar(32) NULL DEFAULT NULL,
    landing varchar(32) NULL DEFAULT NULL,
    type enum('fundraiser_view') NULL DEFAULT NULL,
    visit_date date NOT NULL default '0000-00-00',
    created_at datetime NOT NULL default '0000-00-00 00:00:00',
    PRIMARY KEY (id)
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

ALTER TABLE visitor_visits ADD UNIQUE INDEX (visitor_id,type,visit_date);
ALTER TABLE visitor_visits ADD CONSTRAINT FK_visits_visitor_id FOREIGN KEY (visitor_id) REFERENCES visitors(id);

PAGE_VIEWS 记录单个页面浏览量(不是所有页面,只是我们正在跟踪的页面)。它可以链接到访问者并可以引用访问者访问(当前计数 240 万 - 更高的原因是我们在记录单个页面后开始微访问者记录)。根据已识别用户的 view_date,使用插入/重复查询将记录添加到此。由于不需要ID,因此不需要纯查找查询

CREATE TABLE page_views (
  id int(10) UNSIGNED NOT NULL auto_increment,
  page_id int(10) UNSIGNED NOT NULL,
  current_donations decimal(10,2) NOT NULL DEFAULT 0,
  ip varchar(25) NOT NULL,
  hash varchar(32) NOT NULL,
  visitor_id int(10) UNSIGNED NULL DEFAULT NULL AFTER,
  visitor_visit_id int(10) UNSIGNED NULL DEFAULT NULL AFTER,
  page_views int(10) UNSIGNED NOT NULL DEFAULT 0,
  widget_views int(10) UNSIGNED NOT NULL DEFAULT 0,
  view_date date NOT NULL,
  viewed_at datetime NOT NULL default '0000-00-00 00:00:00',
  created_at datetime NOT NULL default '0000-00-00 00:00:00',
  PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

ALTER TABLE page_views ADD UNIQUE INDEX (page_id,view_date,visitor_id,hash);
ALTER TABLE page_views ADD INDEX (visitor_id);
ALTER TABLE page_views ADD INDEX (visitor_visit_id);
ALTER TABLE page_views ADD CONSTRAINT FK_page_views_page_id FOREIGN KEY (page_id)    REFERENCES pages(id);
ALTER TABLE page_views ADD CONSTRAINT FK_page_views_visitor_id FOREIGN KEY (visitor_id)    REFERENCES visitors(id);
ALTER TABLE page_views ADD CONSTRAINT FK_page_views_visit_id FOREIGN KEY (visitor_visit_id) REFERENCES visitor_visits(id);

上周,由于一篇新闻文章,我们的网站吸引了很多人,而这位访问者发现该网站的性能出现瓶颈。我想知道那里是否有明显的优化。会不会是外键约束?过度索引?需要更好的索引?

【问题讨论】:

你可以试试unhex(hash),并存储为二进制 【参考方案1】:

试试这个 :: 1) varchar 上的索引并没有太大提高性能。 2)尝试在日期范围内对表进行分区。

【讨论】:

您的意思是根据日期范围创建了两个(或更多)表吗? 无需建表,大数据量的单表创建多个分区 有没有办法拥有动态分区?还是您必须根据需要添加分区?例如,是否可以无限期地添加基于月-年组合的分区,而不是定义到达组合的每个分区? dev.mysql.com/doc/refman/5.1/en/partitioning-overview.htmlforums.mysql.com/read.php?106,264106,264110【参考方案2】:

你没有告诉我们什么是你的数据库的瓶颈,所以我只是猜测它是 InnoDB 并发写入。如果不是这样并且问题仅出在 SELECTs 上(我对此表示怀疑),您应该向我们展示确切的查询。您可以尝试通过创建临时表然后将内容从 in 批量移动到主表来降低写入性能:

CREATE TABLE page_views_tmp (
  id int(10) UNSIGNED NOT NULL auto_increment,
  page_id int(10) UNSIGNED NOT NULL,
  current_donations decimal(10,2) NOT NULL DEFAULT 0,
  ip varchar(25) NOT NULL,
  hash varchar(32) NOT NULL,
  visitor_id int(10) UNSIGNED NULL DEFAULT NULL AFTER,
  visitor_visit_id int(10) UNSIGNED NULL DEFAULT NULL AFTER,
  page_views int(10) UNSIGNED NOT NULL DEFAULT 0,
  widget_views int(10) UNSIGNED NOT NULL DEFAULT 0,
  view_date date NOT NULL,
  viewed_at datetime NOT NULL default '0000-00-00 00:00:00',
  created_at datetime NOT NULL default '0000-00-00 00:00:00',
  PRIMARY KEY (id)
) ENGINE=MEMORY DEFAULT CHARSET=utf8; 

然后,每隔几秒或在此表中有大量行之后:

START TRANSACTION;

INSERT INTO page_views SELECT * FROM page_views_tmp;
DELETE FROM page_views_tmp;

COMMIT;

【讨论】:

以上是关于适当的索引(或删除)以优化大型数据集表的主要内容,如果未能解决你的问题,请参考以下文章

如何命名在存储过程中返回的数据集表?

战略数据分析报告数据库优化的方法

优化解决方案以在大型数据集上找到共同的第三个

访问时填充数据集表

Vb.Net 数据集表 = 新数据表

大型网站核心技术