【问题标题】:Is there an advantage to use bound variables running Oracle single record update/insert?使用运行 Oracle 单记录更新/插入的绑定变量是否有优势?
【发布时间】:2020-03-16 11:08:50
【问题描述】:

Oracle 管理员缠着我说,如果我使用绑定变量而不是内联变量,Oracle 可以优化更好的请求。代码是 C# 并使用 Devart Oracle 提供程序 Oracle12c+。我知道如果您运行选择 - 缓存的统计信息和下一个类似的命令会更好。但是单行插入/更新?我不相信。

任何人都可以给出明智的答案吗?

例子:

update x set a = 0 where id = 100

vs 

update x set a = :a where id = :id

编辑:未绑定的值只是数字,因此 SQL 注入超出了问题的范围

【问题讨论】:

  • 我很想从这篇文章中删除 c# 标记,看到问题的核心是关于 Oracle 的 - 生成和发送查询的语言无关紧要。想法?
  • 是的。您可以避免 SQL 注入攻击。这不仅仅是优化。优化好处与必须将查询编译到执行计划中的次数有关。有多少行受到影响或返回并不重要。如果您执行相同的更新 1000 次,避免 1000 次重新编译将大大提高性能。如果更新和您想象的一样便宜,编译将成为总成本的重要组成部分
  • 简而言之,Oracle 管理员是对的。你应该听他的,而不仅仅是这个查询。

标签: c# .net oracle oracle12c devart


【解决方案1】:

如果您通过用户输入的字符串连接来构建查询,那么您很容易受到 SQL 注入攻击。数据库管理员明智地不想让this call。它甚至不关心你做什么样的操作——SQL注入基本上可以运行随机的SQL代码。

现在,确切的替代方法因您的编程语言而异。在 C# 中,您使用 Parametized Queries。这些不仅是 SQL 注入证明,它还提供了一些类型检查,并且应该更快。就我个人而言,我什至发现它也更具可读性。不需要做那个迷宫般的引号。

但是,正如您提到的绑定变量,您可能会改用 Prepared Statements。老实说:我很少使用它们。它们看起来比它的价值要复杂得多。但是,如果这是您的库和语言支持的唯一非串联方式,那就是您必须采用的方式。

请注意,您仍然可以使用串联。也就是说,如果字符串不是从磁盘、网络或用户输入中读取的。我最近有一个案例,根据用户输入,必须附加一个 IN 子句。根据输入,它是不同的(值和长度)。而且我使用的是准备好的语句,它并没有很好地处理它。然而,由于要附加的字符串是在 switch/case 语句中 100% 从头开始​​挑选/生成的(这是给用户输入的),所以没有危险。充其量随机用户输入可能会达到默认情况。

【讨论】:

  • @BoppityBop 您的编辑没有提供任何新内容。您的示例仍然是通过字符串连接与参数化查询/准备语句构建查询之一。
  • 对不起 - 我没有按“发布”看看现在.. sql 注入不是这里的问题。
  • @BoppityBop 如果变量是数字,则更有理由使用绑定变量/参数化查询。如果您尝试将它们连接成字符串,它们首先会变成字符串。然后由 SQL Server 解析回来。您正在添加相当于 ToString()/Parse() 的负载,同时还增加了发送到服务器的数据总量(字符串很快变得比整数值大)。
【解决方案2】:

通过简单的UPDATEINSERT 语句使用绑定变量有巨大的性能优势。

首先,不必解析这么多独特的语句可以直接提高性能。一个小的UPDATE 起初可能看起来很简单,但幕后发生了很多事情。每个唯一的语句都必须被解析——Oracle 必须检查每个语句的语法和安全性。使用绑定变量,这项工作只发生一次。下面的简单测试显示 UPDATE 使用绑定变量的运行速度提高了 10 倍以上。

-- Create simple table with one record.
create table x(id number, a number);
insert into x values(100, 0);
commit;


-- 10,000 concatenated UPDATES - 5 seconds.
begin
    for i in 1 .. 10000 loop
        execute immediate 'update x set a = '||i||' where id = 100';
    end loop;
end;
/


-- 10,000 bind variable UPDATES - 0.3 seconds.
-- (Execute immediate is used here keep this test similar to above.)
begin
    for i in 1 .. 10000 loop
        execute immediate 'update x set a = :i where id = 100' using i;
    end loop;
end;
/

此外,使用绑定变量可以让您使用批处理并将性能再提高 10 倍。

使用绑定变量意味着只有一个 SQL_ID,这使得 DBA 的性能调整和跟踪更加容易。使用单个 SQL_ID,有许多计划管理选项可用,以防执行计划错误。

最后,创建大量唯一语句将填满共享池(用于包含执行计划信息的内存结构)。大量语句可能会将其他语句推出池中,从而间接导致其他性能问题。

【讨论】:

  • shared pool 是什么?是 Oracle 的特性吗?
  • @BoppityBop 是的,共享池是 Oracle 特有的功能。但是每个数据库都可能有一个最近使用的 SQL 语句的缓存。
【解决方案3】:

明确的答案促使我做一个基准测试。它模拟了准确的 prod 服务器条件。

30000 次迭代的结果(差异为 6.5%):

Inline vars: 00:01:09.7444764
Bound  vars: 00:01:05.4454827
static void Main(string[] args)
{
    var LEN = 30000;
    var cmd = new OracleCommand();
    var sql = "update some_table set progress={0} where id=100";
    var rnd = new Random((int)DateTime.Now.Ticks);

    //

    var sw = new Stopwatch();
    sw.Start();

    for (int i = 0; i < LEN; i++)
    {
        using(var cnn = new OracleConnection("xxx"))
        {
            sql = string.Format(sql, rnd.Next());
            cmd.CommandText = sql;
            cmd.Connection = cnn;

            cnn.Open();
            cmd.ExecuteNonQuery();
        }
    }

    sw.Stop();

    Console.WriteLine("Inline vars: {0}", sw.Elapsed);

    //

    sw.Restart();

    sql = "update tm_fnet set progress=:p where id=:i";

    for (int i = 0; i < LEN; i++)
    {
        using (var cnn = new OracleConnection("xxx"))
        {
            cmd.CommandText = sql;
            cmd.Connection = cnn;
                        cmd.Parameters.Clear();
            cmd.Parameters.AddWithValue("i", 100);
            cmd.Parameters.AddWithValue("p", rnd.Next());

            cnn.Open();
            cmd.ExecuteNonQuery();
        }
    }

    sw.Stop();

    Console.WriteLine("Bound vars: {0}", sw.Elapsed);

    //

    Console.ReadKey(false);
}

结论 - 除非您必须注意 SQL 注入(但这与我的情况无关),否则它并不重要。

【讨论】:

  • 我认为您在这里测量的东西不正确。这里可能只有一秒钟的真实数据库工作 - 为什么您的应用程序需要一分钟? (应用程序是否为每一行创建一个新连接,并一次一行地发送数据?)
  • 另外,这看起来像一个串行测试。我想你的应用程序会让用户同时访问数据库。这就是有很大收获的地方。这是我的小组不久前制作的关于这个主题的视频...youtube.com/watch?v=EBdZ-RE2HFs
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-16
  • 1970-01-01
  • 2012-08-08
相关资源
最近更新 更多