我为自己创建了一个数据库,我有一个注册表,要求人们提供姓名,密码,电子邮件,手机,年龄和地点。它有望成为一个约会网站。
到目前为止,我(对于我的数据库)
create table members(
user VARCHAR(16),
password VARCHAR(16),
email VARCHAR(320),
mobile VARCHAR(15),
age INT(3)
#location ???
INDEX(user(6));
)
编辑 忘记提问
location
应使用什么数据类型,数据库看起来足够安全?
位置是按国家/地区,并通过下拉菜单选择。
答案 0 :(得分:7)
数据库看起来足够安全吗?
没有。它需要做一些解释。
让我们从所有这些限制开始。过度热情地使用色谱柱限制是一个非常普遍的问题。
这些限制如何发挥作用常常被误解,例如您询问安全性,或者您可能认为自己节省了空间。真正的问题是难以对软件其他部分如何进入数据库进行硬编码,而且你的限制非常吝啬。
您问这是否会使表更安全。列限制并非真正关于安全性,但我认为它们理论上会阻止某人填满您的磁盘,但这并不是您所拥有的限制。
例如,您的密码限制为16个字符。这不是数据库应该做出的决定,而是安全考虑因素。当您以后查看密码安全性时,您会发现16个字符的密码几乎不够用。你想要更像64或128的东西,这需要昂贵的alter table
。
更重要的是,您要以明文形式存储密码。这是一个很大的安全问题。
然后你有电子邮件(大概是电子邮件地址)设置为320个字符?!这真是一个电子邮件地址!但是用户只能获得16个名字?
一个常见的误解是这些限制会减少使用的磁盘数量。他们没有。 varchar
只会存储该行所需的内容。 age INT(3)
使用的空间不会比age INT
少,它是固定大小的。 好的,它确保你在约会网站上没有任何12938岁的孩子它甚至没有这样做。它显示了多少字段, 肯定 不是应该在您的架构中的字段。 MySQL做了一些奇怪的事情。
你可以使用unsigned tinyint
以1字节存储从0到255 ......但是一旦你担心单个字节就会变得愚蠢。这一切都没有实际意义,根本不存储他们的年龄。存放他们的生日。因为人们变老了。
足够使用限制的错误方法,正确的方法是什么?
限制用于强制执行数据完整性(以及技术限制,请参阅注释)。这是关于它的。您希望制定一个足够灵活的架构来支持您的应用程序想要成为的任何内容,同时还要确保数据的正确性,而不必经常猜测它。
更好的架构可能如下所示:
create table members(
id primary key auto_increment,
username varchar(64) unique,
password_hash varchar(128),
email varchar(64) unique,
mobile varchar(32),
birthday datetime,
location integer references(locations),
index(birthday)
)
您的桌子上缺少主键,这是个大问题。用户名可以更改,并且您不希望引用该用户的所有内容都中断。相反,使用简单的自动递增整数。 "但我不会让用户更改他们的名字!"是的,还记得我所说的关于将软件的硬编码限制纳入数据模型的内容吗?如何构建架构多年来都会产生影响。
最初我从字段中取出所有限制并将其切换为无限text
。他们都没有令人信服的理由来限制。除非你有充分的理由,否则只需使用text
或varchar
即可。限制在数据模型中处理,可由程序员和设计人员更改。 text
或varchar
字段只会占用所需的空间。
...但@PaulSpiegel在MySQL's has limitations on how big a field it will index的评论中指出。我已经习惯了没有这种限制的Postgres。所以我将它们切换到varchar
并选择了慷慨的限制。技术限制可能是使用限制的原因。
username
(不是user
,因为它可以引用整个用户,而不仅仅是他们的名字),而email
已被标记为unique
。这是关于数据完整性,您不希望两个人具有相同的用户名,并且您希望确保每个帐户都有一个联系点(您可能会认为这会将行为放入数据库中,您可能会是的,但删除唯一索引比添加一个索引更容易。
然后我们遇到了明显的安全问题。 从不存储密码! 永远。从来没有。而是存储密码的哈希值。如果您不知道我在说什么,请立即停止并阅读Salted Password Hashing - Doing it Right。
我们将age
存储为birthday
,而不是存储datetime
。存储用户的年龄并不是在展望未来,明年会发生什么?与他们的生日,你可以计算他们的年龄,甚至给他们生日礼物!通过将其存储为datetime
,您可以使用MySQL's confusing date and time functions对其进行各种日期计算。
你询问了如何处理位置问题。位置可能意味着很多东西,它可能变得非常复杂。这不是您现在需要做出的决定,因此最好做到这一点,以便以后可以进行扩展和更改。将它放在自己的表中,并用外键引用它。我们稍后再回过头来看,这就是重点。
最后,索引。没有必要对您的磁盘空间如此吝啬,以至于您只存储用户名的前6个字符!按用户名查找人员将非常非常普遍,给它一个完整的索引。但我们并不需要一个,声明一列unique
给它一个索引。
索引可以提高查询性能,但它们也可以占用磁盘空间并减慢插入速度。而不是直接对索引发疯,等到你看到你将要做什么查询以及性能如何。我唯一明确的指标是birthday
,因为我非常确定约会网站的行为是想按年龄限制。
这里缺少的是您的数据模型。这是数据顶部的代码,例如Member类。它将处理成员可以执行的所有操作,包括访问数据库以及应该具有的限制。模型是唯一触及数据库的模型,其余代码调用模型上的方法。这样可以更改数据库,而不必担心影响整个项目。
这被称为模型 - 视图 - 控制器或MVC,它是目前数据驱动应用程序编码的基本方式。 Ruby On Rails就是一个很好的例子。看看MVC。
好的,位置。我们已将location
作为自己的表格。这使它成为一个抽象的概念,而不是members
表中的一些硬编码字段。
位置真的很复杂很快。所以我们会保持简单。从其他用户想要了解的一些基本信息开始:谁在附近。最小的是邮政编码和国家,你可以从中得到很多。你可能也想保留城市和州,因为这是人们想要找人的另一种方式。
create table locations (
id integer primary key,
city text,
province text,
country text,
postal_code text
);
制作位置模型以封装和管理位置数据。
现在,您可以通过管理位置数据来获取尽可能多的详细信息,而不会弄乱您的成员表。您可以执行@PaulSpiegel建议的内容,并在其余代码中列出所有国家/地区及其名称,以确保他们使用真实国家/地区(数据完整性)。您可以使用他们的邮政编码来获取他们的城市和省份。您可以存储GPS数据,如果它们会提供给您。
所以,呃......如果你刚刚开始,这可能看起来势不可挡。数据建模很复杂。它不一定很难,只需要考虑许多可移动的部件和事物,这样您的应用程序就不会受到架构的限制。您可能必须使用过于简单化的模式制作一些应用程序才能真正理解。
让我们看看我是否能把它煮沸。
答案 1 :(得分:1)
创建一个countries
表格,用于存储您要支持的所有国家/地区。
code CHAR(2) PRIMARY KEY,
name VARCHAR(50) NOT NULL UNIQUE
code | name
=====|=====
FR | France
GB | United Kingdom
US | United States of America
.. | ...
您可以在此处找到 ISO 3166-1 代码:https://en.wikipedia.org/wiki/ISO_3166-1
使用此表创建下拉菜单。
在您的成员表格中,您可以定义列
country_code CHAR(2) NOT NULL
它应该是引用countries
表
CONSTRAINT FOREIGN KEY fk_members_countries (country_code) REFERENCES countries(code)