45种加速Firebird数据库的方法

在这里,您可以找到针对 Firebird 数据库在不同领域的性能优化建议列表——从硬件/操作系统和 Firebird 配置调优到 SQL 优化建议。此列表并非优化 Firebird 的完整参考,它假定您了解 Firebird 运作的基础知识,例如执行计划、事务管理和查询性能统计。
请谨慎应用这些建议,并在投入生产环境之前验证其效果。
我们公司(IBSurgeon)提供全面的数据库性能优化服务。
1. 将数据库放在 SSD 上
将数据库放在 SSD 上。SSD 驱动器提供比传统驱动器好得多的随机 IO。随机 IO 对于读取和写入分布在大数据库文件中的数据至关重要——大多数数据库操作需要密集的并行随机 IO。
2. 使用 RAID 10
如果您使用 RAID1 或 RAID5,请考虑 RAID10——它快 15-25%。
3. 检查 BBU
如果您使用 RAID 控制器,请检查是否安装并正常运行备用电池单元(BBU)——一些供应商默认不提供 BBU。没有 BBU,控制器会禁用缓存,RAID 工作非常慢,甚至比普通 SATA 驱动器还慢。通常,您可以在 RAID 配置工具中检查 BBU 状态。
4. 将写缓存设置为回写
如果您使用安装了 BBU 的 RAID 控制器(以及带 UPS 的服务器),请检查其缓存是否设置为回写(write-back)而非直写(write-through)。«回写»启用控制器的写缓存。
5. 启用读缓存
如果您使用 RAID 控制器,请检查是否启用了读缓存。
6. 检查磁盘子系统
检查您的驱动器是否有坏块和其他硬件问题(包括过热)。硬件问题会显著降低 IO 性能并导致数据库损坏。
7. 在 Firebird 2.5 中使用 SuperClassic 或 Classic
如果您使用 Firebird 2.5 SuperServer 且连接数很多,请尝试使用 SuperClassic 或 Classic,它们可以通过利用 CPU 的所有核心更好地扩展。
8. 在 Firebird 3 中使用 SuperServer 3.0
如果您在 2.5 中使用 Classic 或 SuperClassic,请考虑迁移到 Firebird 3.0 SuperServer,现在它可以使用多核并将其与共享缓存的优势相结合。
9. 增加页面缓冲区缓存
将页面缓冲区缓存的大小(参数 DefaultDBCachePages)从默认值增加。对于 2.5 SuperServer,我们推荐 10000 页,对于 3.0 SuperServer——50000 页,对于 Classic 和 SuperClassic——256 到 2048 页。但是,不要将页面缓冲区缓存值设置得太高——缓存同步有其成本,通过调整此值将所有数据库放入 RAM 的想法不会奏效。在此处使用预优化的 Firebird 配置文件:/zh/optimized-firebird-configuration/
10. 增加排序操作的内存大小
增加 firebird.conf 中 TempCacheLimit 参数的值——它指定用于排序的临时空间的缓存大小。默认值太低(Classic 为 8Mb,SuperServer 为 64Mb),Classic 至少使用 64Mb,SuperServer 和 SuperClassic 至少使用 1Gb。同样,使用 #9 中的优化配置文件。
11. 将强制写设置为关闭(谨慎使用!)
如果您有密集的插入或更新活动(您可以使用 HQbird MonLogger 检查,详情请参阅 HQbird 用户指南第 60 页),并且如果您安装了 UPS 和复制以防范硬件故障,请考虑将强制写(Forced Writes)设置设为 OFF,它可以将写操作速度提高多达 3 倍。
12. 增加 Classic/SuperClassic 的哈希槽数量
将 Classic 和 SuperClassic 的 LockHashSlots 参数值从默认的 1009 增加到某个较大的质数(例如 30011),它将减少内部锁定机制中的队列。
13. 为 Super Server 2.5 使用 CPU 亲和性
如果您使用 SuperServer 2.5,请将 CPUAffinity 参数设置为等于正在使用的数据库数量:2.5 中的 SuperServer 可以使用不同的 CPU 核心来处理特定数据库的请求。
14. 为临时空间使用快速驱动器
将 firebird.conf 中 TempDirectory 参数的第一部分设置为快速磁盘——SSD 或 RAM 驱动器。它将减少大型排序的时间——例如在数据库恢复时。
15. 将数据库备份存储在另一个驱动器上
将数据库备份存储在专用的物理驱动器(RAID)上。它将在备份期间分离读和写 IO,提高备份速度并减少主驱动器的负载。当用户在操作数据库时进行备份,这一点尤为重要。有关 Firebird 硬件配置的更多详细信息,请参阅"Firebird 硬件指南"。
16. 为批量插入停用索引
如果您插入或更新许多记录(超过表的 25%),请停用插入记录所在表的索引,并在插入或更新后重新激活它们。索引重建操作可能比多次更新索引更快。
17. 使用全局临时表进行快速插入
为了加速插入和更新,使用全局临时表(GTT)批量插入大型记录集,然后将记录传输到永久表中。将记录插入 GTT、预处理它们然后移动到持久表可能非常有效。
18. 避免不必要的索引
对于插入和更新密集的表,使用较少的索引。每个索引都会为插入、更新、删除和垃圾回收操作增加显著的开销——对于每个索引,当单条记录被插入/更新/删除/清理时,可能会有 3-4 次额外的页面读写。
19. 用内置函数调用替换 UDF
用内置函数调用替换 UDF 调用。Firebird 的近期版本中添加了许多内置函数,它们提供了以前仅在 UDF 库中可用的功能。尽可能替换此类函数,因为内置函数比 UDF 快多达 3 倍。
20. 对读操作使用只读事务
对不更改记录的操作(即 SELECT)使用隔离模式 = read committed 的只读事务。此类事务不会保留垃圾回收中的记录版本,并且可以无限期运行:它们不影响数据库性能。
21. 使用短写事务并消除所有长时间运行的事务
使用短的可写事务(用于 INSERT/UPDATE/DELETE 操作)。
可写事务越短越好。短事务从垃圾回收中保留的记录版本数量成比例地少于长时间运行的事务。不幸的是,即使是单个长时间运行的事务(例如来自保持打开的开发工具)也可能破坏所有其他短可写事务的良好效果。这就是为什么您需要监控长时间运行的事务并修复源代码中的相应位置。使用 HQbird DataGuard 工具接收有关 Firebird 数据库中最旧活动事务的警报(哪些应用程序启动了它、什么 IP 地址、其开始的时间戳),并使用 HQbird MonLogger 工具查看长时间运行的活动事务的完整列表及其 IO 统计信息。此外,如果您使用的数据库访问组件/库可以缓存记录集,请使用缓存更新。
22. 避免长记录链
避免一条记录有许多记录版本的情况——Firebird 处理长记录链时速度要慢得多。(要查看某些表有多少记录版本以及最长的记录链是什么,您可以使用 HQbird IBAnalyst 工具,表选项卡,按"最大版本"排序)。使用插入和定期删除旧记录的组合,而不是对同一记录进行多次更新。
23. 正确使用 PREPARE
对于仅参数发生变化的 SQL 查询,使用预处理语句来执行——例如,在此类查询的循环之前进行 prepare。Prepare 可能耗费大量时间(尤其是对大表而言),只准备一次查询将大幅提升整体性能。
24. 批量插入/更新操作期间不要过于频繁地 COMMIT
在批量 INSERT/UPDATE/DELETE 操作的情况下,不要在每次更改后提交事务(如果你在数据库驱动中使用了自动提交选项,就可能发生这种情况)——至少每 1000 次操作或更多次操作后再提交事务。每次事务提交都会对数据库执行若干读写 IO 操作,这就是频繁提交会降低数据库性能的原因。
25. 如果使用带大量常量的 IN,请“关闭”索引
如果你使用 WHERE fieldX IN (Constant1, Constant2,… ConstantN) 这样的构造,并且 fieldX 上有索引,Firebird 将按照 IN 列表中常量的数量使用索引相应次数。通过将 fieldX 转换为表达式 +0 来禁用索引搜索:WHERE fieldX+0 IN (Constant1, Constant2,… ConstantN),或者对于字符串,使用 fieldX||''
26. 用 JOIN 替换 IN
避免使用带有嵌套 WHERE IN(SELECT… WHERE IN (SELECT.. WHERE IN() )) 的查询,这可能会使 Firebird 优化器困惑。将嵌套的 IN 转换为连接。
27. 正确使用 LEFT JOIN
如果你使用 LEFT OUTER 连接,请显式地将表按从最小到最大的顺序放入连接中。
28. 限制 SELECT 查询的获取量
始终尝试用 FIRST… SKIP 或 ROWS 子句限制 SELECT 查询的大量输出。如果查询并非专门设计为报表(报表需要打印/导出所有记录),通常显示前 10-100 条记录就足够了。只获取必要的记录。
29. 在带 ORDER BY/GROUP BY 的 SELECT 中指定较少的列数
在带 ORDER BY/GROUP BY 的查询中,减少列的数量及其总宽度,无论是在 SELECT 部分(即要显示的字段)还是在 ORDER BY 子句中。Firebird 会合并 SELECT 和 ORDER BY/GROUP BY 子句中的列,并在内存中(或者,如果内存不足,则在磁盘上)对它们进行排序。因此,如果 SELECT 中有较长的 VARCHAR,排序文件的大小可能真的很大(许多 GB)。仅对必须排序的字段减少字段数量,并稍后连接要显示的大字段,可以大幅(x3-x10)提升带 ORDER BY/GROUP BY 的查询速度。
30. 使用派生表优化带 ORDER BY/GROUP BY 的 SELECT
优化带排序的 SQL 查询的另一种方法是使用派生表来避免不必要的排序操作。不要使用
SELECT FIELD_KEY, FIELD1, FIELD2, ... FIELD_NFROM TORDER BY FIELD2
而使用以下修改:
SELECT T.FIELD_KEY, T.FIELD1, T.FIELD2, ... T.FIELD_NFROM (SELECT FIELD_KEY FROM T ORDER BY FIELD2) T2JOIN T ON T.FIELD_KEY = T2.FIELD_KEY
31. 短字符串存储在 VARCHAR 中,长字符串存储在 BLOB 中
要存储短字符数据,请使用 VARCHAR;要存储长文本,请使用 BLOB。对于小块数据,Varchar 更快,因为它们存储在记录中,并且整条记录在同一次 IO 周期中被读取;如果记录大小小于数据库页面大小的 2/3,则整条记录存储在同一数据库页面上。BLOB 存储在记录之外,需要额外的 IO 轮次来读取,它们在读写长字符串时显示出优势。
32. 从大型 SELECT 中排除 BLOB 列
从大型 SELECT 中排除 BLOB 列。使用一种带子查询的延迟绑定方式来有选择地显示来自 BLOB 的信息(例如,显示文档的内容)。
33. 对主键和唯一键使用 BIGINT
对自动递增的主键和唯一键以及所有类型的标识符使用 BIGINT 类型。BIGINT 的运算速度最快,并且 BIGINT 有足够的容量存储几乎所有的数据范围。
34. 不要对键使用 VARCHAR
除非确实必要,不要对标识符使用 VARCHAR——对它们的操作远不如对整数列的操作高效。尤其要避免使用 GUID 作为标识符——由于 GUID 值的随机分布,使用 GUID 作为主键/唯一键的 INSERT/UPDATE 操作可能比使用整数慢 20 倍。
35. 重新计算索引统计信息
定期重新计算索引统计信息。对频繁或大量更改的表使用 SET STATISTICS 命令更新索引统计信息,这允许 Firebird 优化器选择更好的 SQL 计划。HQbird Firebird DataGuard 可以按照所需的时间表自动执行此类索引统计信息的重新计算(通常每周一次)。
36. 使用连接池
如果到 Firebird 数据库的数据库连接是短连接(这对网站来说是典型的),请使用连接池——例如,在 PHP 中使用函数 ibase_pconnect 而不是 ibase_connect
37. 在 Firebird 3.0 中使用 LINGER 选项
如果数据库连接是短连接并且你使用的是 Firebird 3+,请使用 LINGER 选项在指定时间内保持缓存活跃,即使没有其他连接,它也会将频繁使用的页面保留在缓存中。例如,ALTER DATABASE SET LINGER TO 60 将在最后一个连接结束后将缓存保留 60 秒。
38. 使用 HASH JOIN
在 Firebird 3.0 中,在连接大表和小表的情况下,HASH JOIN 可能比使用带索引的“嵌套循环”的普通连接快得多。要让 Firebird 优化器使用 HASH 连接,请在连接条件中使用 +0:T1 JOIN T2 ON T1.FIELD1+0 = T2.FIELD2+0。在将优化结果投入生产之前,请检查优化结果!
39. 将适当的 PSQL 函数标记为 DETERMINISTIC
将没有参数并返回常量值的 PSQL 函数(在 Firebird 3+ 中)用关键字 DETERMINISTIC 标记。确定性函数在当前查询的范围内被计算并缓存。
40. 在 Firebird 3.0 中使用分析(窗口)函数
如果你运行的 SELECT 同时输出某一列及其聚合函数,请使用窗口(分析)函数——它比子查询或 2 个查询更快。例如:
Select id, department, salary, salary / (select sum(salary) from employee) percentagefrom employee
替换为
Select id, department, salary, salary / sum(salary) OVER () percentage from employee
41. 对 gbak 使用 -se 开关
使用 -se 开关可将 gbak 备份和/或恢复速度提高多达 20%,例如
gbak -b -g -se service_mgr c:\db\data.fdb e:\backup\data.fbk
42. WHERE CURRENT OF
在 PSQL 中处理游标所获取记录的最快方式是子句 ‘where current of <>’。它比 ‘where rb$db_key = :v_db_key’ 更快,并且比使用主键或唯一键搜索快得多。
43. 避免频繁查询监控表
不要过于频繁地运行对 Firebird 监控表(MON$)的查询——此类查询消耗大量资源,并可能大幅降低主要业务逻辑的性能。我们建议运行 MON$ 查询的频率不超过每分钟一次。要持续监控 Firebird 查询/事务/连接,请使用支持 Trace API 的 HQbird PerfMon 工具(详情请参阅 HQbird User Guide 第 66 页)。
44. 对批量插入/更新使用 NO_AUTO_UNDO 选项
如果你在同一事务的范围内运行许多 DML(Update/Insert/Delete)命令,Firebird 会将每个命令的 undo-log 与事务的 undo-log 合并。要加速批量 DML 操作,请使用 «NO AUTO UNDO» 选项启动事务,以便不将每个命令的 undo-log 与事务的 undo-log 合并。
45. 如果不需要,不要在 Firebird 3 中使用 SRP 认证
如果你并不真正需要,请不要使用 SRP 用户认证(Firebird 3.0+)——使用 SRP 认证建立的连接比常规连接更慢。
代替总结
性能优化需要考虑多个因素,并且可能非常棘手。如果您已经尝试了上述所有方法,可以考虑聘请专业的数据库性能优化服务。
联系我们
您有任何问题吗?请随时通过电子邮件联系我们!