【问题标题】:Why does SQLyog returns MySQL query results 10x faster than my Delphi program?为什么 SQLyog 返回 MySQL 查询结果的速度比我的 Delphi 程序快 10 倍?
【发布时间】:2009-06-23 22:28:32
【问题描述】:
select rectype,jobid,jobrecid,template,assignedto,entereddt,enteredby,ref1,processed,processeddt,
processbydt,title,description,connectlanhandle,finished,updateddt,ref2,cancelled,
requireaccept,acceptrejectstate,acceptrejectbydt,alert1dt,alert2dt,alert3dt,despatchallowed,
flag,ref3,projectid,duration,skillset,postcode,prefschedulefrom,prefscheduleto,customdata1,
customdata2,customdata3,hasnotes,displayjobtype,createdby,createddt,colour
 from jobs
 where updateddt >= '1982-02-05 17:25:38'
 or (processed = 'N' and
     cancelled = 'N')
 order by jobid, jobrecid

此查询返回约 80000 个结果。 SQLyog(一个 MySQL gui)可以在大约 600 毫秒内以可见网格的形式返回结果。我的 Delphi 程序使用 ODBC(最新的 MyODBC 驱动程序)进行连接,只需大约 6000 毫秒即可完成查询,甚至没有开始查看结果。

有什么想法可以让我的程序更快吗?

【问题讨论】:

    标签: mysql delphi optimization odbc


    【解决方案1】:

    我的猜测是 SQLyog 实际上并没有在 600 毫秒内显示所有 80,000 个结果 - 它可能仍在加载后面的结果,同时显示第一个结果。 (特别是,即使不涉及数据库,我见过的大多数 GUI 框架也无法快速填充 80,000 行。)

    您可以尝试做同样的事情,假设您使用的 API 允许您以流式方式获取结果(而不是在调用返回之前将所有内容都传输到内存中)。

    【讨论】:

    • 我同意 SQLYog 可能会使用“技巧”尽快刷新 GUI,只显示网格可见部分中的行,然后像“Ertugrul Tamer”一样更新网格卡拉”描述如下。
    【解决方案2】:

    我自己在基本表上尝试过(没有加入),发现即使您单击“显示全部”复选框,SQLyog 也不会立即将所有结果显示在网格上,通过将滚动按钮移动到您自己尝试最低区域,您会注意到 sqlyog 会放慢片刻,并带来更多结果显示。

    众所周知,ODBC 速度较慢,因为它为本地访问添加了更多层,因此请尝试使用直接访问 mysql 的MyDac from DevArt(即使没有 mysql 客户端库)。

    正如大多数人所说,永远不要尝试一次向用户显示 80,000 条记录。

    顺便说一句,来自 sun 的官方 MySql GUI tools 使用 Delphi 构建 ;-)

    【讨论】:

      【解决方案3】:

      我猜这是因为 SQLyog 使用本机 MySQL C API(直接连接),而您使用的是 ODBC 连接器。 您是否尝试过像 Devart 的 MyDAC 这样的 3rd 方连接器?您可以从那里获取免费试用版并使用它测试您的应用程序。

      FWIW 我已经使用 MyDac 多年了,我对(性能/奖品/支持)感到非常满意

      【讨论】:

        【解决方案4】:

        正如其他人所说,这可能是因为 SQLyog 没有加载所有记录,并且可能将其限制为 200 条左右。

        为了解决这个性能问题,您可以使用偷偷摸摸的技巧。由于您基本上是在启动时将数据集的缓存加载到您的应用程序中,因此您可以将其线程化。加载仍需要 6 或 8 秒,但您的应用程序仍会启动并且 UI 将可用。如果有人在加载之前执行了需要缓存的操作,您可以简单地显示沙漏光标,或显示“请稍等...”的消息,直到缓存准备好。

        在线程中进行数据访问时需要注意的一点是,您通常需要在线程中创建单独的数据库连接。像这样的:

        type
          TLoadCacheThread = class(TThread)
          private
            FConnection : TODBCConnection; // Or whatever, I don't use ODBC :-)
            FQuery : TODBCQuery;
            FMemData : TkbmMemTable; // This is what I use, YMMV
          protected
            procedure PopulateCachedDataset;
          public
            constructor Create; override;
            procedure Execute; override;
          end;
        
        constructor Create;
        begin
          inherited Create(True); // create suspended thread
          FConnection := TODBCConnection.Create(nil);
          // Set any properties for the connection here.
          FQuery := TODBCQuery.Create(nil);
          // Set any properties for the query here.
          FQuery.SQL.Text := 'select * from mytable';
          Resume;
        end;
        
        procedure Execute;
        begin
          FQuery.Open;
          FMemTable.LoadFromDataset(FQuery);
          Synchronize(PopulateCachedDataset);
        end;
        
        // The idea here is that you're loading into a mem dataset, which can then
        // quickly be copied to another memory dataset, rather than loading the
        // cached data directly from FQuery, which is slow and why we're threading
        // in the first place. This assumes you have some kind of globalsettings unit
        // or class, and it has a cacheddataset variable or property.
        procedure PopulateCachedDataset;
        begin
          GlobalSettings.CachedDataset.LoadFromDataset(FMemTable);
        end;
        

        无论如何,这是基本的想法。还有其他更复杂但在技术上更优越的方法,比如让 GlobalSettings.CachedDataset 按需加载数据,所以第一次访问它会很慢,但随后的时间会更长,等等。但是,它将取决于您的需求。

        HTH

        【讨论】:

        • 对这个问题的回复质量和速度都很棒。继续前进:)
        【解决方案5】:

        您可以在查询中使用 LIMIT 0,1000,然后在用户到达远端时更改它 - 通过检查 OnAfterScroll 事件。

        【讨论】:

        • 谢谢,但我正在填充整个程序中使用的内部对象数组,有时显示列表但通常不显示。我们只在程序启动时进行此查询。
        【解决方案6】:

        在我看来,您应该对您的应用程序和/或数据库架构三思而后行 - 在处理 80K 记录时,这并不好。试着缩小你的查询范围——如果你不让你的生活变得更简单——没有人会 $)

        【讨论】:

        • 我们会这样做,但工作量很大。如果能够通过更改某处的设置来节省几秒钟,那就太好了!
        【解决方案7】:

        SQLyog 可能不会一次加载所有 80000 行,至少我使用的一些数据库工具在滚动时会“按需加载”。如果您绝对需要一次获取所有记录,请考虑使用线程执行查询并填充内部数组。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-02-03
          • 1970-01-01
          • 2011-07-24
          • 1970-01-01
          • 2011-01-02
          • 2011-11-04
          • 2017-12-12
          • 2020-05-04
          相关资源
          最近更新 更多