Поделиться через


SQL Server сообщает 701 "Недостаточно памяти для выполнения этого запроса" при выполнении больших пакетов

В статье рассматривается ошибка 701, которая может возникать при выполнении большого пакета операций в SQL Server. Сведения о других причинах ошибки 701 см. в MSSQLSERVER_701.

Исходная версия продукта: SQL Server
Исходный номер базы знаний: 2001221

Симптомы

В SQL Server при выполнении большого пакета удаленных вызовов процедур (RPC) (например, десятки тысяч вставок в одном пакете) операция может завершиться ошибкой со следующими ошибками в журнале ошибок SQL Server:

2020-07-04 13:30:45.78 spid56 Error: 701, Severity: 17, State: 193. 
2020-07-04 13:30:45.78 spid56 There is insufficient system memory to run this query.

Если вы проверяете выходные данные DBCC MEMORYSTATUS , которые автоматически регистрируются в журнал ошибок в сообщениях об ошибках 701, вы найдете такие записи, как следующие:

2020-07-04 13:30:45.74 spid56 Failed allocate pages: FAIL_PAGE_ALLOCATION 1 
2020-07-04 13:30:45.76 spid58
Memory Manager 
VM Reserved = 1657936 KB 
VM Committed = 66072 KB 
AWE Allocated = 2351104 KB ==> ~2.2 GB 
Reserved Memory = 1024 KB 
Reserved Memory In Use = 0 KB 

2020-07-04 13:30:45.76 spid56
USERSTORE_SXC (Total) 

VM Reserved = 0 KB 
VM Committed = 0 KB 
AWE Allocated = 0 KB 
SM Reserved = 0 KB 
SM Committed = 0 KB 
SinglePage Allocator = 1127848 KB==> ~1.07 GB 
MultiPage Allocator = 0 KB 

2020-07-04 13:30:45.78 spid56 Error: 701, Severity: 17, State: 193. 
2020-07-04 13:30:45.78 spid56 There is insufficient system memory to run this query. 

Примечание.

Обратите внимание на большие выделения для кэша USERSTORE_SXC.

Кроме того, при запросе sys.dm_os_memory_clerks динамического административного представления (DMV) во время выполнения single_pages_kb пакета столбец USERSTORE_SXC кэша показывает непрерывный рост за период, который создает ошибку 701.

Пример приложения, которое потенциально может привести к такому поведению, см . дополнительные сведения.

Причина

Объем памяти, выделенной для хранения запроса в SQL Server, зависит от:

  • Размер пакета (количество RPCs на запрос).
  • Число параметров.
  • Тип параметров.

Для определенных типов параметров (например, sql_variant), SQL Server может сохранять запросы в памяти потенциально неэффективным образом. Когда клиент отправляет большой пакет запросов, использующих эти типы параметров, несколько RPC можно отправлять в одном запросе. В этом сценарии сервер накапливает весь запрос в памяти перед выполнением. Это может привести к ошибке 701, рассмотренной в разделе "Симптомы".

Проблема гораздо более распространена в более ранних версиях SQL Server (особенно при использовании sql_variant типа данных). В SQL Server 2008 и более поздних версиях имеются некоторые улучшения проектирования, которые снижают объем памяти, используемой в определенных случаях, и более эффективны в целом.

Решение

Чтобы устранить ошибку, используйте один из следующих методов:

  • Уменьшение размера пакета.
  • Изменение типов параметров. Например, замените sql_variant другими типами.

Кэш USERSTORE_SXC используется для выделения уровней управления подключениями, таких как параметры RPC и память, связанная с подготовленными дескрипторами. Когда клиент отправляет запрос, содержащий большой пакет вызовов RPC, каждый из них потенциально использует большое количество определенных типов параметров, например sql_variant, может привести к чрезмерному выделению из этого кэша, тем самым исчерпая всю доступную память.

Приложение также следует отслеживать, чтобы убедиться, что вы закрываете подготовленные дескрипторы своевременно. Если эти дескрипторы не закрываются, sql Server не освобождает память для связанных объектов на стороне сервера.

Дополнительная информация

Чтобы воспроизвести проблему, описанную в этой статье, создайте приложение с помощью следующего кода и обратите внимание, что USERSTORE_SXC кэш растет и сжимается по мере выполнения программы.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;

namespace RPCBatching
{
    class Program
    {
        static void Main(string[] args)
        {
            DataTable t = new DataTable();
            t.Columns.Add("a", typeof(int));

            for(int i=0;i<100000;i++)
                t.Rows.Add(1);

            // pre-create the table with "CREATE TABLE t (a sql_Variant)" in a database named as test
            using (SqlConnection conn = new SqlConnection("server=tcp:localhost; integrated security=true; database=test"))
            {
               conn.Open();
                SqlDataAdapter da = new SqlDataAdapter(null, conn);
                da.InsertCommand = new SqlCommand("INSERT INTO t VALUES (@a)", conn);
                da.InsertCommand.Parameters.Add("@a", SqlDbType.Variant, 0, "a");
                da.InsertCommand.UpdatedRowSource = UpdateRowSource.None;
                da.UpdateBatchSize = 100000;
                da.InsertCommand.CommandTimeout = 12000;
                da.Update(t);
            }
        }
    }
}