在MySQL中使用带有NULL字段的UNIQUE索引?

时间:2011-06-10 19:36:25

标签: mysql sql

我可以定期检查当前在线的用户列表。我想把它变成有用的东西,比如每个用户的登录/登出时间条目列表。除了检查当前在线的人之外,没有其他方法可以确定此信息 经过一番思考后我想出了类似的东西:

CREATE TABLE onlineActivity (
    id INT UNSIGNED NOT NULL AUTO_INCREMENT,
    name CHAR (32) NOT NULL,
    login_time DATETIME NOT NULL,
    logout_time DATETIME NOT NULL,
    time SMALLINT (3) NOT NULL DEFAULT 0,
    online BOOL DEFAULT NULL,
    UNIQUE (name, online),
    PRIMARY KEY (id)
)   ENGINE = MyISAM;

我每隔几分钟运行一次此查询,以在活动列表中添加/更新名称:

INSERT INTO onlineActivity (name, login_time, logout_time, online)
    SELECT name, now(), now(), true FROM onlineList ON DUPLICATE KEY UPDATE logout_time = now()

此查询针对已注销的每个用户运行:
(这些名称是通过比较两个相邻的在线列表,即当前的在线列表和前一个在线列表确定的)

UPDATE onlineActivity SET online = NULL WHERE name = ? AND online = 1

问题:

  • 我担心在UNIQUE索引中使用NULL字段(online)是一个坏主意,并且会损害性能。我认为MySQL可能必须对每个名称执行所有online(而不是使用索引)的完整扫描,以找到非NULL的。有人可以澄清这是否是这种情况?我找不到有关MySQL如何处理这种情况的任何信息。
  • 在这方面,其他数据库系统(PostgreSQL,SQLite)的行为与MySQL不同吗?
  • 我应该代替第一个查询,为每个名称运行两个查询,以查看指定的用户当前是否在线,并采取相应的行动吗?
  • 我想到了这个设计,因为我想最大限度地减少使用的查询量,这本身就是一个有缺陷的想法吗?
  • 此表每天将获得约300~500k的新记录。我还能做些什么来减少性能下降吗?

我想存储用户活动的完整历史记录,而不是单个条目。

1 个答案:

答案 0 :(得分:1)

我不确定为什么你有一个独特的名字和在线,因为你要做的是创建一个在线活动列表。根据您的指定放置一个唯一键意味着您只能在其中有三个名称,每个州一个名称(null,true,false)。

你实际做的是尝试创建一个历史表,在这种情况下,使用当前填充表的方法,你应该在(name,logout_time)上放一个唯一的键,并使用null logout_time指示当前登录的用户(因为你只想要一个空的注销时间。

这样的事情:

CREATE TABLE onlineActivity (
    id INT UNSIGNED NOT NULL AUTO_INCREMENT,
    name CHAR (32) NOT NULL,
    login_time DATETIME NOT NULL,
    logout_time DATETIME NULL,
    time SMALLINT (3) NOT NULL DEFAULT 0,
    online BOOL not null DEFAULT false,
    UNIQUE (name, logout_time),
    PRIMARY KEY (id)
)   ENGINE = MyISAM;

然后按计划运行此更新表

INSERT IGNORE INTO onlineActivity (name, login_time, logout_time, online)
    SELECT name, now(), null, true FROM onlineList

这是用户注销

UPDATE onlineActivity SET online = false, logout_time = now() WHERE name = ? AND logout_time = null