【问题标题】:Fetching ElasticSearch Results into SQL Server by calling Web Service using SQL CLR通过使用 SQL CLR 调用 Web 服务将 ElasticSearch 结果提取到 SQL Server
【发布时间】:2020-01-09 21:01:36
【问题描述】:

由于性能问题导致的代码迁移:-

  1. SQL Server LIKE 条件(之前)
  2. SQL Server 全文搜索 --> 包含(之前)
  3. 弹性搜索(当前)

目前为止:-

我们在 ASP.Net Core 中创建了一个网页,该网页自动完成下拉列表,包含在 Elastic Search https://www.99corporates.com/ 中索引的 2.5+ 百万家公司

由于性能问题,我们已成功地将代码从 SQL Server 全文搜索转移到 Elastic Search,并在我们的 .Net 代码中使用了 NEST v7.2.1 和 Elasticsearch.Net v7.2.1。

仍在寻找解决方案:-

如果用户没有从自动完成列表中选择一家公司,而只是输入了几个字符并单击“开始”,那么应该会显示一个列表,我们之前使用 SQL Server 全文搜索完成了该列表 --> CONTAINS

  1. 我们可以调用我们使用 SQL CLR 和代码如 SELECT * FROM dbo.Table WHERE Name IN( dbo.SQLWebRequest('') ) 创建的 ASP.Net Web 服务

    [System.Web.Script.Services.ScriptMethod()]
    [System.Web.Services.WebMethod]
    public static List<string> SearchCompany(string prefixText, int count)
    {
    }
    
  2. 任何更好或替代的选择

【问题讨论】:

    标签: sql-server elasticsearch nest sqlclr elasticsearch-plugin


    【解决方案1】:

    完成。得到了解决方案。

    使用 SQL CLR https://github.com/geral2/SQL-APIConsumer

    exec  [dbo].[APICaller_POST]
    @URL = 'https://www.-----/SearchCompany'
    ,@JsonBody = '{"searchText":"GOOG","count":10}'
    

    如果有任何其他/更好的选择可以实现这一点,请告诉我。

    【讨论】:

      【解决方案2】:

      虽然该解决方案(即 SQL-APIConsumer SQLCLR 项目)“有效”,但它不可扩展。它还需要将数据库设置为TRUSTWORTHY ON(存在安全风险),并将一些程序集加载为UNSAFE,例如Json.NET,如果其中任何一个使用静态变量进行缓存,这是有风险的,期望每个调用者都是隔离/拥有自己的应用程序域,因为 SQLCLR 是一个单一的、共享的应用程序域,因此静态变量在所有调用者之间共享,并且多个并发线程可能导致竞争条件(这并不是说这肯定会发生因为我还没有看到代码,但是如果您没有查看代码或使用多个并发线程进行测试以确保它不会造成问题,那么在稳定性和确保可预测性方面绝对是一场赌博,预期的行为)。

      考虑到我确实出售了一个 SQLCLR 库SQL#,在某种程度上我有偏见,其中完整版包含一个也可以执行此操作的存储过程,但是 a) 通过签名正确处理安全性(它确实 启用TRUSTWORTHY),b) 允许处理可伸缩性,c) 不需要任何UNSAFE 程序集,并且d) 处理更多场景(更好的标头处理等)。它不处理任何 JSON,它只返回 Web 服务响应,如果您愿意,您可以使用 OPENJSON 或其他方式解压。 (是的,SQL# 有一个免费版本,但它不包含 INET_GetWebPages)。

      但是,我认为 SQLCLR 首先不适合这种情况。在这个项目的前两个版本中(使用LIKE 然后CONTAINS)将用户输入直接发送到查询中是有意义的。但是现在您正在使用 Web 服务从该用户输入中获取匹配值的列表,您不再局限于这种方法。您可以而且应该在应用层单独处理 Web 服务/弹性搜索部分。

      您应该执行以下操作,而不是将用户输入传递到查询中,而只是让查询暂停以获取 0 个或更多匹配值的列表:

      1. 在执行任何查询之前,直接在应用层获取匹配值列表。
      2. 如果没有返回匹配值,您可以完全跳过数据库调用,因为您已经有了答案,并立即响应用户(没有匹配返回时响应时间快得多)
      3. 如果有匹配,则执行搜索存储过程,通过表值参数 (TVP) 按原样发送匹配列表,该参数成为存储过程中的表变量。使用该表变量对表进行 INNER JOIN,而不是执行 IN 列表,因为 IN 列表不能很好地扩展。此外,请务必使用IEnumerable&lt;SqlDataRecord&gt; 方法将 TVP 值发送到 SQL Server,不是DataTable 方法,因为这只会浪费 CPU/时间和内存。

        有关如何正确完成此操作的示例代码,请参阅我对Pass Dictionary to Stored Procedure T-SQL的回答

      在 C# 风格的伪代码中,大致如下:

      List<string> = companies;
      
      companies = SearchCompany(PrefixText, Count);
      
      if (companies.Length == 0)
      {
         Response.Write("Nope");
      }
      else
      {
         using(SqlConnection db = new SqlConnection(connectionString))
         {
           using(SqlCommand batch = db.CreateCommand())
           {
              batch.CommandType = CommandType.StoredProcedure;
              batch.CommandText = "ProcName";
      
              SqlParameter tvp = new SqlParameter("ParamName", SqlDbType.Structured);
              tvp.Value = MethodThatYieldReturnsList(companies);
              batch.Paramaters.Add(tvp);
      
              db.Open();
              using(SqlDataReader results = db.ExecuteReader())
              {
                 if (results.HasRows)
                 {
                    // deal with results
                    Response.Write(results....);
                 }
              }
           }
         }
      }
      

      【讨论】:

      • 感谢所罗门,首先感谢您花费宝贵的时间回答我的问题并给出了真正有意义的详细解释。我将尝试步骤 1、2 和 3。我将在实施相同的步骤后发布我的观察结果。
      • @ShirishPatel 不客气。我刚刚更新以添加一些伪代码,以帮助确保您理解我的意思。我还添加了一个指向更具体的示例代码的链接,其中也应该包括 T-SQL 端。
      猜你喜欢
      • 1970-01-01
      • 2015-12-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多