应用程序开发人员犯的数据库开发错误
什么是应用程序开发人员常见的数据库开发错误?
1.不使用适当的指数
这是一个相对容易的事情,但它始终在发生。 外键应该有索引。 如果你在WHERE
使用一个字段,你应该(可能)有一个索引。 这些索引通常应该根据您需要执行的查询来覆盖多个列。
2.不强制参照完整性
你的数据库在这里可能会有所不同,但是如果你的数据库支持引用完整性 – 也就是说所有的外键都保证指向一个存在的实体 – 你应该使用它。
在MySQL数据库上看到这种失败是很常见的。 我不相信MyISAM支持它。 InnoDB的确如此。 你会发现使用MyISAM的人或者使用InnoDB的人,但是仍然没有使用它。
更多在这里:
- 如果我总是用php控制我的数据库输入,那么NOT NULL和FOREIGN KEY等约束有多重要?
- 外键在数据库设计中是否真的有必要?
- 外键在数据库设计中是否真的有必要?
3.使用自然而不是代理(技术)主键
自然键是基于(表面上)唯一的外部有意义数据的键。 常见的例子是产品代码,双字母州代码(US),社会安全号码等等。 代理或技术主键是那些在系统之外绝对没有意义的主键。 它们纯粹是为了识别实体而发明的,通常是自动递增的字段(SQL Server,MySQL等)或序列(最显着的是Oracle)。
在我看来,你应该总是使用代理键。 这个问题已经出现在这些问题中:
- 你喜欢你的主键?
- 表中主键的最佳做法是什么?
- 在这种情况下你将使用哪种格式的主键。
- 代理与自然/商业密钥
- 我应该有一个专门的主键字段?
这是一个有点争议的话题,你不会得到普遍的同意。 虽然你可能会发现一些人认为自然键在某些情况下是可以的,但除了被证明是不必要的,你不会发现对代理键的批评。 如果你问我,这是一个相当小的缺点。
请记住,即使是国家也不会存在 (例如南斯拉夫)。
4.编写需要DISTINCT
查询
您经常在ORM生成的查询中看到这一点。 看看Hibernate的日志输出,你会看到所有的查询开始:
SELECT DISTINCT ...
这是确保您不返回重复行并因此获取重复对象的快捷方式。 有时你会看到有人在做这个。 如果你看得太多,这是一个真正的红旗。 不是DISTINCT
不好或没有有效的应用程序。 它确实(在这两个方面),但它不是代写或正确查询的权宜之计。
从我为什么讨厌DISTINCT :
在我看来,事情开始变坏的时候,开发人员正在建立实质性的查询,将表格连接在一起,突然之间,他意识到, 看起来他正在获得重复(甚至更多)的行,并立即做出回应。他对这个“问题”的“解决方案”是抛出DISTINCT关键字, POOF所有的麻烦都消失了。
5.倾向于通过连接进行聚合
数据库应用程序开发人员的另一个常见错误是没有意识到多少昂贵的聚合(即GROUP BY
子句)可以与连接进行比较。
为了让你知道这是多么的广泛,我在这里多次写过这个话题,并且为此付出了很多的代价。 例如:
从SQL语句 – “加入”与“由…分组” :
第一个查询:
SELECT userid FROM userrole WHERE roleid IN (1, 2, 3) GROUP by userid HAVING COUNT(1) = 3
查询时间:0.312秒
第二个查询:
SELECT t1.userid FROM userrole t1 JOIN userrole t2 ON t1.userid = t2.userid AND t2.roleid = 2 JOIN userrole t3 ON t2.userid = t3.userid AND t3.roleid = 3 AND t1.roleid = 1
查询时间:0.016秒
那就对了。 我提出的连接版本比汇总版本快20倍。
6.不要通过视图简化复杂的查询
并非所有数据库供应商都支持视图,但对于那些支持视图的用户,如果明智地使用它们,可以大大简化查询。 例如,在一个项目中,我使用了CRM的通用Party模型 。 这是一个非常强大和灵活的建模技术,但可以导致许多联接。 在这个模型中有:
- 党 :人民和组织;
- 党角色 :这些党所做的事情,例如雇员和雇主;
- 党角色关系 :这些角色是如何相互关联的。
例:
- 特德是一个人,是党的一个子类型;
- 泰德有很多角色,其中之一是员工;
- 英特尔是一个组织,是党的一个子类型;
- 英特尔有很多角色,其中之一就是雇主;
- 英特尔雇用泰德,这意味着他们各自的角色之间有一种关系。
所以有五张桌子连接在一起把特德和他的雇主联系起来。 你假设所有员工都是人员(不是组织)并提供这个帮手视图:
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 = ?
要么
SELECT * FROM users WHERE username = :username
取决于你的平台。
我已经看到数据库这样做了。 基本上,每当现代数据库遇到新的查询时,都必须对其进行编译。 如果遇到以前看到的查询,那么您将为数据库提供缓存已编译查询和执行计划的机会。 通过执行查询,您可以使数据库有机会计算出并相应地进行优化(例如,通过将编译的查询固定在内存中)。
使用准备好的语句也会给你有意义的统计信息,说明使用某些查询的频率。
准备好的语句也将更好地保护您免受SQL注入攻击。
9.不够正常化
数据库规范化基本上是优化数据库设计的过程,或者是如何将数据组织到表中的过程。
就在这个星期,我碰到了一些代码,这些代码在某个数据集中爆炸了一个数组,并将其插入到一个单独的字段中。 规范化将把该数组的元素作为子表中的单独行(即一对多关系)处理。
这也是最好的方法来存储用户ID列表 :
我在其他系统中看到列表存储在序列化的PHP数组中。
但是正常化的缺乏以多种形式出现。
更多:
- 正常化:多远足够了?
- SQL设计:为什么你需要数据库规范化
10.正常化太多
这可能看起来像以前的一个矛盾,但正常化,像许多事情一样,是一个工具。 它本身就是结束而不是结束的手段。 我想很多开发者忘记了这一点,开始把“手段”当作“终点”来对待。 单元测试就是一个很好的例子。
我曾经在一个系统上有一个巨大的层次结构,客户端是这样的:
Licensee -> Dealer Group -> Company -> Practice -> ...
这样,在获得任何有意义的数据之前,你必须一起加入大约11个表格。 这是正常化过程中很好的例子。
更重要的是,仔细和认真的非规范化可以有巨大的性能好处,但是在做这件事时你必须非常小心。
更多:
- 为什么太多的数据库正常化可能是一件坏事
- 在数据库设计中采取正常化有多远?
- 何时不正常化您的SQL数据库
- 也许正常化不正常
- 所有数据库规范化的母亲编码恐怖的辩论
11.使用专用弧
独占弧是一个常见的错误,其中一个表是由两个或多个外键创建的,其中只有一个外键可以是非空的。 大错。 首先,保持数据完整性变得困难得多。 毕竟,即使具有参照完整性,也没有任何东西阻止设置这些外键中的两个或更多(尽管有复杂的检查约束)。
从实用指南到关系数据库设计 :
我们强烈建议尽可能不要使用专用的弧形结构,因为编写代码会带来很大的困难,并且会造成更多的维护困难。
12.根本不对性能进行分析
实用主义至高无上,特别是在数据库领域。 如果你坚持原则,他们已经成为一个教条,那么你很可能犯了错误。 以上面的聚合查询为例。 汇总版本可能看起来不错,但其性能是可悲的。 性能比较本应该结束辩论(但事实并非如此),更重要的是:首先发布这种不明智的观点是无知的,甚至是危险的。
13.过度依赖UNION ALL,特别是UNION构造
SQL中的UNION只是连接一致的数据集,这意味着它们具有相同的类型和数量的列。 它们之间的区别在于,UNION ALL是一个简单的连接,应该在可能的情况下优先使用UNION,而UNION会隐式地执行DISTINCT来删除重复的元组。
联盟,像DISTINCT,有他们的位置。 有有效的应用程序。 但是如果你发现自己做了很多,特别是在子查询中,那么你可能做错了什么。 这可能是一个糟糕的查询构造或设计不佳的数据模型,迫使你做这样的事情。
UNION,特别是在连接或从属子查询中使用时,可能会使数据库瘫痪。 尽量避免他们。
14.在查询中使用OR条件
这可能看起来无害。 毕竟,ANDs是好的。 或者应该也可以吧? 错误。 基本上,一个AND条件限制了数据集,而一个OR条件增长了它,但并不是以一种有利于优化的方式。 特别是当不同的OR条件可能相交时,强制优化器有效地对结果执行DISTINCT操作。
坏:
... 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#,德尔福等语言中微不足道的问题。
这种缺乏理解表现在几个方面。
- 不恰当地在数据库上施加过多的程序性或命令性的逻辑。
- 不适当或过度使用游标。 特别是当一个查询就足够了。
- 错误地假定触发器在多行更新中受影响的每行激发一次。
确定明确责任分工,力求用适当的工具来解决每一个问题。
密钥数据库设计和开发人员编程错误
-
自私的数据库设计和使用。 开发人员经常将数据库视为其个人持久对象存储,而不考虑数据中其他利益相关者的需求。 这也适用于应用程序架构师。 糟糕的数据库设计和数据完整性使第三方很难处理这些数据,并且可能大大增加系统的生命周期成本。 报告和管理信息系统往往是应用程序设计中的差异表亲,只是事后才做的。
-
滥用非规范化的数据。 过度使用非规范化数据并试图在应用程序中维护数据是数据完整性问题的秘诀。 谨慎使用去标准化。 不想添加连接到查询不是denormalising的借口。
-
害怕写SQL。 SQL不是火箭科学,实际上相当擅长。 O / R映射层在完成95%的简单查询以及适合该模型的查询方面相当出色。 有时SQL是做这个工作的最好方法。
-
教条式的“无存储程序”政策。 不管你是否相信存储过程是邪恶的,这种教条式的态度在软件项目上是没有地位的。
-
不了解数据库设计。 正常化是你的朋友,这不是火箭科学。 连接和基数是相当简单的概念 – 如果你参与数据库应用程序的开发,真的没有理由不理解它们的借口。
- 不使用数据库模式的版本控制
- 直接针对实时数据库
- 没有阅读和理解更高级的数据库概念(索引,聚集索引,约束,物化视图等)
- 未能测试可扩展性…只有3或4行测试数据永远不会给你真实的现场表演的真实情况
过度使用和/或依赖于存储过程。
一些应用程序开发人员将存储过程视为中间层/前端代码的直接扩展。 这似乎是微软堆栈开发人员的一个共同特征(我是一个人,但是我已经长大了),并产生了许多执行复杂业务逻辑和工作流处理的存储过程。 其他地方做得好得多。
存储过程在实际证明某些实际技术因素需要使用(例如,性能和安全性)的情况下是有用的。例如,保持大数据集合“接近数据”的聚合/过滤。
我最近必须帮助维护和增强一个庞大的Delphi桌面应用程序,其中70%的业务逻辑和规则是在1400个SQL Server存储过程(UI事件处理程序中的其余部分)中实现的。 这是一个噩梦,主要是由于有效的单元测试引入TSQL,缺乏封装和不良工具(调试器,编辑器)。
在过去与一个Java团队合作,我很快就发现,在这种环境下,情况恰恰相反。 一位Java架构师曾经告诉我:“数据库是为了数据,而不是代码”。
这些天我认为根本不考虑存储过程是一个错误,但是在它们提供有用的好处的情况下(请参阅其他答案),应该谨慎地使用它们(而不是默认情况下)。
头号问题? 他们只测试玩具数据库。 所以他们不知道他们的SQL会在数据库变大的时候爬行,而且有人必须随后来修复(听到的声音就是我的牙齿)。
不使用索引。
由相关子查询导致的性能不佳
大多数时候你想避免相关的子查询。 如果在子查询中存在对来自外部查询的列的引用,则子查询是相关的。 发生这种情况时,如果在应用了包含相关子查询的条件之后应用了其他条件,则子查询至少会针对每个返回的行执行一次,并且可能会执行多次。
原谅这个人为的例子和Oracle的语法,但是假设你想从上次商店每天销售不到一万美元的时候开始,找到你所有商店里雇佣的所有员工。
select e.first_name, e.last_name from employee e where e.start_date > (select max(ds.transaction_date) from daily_sales ds where ds.store_id = e.store_id and ds.total < 10000)
本例中的子查询与store_id的外部查询相关联,并将为系统中的每个员工执行。 可以优化此查询的一种方法是将子查询移至内联视图。
select e.first_name, e.last_name from employee e, (select ds.store_id, max(s.transaction_date) transaction_date from daily_sales ds where ds.total < 10000 group by s.store_id) dsx where e.store_id = dsx.store_id and e.start_date > dsx.transaction_date
在这个例子中,from子句中的查询现在是一个内联视图(也是一些Oracle特定的语法),只能执行一次。 根据您的数据模型,这个查询可能会执行得更快。 随着员工数量的增长,它会比第一个查询更好地执行。 如果员工很少,很多商店(也许很多商店没有员工),并且daily_sales表在store_id上进行了索引,则第一个查询可能会更好。 这不是一个可能的情况,但显示了一个相关的查询如何可能表现比替代方案更好。
我已经看到初级开发人员多次关联子查询,并且通常会对性能产生严重影响。 但是,在删除相关的子查询时,务必查看前后的解释计划 ,以确保不会使性能变差。
在我的经验中:
不与有经验的DBA沟通。
使用Access而不是“真实”的数据库。 有很多非常小的,甚至免费的数据库,比如SQL Express , MySQL和SQLite ,它们的工作和扩展都会更好。 应用程序通常需要以意想不到的方式扩展
忘记建立表之间的关系。 我记得当我刚开始在现在的雇主工作时,必须清理这个问题。
使用Excel存储(大量)数据。
我看到公司持有成千上万的行,并使用多个工作表(由于以前版本的Excel中的行数限制为65535)。
Excel非常适合用于报告,数据表示和其他任务,但不应被视为数据库。
我想补充一下:在高性能代码中使用“优雅”代码。 对数据库来说效果最好的代码对应用程序开发人员来说通常是丑陋的。
相信那些过早优化的废话。 数据库必须考虑原始设计和后续开发中的性能。 在我看来,性能是数据库设计的50%(40%是数据完整性,最后10%是安全性)。 一旦真正的用户和真正的流量被放置在数据库中,那么从底部开始执行的数据库将执行得不好。 过早优化并不意味着没有优化! 这并不意味着你应该编写几乎总是执行得很差的代码,因为你发现它比较容易(例如游标,除非所有的失败都不应该被允许在生产数据库中)。 这意味着你不需要考虑最后一点的表现,直到你需要。 对于在数据库上表现更好的方面知道很多,在设计和开发中忽视这一点是充其量短视的。
不使用参数化查询。 他们在停止SQL注入方面非常方便。
这是另一个答案中提到的不清理输入数据的具体示例。
我讨厌它,当开发人员使用嵌套的选择语句,甚至函数返回查询的“选择”部分内的一个选择语句的结果。
我真的很惊讶,我在这里没有看到这个地方,也许我忽略了它,尽管@adam也有类似的问题。
例:
SELECT (SELECT TOP 1 SomeValue FROM SomeTable WHERE SomeDate = c.Date ORDER BY SomeValue desc) As FirstVal ,(SELECT OtherValue FROM SomeOtherTable WHERE SomeOtherCriteria = c.Criteria) As SecondVal FROM MyTable c
In this scenario, if MyTable returns 10000 rows the result is as if the query just ran 20001 queries, since it had to run the initial query plus query each of the other tables once for each line of result.
Developers can get away with this working in a development environment where they are only returning a few rows of data and the sub tables usually only have a small amount of data, but in a production environment, this kind of query can become exponentially costly as more data is added to the tables.
A better (not necessarily perfect) example would be something like:
SELECT s.SomeValue As FirstVal ,o.OtherValue As SecondVal FROM MyTable c LEFT JOIN ( SELECT SomeDate, MAX(SomeValue) as SomeValue FROM SomeTable GROUP BY SomeDate ) s ON c.Date = s.SomeDate LEFT JOIN SomeOtherTable o ON c.Criteria = o.SomeOtherCriteria
This allows database optimizers to shuffle the data together, rather than requery on each record from the main table and I usually find when I have to fix code where this problem has been created, I usually end up increasing the speed of queries by 100% or more while simultaneously reducing CPU and memory usage.
For SQL-based databases:
- Not taking advantage of CLUSTERED INDEXES or choosing the wrong column(s) to CLUSTER.
- Not using a SERIAL (autonumber) datatype as a PRIMARY KEY to join to a FOREIGN KEY (INT) in a parent/child table relationship.
- Not UPDATING STATISTICS on a table when many records have been INSERTED or DELETED.
- Not reorganizing (ie unloading, droping, re-creating, loading and re-indexing) tables when many rows have been inserted or deleted (some engines physically keep deleted rows in a table with a delete flag.)
- Not taking advantage of FRAGMENT ON EXPRESSION (if supported) on large tables which have high transaction rates.
- Choosing the wrong datatype for a column!
- Not choosing a proper column name.
- Not adding new columns at the end of the table.
- Not creating proper indexes to support frequently used queries.
- creating indexes on columns with few possible values and creating unnecessary indexes.
…more to be added.
-
Not taking a backup before fixing some issue inside production database.
-
Using DDL commands on stored objects(like tables, views) in stored procedures.
-
Fear of using stored proc or fear of using ORM queries wherever the one is more efficient/appropriate to use.
-
Ignoring the use of a database profiler, which can tell you exactly what your ORM query is being converted into finally and hence verify the logic or even for debugging when not using ORM.
Not doing the correct level of normalization . You want to make sure that data is not duplicated, and that you are splitting data into different as needed. You also need to make sure you are not following normalization too far as that will hurt performance.
Treating the database as just a storage mechanism (ie glorified collections library) and hence subordinate to their application (ignoring other applications which share the data)
- Dismissing an ORM like Hibernate out of hand, for reasons like "it's too magical" or "not on my database".
- Relying too heavily on an ORM like Hibernate and trying to shoehorn it in where it isn't appropriate.
1 – Unnecessarily using a function on a value in a where clause with the result of that index not being used.
例:
where to_char(someDate,'YYYYMMDD') between :fromDate and :toDate
代替
where someDate >= to_date(:fromDate,'YYYYMMDD') and someDate < to_date(:toDate,'YYYYMMDD')+1
And to a lesser extent: Not adding functional indexes to those values that need them…
2 – Not adding check constraints to ensure the validity of the data. Constraints can be used by the query optimizer, and they REALLY help to ensure that you can trust your invariants. There's just no reason not to use them.
3 – Adding unnormalized columns to tables out of pure laziness or time pressure. Things are usually not designed this way, but evolve into this. The end result, without fail, is a ton of work trying to clean up the mess when you're bitten by the lost data integrity in future evolutions.
Think of this, a table without data is very cheap to redesign. A table with a couple of millions records with no integrity… not so cheap to redesign. Thus, doing the correct design when creating the column or table is amortized in spades.
4 – not so much about the database per se but indeed annoying. Not caring about the code quality of SQL. The fact that your SQL is expressed in text does not make it OK to hide the logic in heaps of string manipulation algorithms. It is perfectly possible to write SQL in text in a manner that is actually readable by your fellow programmer.
This has been said before, but: indexes, indexes, indexes . I've seen so many cases of poorly performing enterprise web apps that were fixed by simply doing a little profiling (to see which tables were being hit a lot), and then adding an index on those tables. This doesn't even require much in the way of SQL writing knowledge, and the payoff is huge.
Avoid data duplication like the plague. Some people advocate that a little duplication won't hurt, and will improve performance. Hey, I'm not saying that you have to torture your schema into Third Normal Form, until it's so abstract that not even the DBA's know what's going on. Just understand that whenever you duplicate a set of names, or zipcodes, or shipping codes, the copies WILL fall out of synch with each other eventually. It WILL happen. And then you'll be kicking yourself as you run the weekly maintenance script.
And lastly: use a clear, consistent, intuitive naming convention. In the same way that a well written piece of code should be readable, a good SQL schema or query should be readable and practically tell you what it's doing, even without comments. You'll thank yourself in six months, when you have to to maintenance on the tables. "SELECT account_number, billing_date FROM national_accounts"
is infinitely easier to work with than "SELECT ACCNTNBR, BILLDAT FROM NTNLACCTS".
Not executing a corresponding SELECT query before running the DELETE query (particularly on production databases)!
The most common mistake I've seen in twenty years: not planning ahead. Many developers will create a database, and tables, and then continually modify and expand the tables as they build out the applications. The end result is often a mess and inefficient and difficult to clean up or simplify later on.
a) Hardcoding query values in string
b) Putting the database query code in the "OnButtonPress" action in a Windows Forms application
I have seen both.
Not paying enough attention towards managing database connections in your application. Then you find out the application, the computer, the server, and the network is clogged.
-
Thinking that they are DBAs and data modelers/designers when they have no formal indoctrination of any kind in those areas.
-
Thinking that their project doesn't require a DBA because that stuff is all easy/trivial.
-
Failure to properly discern between work that should be done in the database, and work that should be done in the app.
-
Not validating backups, or not backing up.
-
Embedding raw SQL in their code.
Here is a link to video called ' Classic Database Development Mistakes and five ways to overcome them ' by Scott Walz
Not having an understanding of the databases concurrency model and how this affects development. It's easy to add indexes and tweak queries after the fact. However applications designed without proper consideration for hotspots, resource contention and correct operation (Assuming what you just read is still valid!) can require significant changes within the database and application tier to correct later.
Not understanding how a DBMS works under the hood.
You cannot properly drive a stick without understanding how a clutch works. And you cannot understand how to use a Database without understanding that you are really just writing to a file on your hard disk.
Specifically:
-
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? 大。 Now be honest with yourself: Did you understand even half of what you saw? No, you probably didn't. 修复这个。
-
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.
- Using an ORM to do bulk updates
- Selecting more data than needed. Again, typically done when using an ORM
- Firing sqls in a loop.
- Not having good test data and noticing performance degradation only on live data.