应用程序开发人员常见的数据库开发错误有哪些?
当前回答
这里有一个视频链接,名为“经典数据库开发错误和克服它们的五种方法”,作者是Scott Walz
其他回答
不了解DBMS的工作原理。
如果不了解离合器的工作原理,你就不能正确地驾驶变速杆。如果不了解实际上只是在硬盘上写入文件,就无法理解如何使用数据库。
具体地说:
Do you know what a Clustered Index is? Did you think about it when you designed your schema? Do you know how to use indexes properly? How to reuse an index? Do you know what a Covering Index is? So great, you have indexes. How big is 1 row in your index? How big will the index be when you have a lot of data? Will that fit easily into memory? If it won't it's useless as an index. Have you ever used EXPLAIN in MySQL? Great. Now be honest with yourself: Did you understand even half of what you saw? No, you probably didn't. Fix that. Do you understand the Query Cache? Do you know what makes a query un-cachable? Are you using MyISAM? If you NEED full text search, MyISAM's is crap anyway. Use Sphinx. Then switch to Inno.
当查询在您的开发机器上运行得如此之快时,一旦您向应用程序抛出一些流量,查询就会崩溃并阻塞,这要归咎于db引擎。
许多开发人员倾向于对数据库执行多个查询(通常查询一个或两个表),提取结果并在java/c/c++中执行简单的操作——所有这些都可以用一条SQL语句完成。
许多开发人员通常没有意识到,在开发环境中,数据库和应用程序服务器在他们的笔记本电脑上——但在生产环境中,数据库和应用程序服务器将在不同的机器上。因此,对于每个查询,在应用程序服务器和数据库服务器之间传递的数据都有额外的n/w开销。我惊奇地发现,为了向用户呈现一个页面,应用程序服务器对数据库服务器进行了大量的数据库调用!
1. 没有使用合适的索引
这是一个相对简单的问题,但它仍然经常发生。外键上应该有索引。如果你在WHERE中使用一个字段,你应该(可能)在它上面有一个索引。根据需要执行的查询,这样的索引通常应该涵盖多个列。
2. 没有强制引用完整性
您的数据库在这里可能有所不同,但如果您的数据库支持引用完整性(意味着所有外键都保证指向一个存在的实体),那么您应该使用它。
在MySQL数据库中,这种失败是很常见的。我不相信MyISAM支持它。InnoDB。你会发现有些人正在使用MyISAM,有些人正在使用InnoDB,但根本不用它。
更多:
如果我总是用php控制我的数据库输入,像NOT NULL和FOREIGN KEY这样的约束有多重要? 外键在数据库设计中真的有必要吗? 外键在数据库设计中真的有必要吗?
3.使用自然主键而不是代理(技术)主键
自然键是基于(表面上)惟一的外部有意义的数据的键。常见的例子是产品代码、两个字母的州代码(美国)、社会保险号等等。代理键或技术主键是那些在系统之外完全没有意义的键。它们纯粹是为了识别实体而发明的,通常是自动递增的字段(SQL Server, MySQL,其他)或序列(最著名的是Oracle)。
在我看来,您应该始终使用代理键。这个问题出现在以下问题中:
你喜欢你的主键吗? 表中主键的最佳实践是什么? 在这种情况下,您将使用哪种主键格式。 代理键vs.天然/业务键 我应该有一个专用的主键字段吗?
这是一个有点争议的话题,你不会得到普遍的同意。虽然您可能会发现有些人认为自然键在某些情况下是可以的,但您不会发现任何对代理键的批评,除了可以说是不必要的之外。要我说,这是一个很小的缺点。
记住,即使是国家也可能不复存在(例如,南斯拉夫)。
4. 编写需要DISTINCT才能工作的查询
您经常在orm生成的查询中看到这一点。查看Hibernate的日志输出,您将看到所有查询都以以下开头:
SELECT DISTINCT ...
这是一种确保不会返回重复行从而获得重复对象的捷径。你有时也会看到有人这样做。如果你看到太多,那就是一个真正的危险信号。并不是说DISTINCT不好或没有有效的应用程序。它确实(在这两方面),但它不是编写正确查询的替代品或权宜之计。
为什么我讨厌DISTINCT:
什么时候开始变糟了 意见是当一个开发者 建实体查询、加盟 桌子在一起,突然之间 他意识到看起来他是 获取重复的(甚至更多)行 他的直接反应…他的 “解决”这个“问题”的方法就是 抛出DISTINCT关键字和POOF 他所有的烦恼都消失了。
5. 聚合优于连接
数据库应用程序开发人员的另一个常见错误是没有意识到与连接相比,聚合(即GROUP by子句)的开销要高得多。
为了让你知道这个问题有多普遍,我在这里写过几次这个话题,并且因为它而被否决了很多次。例如:
SQL语句- " join " vs " group by and having ":
第一个查询: 选择用户标识 从userrole WHERE roleid IN (1,2,3) 按用户id分组 count (1) = 3 查询时间:0.312 s 第二个查询: 选择t1.userid FROM userrole t1 JOIN userrole t2在t1上。Userid = t2。userid和t2。Roleid = 2 JOIN userrole t3开启t2。Userid = t3。userid和t3。Roleid = 3 和t1。Roleid = 1 查询时间:0.016 s 这是正确的。连接版本I 求婚的速度比 聚合版本。
6. 没有通过视图简化复杂的查询
并不是所有的数据库供应商都支持视图,但对于那些支持视图的供应商,如果使用得当,它们可以极大地简化查询。例如,在一个项目中,我为CRM使用了一个通用的Party模型。这是一种非常强大和灵活的建模技术,但可能导致许多连接。在这个模型中有:
政党:个人和组织; 当事人角色:当事人所做的事情,例如雇员和雇主; 政党角色关系:这些角色如何相互关联。
例子:
泰德是一个人,是一个政党的子类型; Ted有很多角色,其中一个是员工; 英特尔是一个组织,是一个政党的子类型; 英特尔有很多角色,其中之一是雇主; 英特尔雇佣了泰德,这意味着他们各自的角色之间有关系。
有五张桌子连接在一起,把泰德和他的雇主联系起来。假设所有员工都是person(不是组织),并提供以下帮助视图:
CREATE VIEW vw_employee AS
SELECT p.title, p.given_names, p.surname, p.date_of_birth, p2.party_name employer_name
FROM person p
JOIN party py ON py.id = p.id
JOIN party_role child ON p.id = child.party_id
JOIN party_role_relationship prr ON child.id = prr.child_id AND prr.type = 'EMPLOYMENT'
JOIN party_role parent ON parent.id = prr.parent_id = parent.id
JOIN party p2 ON parent.party_id = p2.id
突然之间,你就有了一个非常简单的数据视图,但却有了一个高度灵活的数据模型。
7. 没有消毒输入
这是一个巨大的问题。现在我喜欢PHP,但如果你不知道你在做什么,它真的很容易创建易受攻击的网站。没有什么比小鲍比的故事更能概括它了。
用户通过url、表单数据和cookie提供的数据应始终被视为恶意和消毒。确保你得到了你想要的。
8. 不使用准备好的语句
准备语句是指在编译查询时减去插入、更新和WHERE子句中使用的数据,然后再提供这些数据。例如:
SELECT * FROM users WHERE username = 'bob'
vs
SELECT * FROM users WHERE username = ?
or
SELECT * FROM users WHERE username = :username
这取决于你的平台。
我曾看到数据库因为这样做而崩溃。基本上,任何现代数据库每次遇到新的查询都必须编译它。如果它遇到了以前见过的查询,就给了数据库缓存已编译查询和执行计划的机会。通过大量执行查询,可以让数据库有机会发现并相应地进行优化(例如,将编译后的查询固定在内存中)。
使用准备语句还可以提供有关某些查询使用频率的有意义的统计数据。
准备好的语句还可以更好地保护您免受SQL注入攻击。
9. 不够正常化
数据库规范化基本上是优化数据库设计或如何将数据组织到表中的过程。
就在本周,我遇到了一些代码,其中有人将一个数组内爆,并将其插入到数据库中的一个字段中。规范化的方法是将该数组的元素视为子表中的单独行(即一对多关系)。
这也出现在存储用户id列表的最佳方法中:
我在其他系统中看到,列表存储在一个序列化的PHP数组中。
但是,缺乏正常化有多种形式。
更多:
正常化:多远才算够? SQL by Design:为什么需要数据库规范化
10. 过度正常化
这似乎与前面的观点相矛盾,但是正常化,像许多事情一样,是一种工具。它是达到目的的一种手段,本身并不是目的。我认为许多开发者忘记了这一点,开始将“手段”视为“目的”。单元测试就是一个很好的例子。
我曾经工作过一个系统,它为客户端提供了一个巨大的层次结构,大概是这样的:
Licensee -> Dealer Group -> Company -> Practice -> ...
因此,在获得任何有意义的数据之前,您必须将大约11个表连接在一起。这是正常化走得太远的一个很好的例子。
更重要的是,仔细考虑的非正规化可以带来巨大的性能好处,但在这样做时必须非常小心。
更多:
为什么过多的数据库规范化可能是一件坏事 在数据库设计中规范化要走多远? 什么时候不规范化你的SQL数据库 也许正常化并不正常 所有数据库标准化之母关于编码恐怖的争论
11. 使用独占弧
排他弧是一个常见的错误,其中一个表创建了两个或多个外键,其中一个且只有一个外键可以是非空的。大错误。首先,维护数据完整性变得更加困难。毕竟,即使使用引用完整性,也没有什么可以阻止设置两个或多个外键(尽管有复杂的检查约束)。
关系数据库设计实践指南:
我们强烈建议不要在任何地方修建排他性的弧形建筑 有可能,因为它们编写代码时会很尴尬 并造成更多的维护困难。
12. 根本没有对查询进行性能分析
实用主义是至高无上的,尤其是在数据库领域。如果你坚持的原则已经成为教条,那么你很可能已经犯了错误。以上面的聚合查询为例。综合版本可能看起来“不错”,但其性能却很糟糕。一场业绩比较本应结束这场争论(但它并没有),但更重要的是:从一开始就发表这种无知的观点是无知的,甚至是危险的。
13. 过度依赖UNION ALL,特别是UNION结构
在SQL术语中,UNION只是连接一致的数据集,这意味着它们具有相同的类型和列数。它们之间的区别是UNION ALL是一个简单的连接,应该在任何可能的地方被优先使用,而UNION将隐式地执行DISTINCT来删除重复的元组。
工会,像DISTINCT,有他们的位置。有有效的申请。但是如果您发现自己做了很多这样的事情,特别是在子查询中,那么您可能做错了什么。这可能是由于糟糕的查询结构或设计糟糕的数据模型迫使您这样做。
union,特别是在连接或依赖子查询中使用时,可能会削弱数据库。只要有可能就尽量避免。
14. 在查询中使用OR条件
这似乎是无害的。毕竟,and是可以的。OR应该也可以吧?错了。基本上,AND条件限制数据集,而OR条件增长数据集,但不是以一种有利于优化的方式。特别是当不同的OR条件可能相交时,从而迫使优化器有效地对结果进行DISTINCT操作。
Bad:
... WHERE a = 2 OR a = 5 OR a = 11
好:
... WHERE a IN (2, 5, 11)
现在,SQL优化器可以有效地将第一个查询转换为第二个查询。但事实可能并非如此。千万别这么做。
15. 没有将数据模型设计为适合高性能解决方案
这一点很难量化。它通常是通过其效果来观察的。如果您发现自己为相对简单的任务编写了复杂的查询,或者用于查找相对直接的信息的查询效率不高,那么您可能有一个糟糕的数据模型。
在某种程度上,这一点总结了所有早期的观点,但它更像是一个警世故事,做像查询优化这样的事情,往往是在应该第二做的时候先做的。首先,在尝试优化性能之前,您应该确保拥有良好的数据模型。正如Knuth所说:
过早的优化是万恶之源
16. 数据库事务的不正确使用
特定流程的所有数据更改都应该是原子性的。也就是说,如果操作成功,则完全成功。如果失败,则数据保持不变。不应该出现“半途而废”的改动。
理想情况下,实现这一点的最简单方法是,整个系统设计应该努力通过单个INSERT/UPDATE/DELETE语句来支持所有数据更改。在这种情况下,不需要特殊的事务处理,因为数据库引擎应该自动处理。
但是,如果任何流程确实需要将多条语句作为一个单元执行,以保持数据处于一致状态,则需要适当的事务控制。
在第一条语句之前开始一个事务。 在最后一条语句之后提交事务。 对于任何错误,回滚事务。非常NB!不要忘记跳过/中止错误之后的所有语句。
还建议仔细注意数据库连接层和数据库引擎在这方面如何交互的细微差别。
17. 不理解“基于集合”的范式
SQL语言遵循一种适用于特定类型问题的特定范式。尽管有各种特定于供应商的扩展,但该语言仍难以处理在Java、c#、Delphi等语言中微不足道的问题。
这种理解的缺乏体现在几个方面。
不恰当地在数据库上强加了太多的过程逻辑或命令式逻辑。 游标使用不当或过多。尤其是当一个问题就足够了。 错误地假设在多行更新中每个受影响的行触发一次火灾。
确定明确的职责分工,力求用合适的工具解决每一个问题。
不使用索引。
推荐文章
- 不可重复读和幻影读的区别是什么?
- 外键约束:何时使用ON UPDATE和ON DELETE
- 连接查询vs多个查询
- MySQL:在同一个MySQL实例上克隆MySQL数据库
- 优化PostgreSQL进行快速测试
- 表被标记为崩溃,应该修复
- 在Android SQLite中处理日期的最佳方法
- 使用{merge: true}设置的Firestore与更新之间的差异
- mysql_connect():[2002]没有这样的文件或目录(试图通过unix:///tmp/mysql.sock连接)在
- 使用电子邮件地址为主键?
- MongoDB在v4之前不兼容ACID意味着什么?
- 第一次设计数据库:我是否过度设计了?
- 我应该在SQL varchar(长度)中考虑电话的最长的全球电话号码是什么
- MySQL查询转储
- phpMyAdmin错误>格式参数错误?