Почему большие БД работают не как хочется, или про несбыточные мечты SQL-запросов

    image


    В команде эксплуатации подумали, что наш опыт с Microsoft SQL в нагруженной среде больше нельзя скрывать, и потому родилась эта статья. В ней я расскажу о нюансах работы с этой СУБД из практики.


    Microsoft SQL Server давно нашел свое место в семействе продуктов Яндекс.Денег и успешно решает задачу сбора разрозненной информации обо всех операциях из множества отдельных сервисов. Без такой сборки воедино нельзя было бы отследить платеж, собрать статистику или решить проблему.


    Все сказанное далее будет полезно администраторам крупных БД – тем, кто заботится о быстрой и точной работе аналитики Microsoft SQL Server 2012-2014.


    Если информация окажется вам полезной и интересной — обязательно дайте знать в комментариях, чтобы автор не расслаблялся.


    Почему все это рассказываю именно я, или краткая характеристика автора.

    Меня зовут Слава, я руководитель группы по администрированию серверов Microsoft SQL в Яндекс.Деньгах.


    С этой СУБД работаю еще с версии 6.5, на MS SQL 7-2000 создавал систему мониторинга для 20-30 серверов; после выхода MS SQL 2005 и перехода от DMO к SMO полностью переписал мониторинг.


    В команде Яндекс.Денег — с конца 2012 года.


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


    Из этих данных ежесуточно строятся кубы с сотнями измерений, генерируются отчеты и реестры, происходят сверки. После чего «причесанные» данные отдаются пользователям и финансовым системам для дальнейшей работы. Для этих задач отлично подошел Microsoft SQL Server с его мощным Integration Services и процессом ETL (Extract — Transform — Load).


    Еще мы активно используем Analysis Services для построения OLAP кубов. Они предоставляют данные в многомерном виде с любым срезом, простым для аналитиков, финансистов, продакт менеджеров и руководства.


    Почему минутные запросы могут работать час


    Самая частая причина такого — устаревшие статистики (информация о состоянии колонок таблицы) и, как следствие, не оптимальные планы.


    image alt text


    Небольшая памятка о статистике, планах и том, как все это связано.

    При выполнении любого запроса к БД строится план выполнения из отсортированного списка выполняемых операций. Выбирая конкретную операцию, отдельный компонент «оптимизатор запросов» выделяет среди важных входных данных статистику, которая описывает распределение значений для столбцов таблицы БД.


    Такая оценка числа элементов позволяет оптимизатору запросов строить более-менее оптимальные планы выполнения. Поэтому устаревшая статистика портит все дело.


    Неправильная оценка может привести к избыточным дисковым I/O-операциям из-за нехватки объема выделенного для переброса в TempDB буфера памяти. Кроме того, СУБД может выбрать последовательный план выполнения вместо параллельного — и это лишь некоторые из возможных последствий.


    На Хабре уже есть статьи по автоматизации обновления статистики, поэтому подробно на этом моменте останавливаться не буду. Но обновление статистик может быть накладным по времени и нагрузке, а результат — нестабильным из-за того, что данные в таблице меняются быстрее обновления статистик. В таких случаях я просто глобально включаю флаг трассировки, который изменяет 20%-ный порог обновления статистики на динамически изменяющийся процент изменений таблицы:


    DBCC TRACEON (2371,-1)
    

    С включенным флагом статистики будут обновляться чаще с ростом количества строк.


    Это здорово помогает, но на таблицах с миллиардом строк и больше процесс может проходить крайне медленно, несмотря на нормальный план.


    В этом случае помогает включение асинхронного обновления статистик:


    ALTER DATABASE dbName SET AUTO_UPDATE_STATISTICS_ASYNC ON
    

    В ходе обновления статистики SQL продолжит работу, не дожидаясь завершения процесса. По нашим тестам, запросы к БД без включения этой опции очень нестабильны. Попробуйте провести такой эксперимент сами.


    Но даже с хорошими планами и актуальной статистикой могут возникать необъяснимые задержки в выполнении запросов. По моему опыту, самой частой причиной этого (после некорректных планов) были требовательные к памяти сервера запросы, которые находились в очереди на выполнение из-за формальной нехватки запрошенных ресурсов.


    При одновременном запуске пяти и более запросов, прожорливых до памяти, а также при наличии ограничения «в одни руки» по умолчанию в 25% — первым четырем отдается вся память. Остальным запросам остается только встать в очередь с индикатором RESOURCE_SEMAPHORE, ожидая освобождения памяти.


    Около половины доступной оперативной памяти сервера используется для выполнения запросов. В системах, где RAM более 128 ГБ, 25% имеют весьма существенное значение.


    По моим наблюдениям, от 20 до 80% выделенной памяти запрос просто не использует — это отлично видно, если во время работы запросов запустить команду:


    select * from sys.dm_exec_query_memory_grants
    

    После выполнения команды вы увидите обрабатывающиеся в настоящий момент запросы в базе данных с ожиданиями ресурсов, если они возникли в процессе работы.


    image alt text


    Результат отработки запроса на одной из наших тестовых БД.


    Запрос попросил и получил 9 ГБ, но использовал только 10%. Обратите внимание на поле ideal_memory_kb — в нем фиксируются несбыточные мечты запросов.


    Эффективно бороться с нерациональным расходом памяти при выполнении запросов можно с помощью Resource Governor. Он позволяет задать ограничения на загрузку ЦП и использование памяти.
    Еще рекомендую использовать хранимую процедуру sp_WhoIsActive. Она собирает информацию о состоянии сервера через динамические представления (DMV). Очень простой и мощный инструмент.

    Я использую процедуру sp_WhoIsActive с таким набором параметров:


    sp_whoisactive @not_filter = 'ReportServer',    @not_filter_type = 'database', @get_plans=1, @find_block_leaders = 1, @get_task_info = 2, @get_additional_info=1--, @get_locks = 1
    

    Идентифицировать такие запросы можно простым наблюдением за активными запросами в момент чрезмерной нагрузки. Также можно создать задачу, которая будет раз в 5-10 минут запускать запрос select * from sys.dm_exec_query_memory_grants, сохраняя в таблицу результаты. Итоговые значения можно проанализировать и найти проблемные запросы по разности между granted_memory_kb и max_used_memory_kb.


    Далее нужно идентифицировать соединения, берущие память сверх необходимого, и перенаправить их с помощью ResourceGovernor в группу, где выдача памяти происходит с запасом 10-20% вместо 80%.


    Значение для параметра REQUEST_MAX_MEMORY_GRANT_PERCENT (задается в процентах от доступной памяти) можно рассчитать с помощью следующего скрипта:


    SELECT 
    
        res.name, 
    
        sem.target_memory_kb /1024 as target_memory_Mb, 
    
        sem.available_memory_kb / 1024 as available_memory_MB, 
    
        sem.granted_memory_kb/1024 as granted_memory_Mb, 
    
        sem.used_memory_kb / 1024 as used_memory_Mb, 
    
        sem.grantee_count, sem.waiter_count 
    
    FROM sys.dm_exec_query_resource_semaphores sem 
    
    join sys.resource_governor_workload_groups res on sem.pool_id = res.pool_id where sem.resource_semaphore_id =0
    

    В результате можно определить, каким объемом памяти для запросов располагает сервер и каждая группа, которую вы добавите или настроите.


    После всех перечисленных выше манипуляций распределение памяти между запросами станет более эффективным, с минимумом ситуаций, когда запросы просто ждут выделения памяти и ничего не делают.


    AlwaysON в нагруженных базах данных


    О плюсах и минусах AlwaysON, среды высокой доступности для БД, не писал только ленивый. Однако, информации о практическом использовании этой технологии для терабайтных баз уже не так много. Когда в далеком 2013 мы внедряли группы высокой доступности в Яндекс.Деньгах, информации не было даже о реальной эксплуатации в боевых средах. Наша основная база данных на тот момент как раз занимала около 4 ТБ, поэтому до многих вещей приходилось доходить самостоятельно.


    Конфигурация типового узла кластера.

    Кластер высокой доступности состоял из двух узлов со следующими характеристиками: 192 ГБ памяти, по 2 «полки» с дисками SAS на сервер и отдельная полка для бэкапа с накопителями SATA, сеть из 4-гигабитных интерфейсов в TEAM.


    За четыре года объем этой базы вырос до 20 ТБ, поэтому серверы с трудом справлялись с новой нагрузкой, а оптимизация индексов вызывала дополнительные проблемы, о которых ниже. Производительность БД во время процессинга кубов по сети была такая, что трафик мешал другим компонентам в сети, пока кластер не пересадили на отдельный мощный коммутатор.


    В пиках доходило до того, что узлы теряли друг друга и БД на втором узле отключалась (привет кластерному кворуму). Но с этим разобрались добавлением новых сетевых карт; а в новых машинах просто сразу ставили 10 Гб карты.


    За вычетом перечисленных архитектурных проблем, общее впечатление от технологии AlwaysON положительное:


    • Трафик Между узлами сжимается до 80-90%.


    • Read-Only (RO) реплики быстро синхронизируются и возвращаются в строй после длительного отключения.


    • Для клонирования БД в тестовую среду можно использовать log shipping (доставку журналов транзакций) в ручном режиме.


    • AlwaysON очень надежен, а вот Windows Server Failover Clustering (WSFC) порой дает сбой и тянет за собой группы AlwaysON.

    Чтобы минимизировать количество необоснованных решений службы кластеризации погасить кластер в конфигурации с двумя узлами, мы выработали решение с принудительным лишением голоса вторичного узла:


    Import-Module FailoverClusters 
    
    $node = "Srv1" 
    (Get-ClusterNode $node).NodeWeight = 0 
    

    С такими параметрами становится спокойнее при любых работах на вторичных узлах.


    Рассинхронизации в группах AlwaysON


    При эксплуатации больших БД с распределением нагрузки в группах AlwaysON может возникать рассинхронизация главного и вторичного узлов. Если на узле RO запустить долгую операцию SELECT к таблице, которая в этот же момент обновляется на главном узле, произойдет блокировка восстановления цепочки журналов изменений (LSN). Рассинхронизация коснется всех таблиц и будет держаться столько, сколько работает запрос – в это время данные в базе на реплике станут неактуальными.


    Явных признаков рассинхронизации обычно нет, но на Read-Only узлах появляется новый процесс, запущенный от имени sa. Также часто возникают каскадные блокировки, которые можно быстро обнаружить с помощью описанной выше процедуры sp_whoisactive.


    Причина — в Select, запущенном с уровнем изоляции READ COMMITTED (то есть со значением по умолчанию).


    В качестве решения можно использовать параметр WITH (NOLOCK) в самом запросе или поменять уровень изоляции для базы данных либо сессии на SNAPSHOT для изоляции при долгих запросах выборки к таблицам.


    Чтобы узнавать о рассинхронизациях раньше пользователей, мы используем скрипт для мониторинга. Он раз в 5 минут проверяет статус синхронизации и отправляет e-mail в случае проблемы.


    Пример скрипта для мониторинга неизвестного автора из интернета
    declare @Delay int
    
    set @Delay = 2
    
          select * into #tmpag_availability_groups from master.sys.availability_groups
    
          select group_id, replica_id,replica_server_name,availability_mode into #tmpdbr_availability_replicas from master.sys.availability_replicas
    
          select replica_id,group_database_id,database_name,is_database_joined,is_failover_ready into #tmpdbr_database_replica_cluster_states from master.sys.dm_hadr_database_replica_cluster_states
    
          select * into #tmpdbr_database_replica_states from master.sys.dm_hadr_database_replica_states
    
          select replica_id,role,is_local into #tmpdbr_availability_replica_states from master.sys.dm_hadr_availability_replica_states
    
          select ars.role, drs.database_id, drs.replica_id, drs.last_commit_time into #tmpdbr_database_replica_states_primary_LCT from  #tmpdbr_database_replica_states as drs left join #tmpdbr_availability_replica_states ars on drs.replica_id = ars.replica_id where ars.role = 1
    
    SELECT   
    
    --'<tr><td align="center">' + AR.replica_server_name + '- ' + AG.name + ' ' + dbcs.database_name + '</td>' AS SRV_AG_DB,
    
    AR.replica_server_name + '- ' + AG.name + ' ' + dbcs.database_name  AS SRV_AG_DB,
    
    --CASE dbcs.is_failover_ready WHEN 1 THEN 0 ELSE ISNULL(DATEDIFF(ss, dbr.last_commit_time, dbrp.last_commit_time), 0) END  AS [EstimatedDataLoss],
    
    --'<td align="center">' + Cast(ISNULL(dbr.last_redone_time, 0) AS varchar(50)) + '</td>' As LastRedoneTime ,
    
    Cast(ISNULL(dbr.last_redone_time, 0) AS varchar(50))  As LastRedoneTime ,
    
    --'<td align="center">' + Cast((CASE dbcs.is_failover_ready WHEN 1 THEN 0 ELSE ISNULL(DATEDIFF(ss, dbr.last_commit_time, dbrp.last_commit_time), 0) END)as nvarchar(10)) + '</td>' AS [EstimatedDataLoss2],
    
    Cast((CASE dbcs.is_failover_ready WHEN 1 THEN 0 ELSE ISNULL(DATEDIFF(ss, dbr.last_commit_time, dbrp.last_commit_time), 0) END)as nvarchar(10))  AS [EstimatedDataLoss2],
    
    --'<td align="center">' + Cast(ROUND(ISNULL(CASE dbr.redo_rate WHEN 0 THEN -1 ELSE CAST(dbr.redo_queue_size AS float) / dbr.redo_rate END, -1),-1) as nvarchar(10)) + '</td> </tr>' AS [EstimatedRecoveryTime]
    
    Cast(ROUND(ISNULL(CASE dbr.redo_rate WHEN 0 THEN -1 ELSE CAST(dbr.redo_queue_size AS float) / dbr.redo_rate END, -1),-1) as nvarchar(10))  AS [EstimatedRecoveryTime]
    
    INTO #tt
    
    FROM
    
    #tmpag_availability_groups AS AG
    
    INNER JOIN #tmpdbr_availability_replicas AS AR ON AR.group_id=AG.group_id
    
    INNER JOIN #tmpdbr_database_replica_cluster_states AS dbcs ON dbcs.replica_id = AR.replica_id
    
    LEFT OUTER JOIN #tmpdbr_database_replica_states AS dbr ON dbcs.replica_id = dbr.replica_id AND dbcs.group_database_id = dbr.group_database_id
    
    LEFT OUTER JOIN #tmpdbr_database_replica_states_primary_LCT AS dbrp ON dbr.database_id = dbrp.database_id
    
    INNER JOIN #tmpdbr_availability_replica_states AS arstates ON arstates.replica_id = AR.replica_id
    
    WHERE
    
    --(AG.name='Nastro') and ISNULL(CASE dbr.redo_rate WHEN 0 THEN -1 ELSE CAST(dbr.redo_queue_size AS float) / dbr.redo_rate END, -1) > @Delay
    
    --(AG.name=@AGN) and 
    
    ISNULL(CASE dbr.redo_rate WHEN 0 THEN -1 ELSE CAST(dbr.redo_queue_size AS float) / dbr.redo_rate END, -1) > @Delay
    
    IF  EXISTS (SELECT * from #tt)
    
    BEGIN
    
    declare @tableHTML nvarchar(max)
    
    set @tableHTML =N'<H3><FONT SIZE="3" FACE="Tahoma">AlwaysOn Status </FONT></H3>' 
    
    set @tableHTML = @tableHTML +N'<table border="1" bgcolor=D7D1F8>' + -- change the background color if you want
    
               N'<FONT SIZE="2" FACE="Calibri">' +            
    
                N'<tr><th align="center">Server Group DB</th>' +
    
                N'<th align="center">LastRedoneTime</th>' +
    
                N'<th align="center">EstimatedDataLoss</th>' +
    
                N'<th align="center">EstimatedRecoveryTime</th>' +
    
                N'</tr>' +
    
               ISNULL(CAST ( ( 
    
    SELECT * from #tt
    
        FOR XML PATH('tr'), TYPE 
    
                ) AS NVARCHAR(MAX) ),'') +
    
                N'</FONT>' +
    
                N'</table>' ;
    
    --send email 
    
    EXEC msdb.dbo.sp_send_dbmail 
    
                @profile_name = 'mail', -- change here !!
    
                @recipients='Admin1@yandex.ru;Admin2@yandex.ru', -- change here !!
    
                @subject = 'AlwaysON Report',
    
                @body = @tableHTML,
    
                @body_format = 'HTML' ;
    
    END
    
          DROP TABLE #tmpdbr_availability_replicas
    
          DROP TABLE #tmpdbr_database_replica_cluster_states
    
          DROP TABLE #tmpdbr_database_replica_states
    
          DROP TABLE #tmpdbr_database_replica_states_primary_LCT
    
          DROP TABLE #tmpdbr_availability_replica_states
    
          drop table #tmpag_availability_groups
    
          SELECT * from #tt
    
          drop table #tt
    

    Еще одна частая причина рассинхронизации — работы по обслуживанию индексов. Например, у нас при перестройке кластерного индекса в таблице объемом 500 ГБ наблюдалась рассинхронизация на 10 часов.


    Самый простой способ минимизировать это время — использовать опцию MAXDOP при создании или перестройке индексов. Я обычно ставлю значение «2» для индексов от 10-20 ГБ, а для более мелких — «4».


    Маршрутизация Read-Only запросов


    Нюанс с запросами на чтение из Read-Only реплик баз данных в том, что мало прописать в строке соединения параметр ApplicationIntent=ReadOnly — потребуется еще настроить маршрутизацию. Конечно, эта настройка относится к тем, которые, настроив однажды, больше не трогаешь, но его величество Случай ломал и не такие схемы. Например, в процессе работы может потребоваться перенаправить запросы с RO реплики на главную.


    Для этого мы раньше использовали настройки самой реплики, просто запрещая запросы к RO. Но подход пришлось изменить после нескольких неприятных ситуаций из-за следующих особенностей переключения настройками реплики:


    • Переключение было жестким, терялись текущие запросы к RO, что не очень здорово.


    • При возврате трафика в RO часто возникала блокировка всех запросов с индикатором hadr_sync_commit для каждого из них. И вот это уже было совсем не весело, ведь, пока они висят, commit не происходит. Приходилось менять адреса подключений в клиентских приложениях, пока не нормализуется реплика.

    Позже выяснилось, что правильнее перенаправлять RO-запросы через смену маршрутизации.


    Вот пример для конфигурации с распределенной нагрузкой на 2 узла:


    ALTER AVAILABILITY GROUP [AGGroupName]
    
    MODIFY REPLICA ON N’PrimaryDB01′
    
    WITH (PRIMARY_ROLE
    
    (READ_ONLY_ROUTING_LIST=(‘SecondaryDB01‘,’PrimaryDB01‘))); -- Указываем, что RO запросы должны направляться сначала на узел SecondaryDB01
    
    Пример с маршрутизацией всего трафика на главный узел
    
    ALTER AVAILABILITY GROUP [AGGroupName]
    
    MODIFY REPLICA ON N’PrimaryDB01′
    
    WITH (PRIMARY_ROLE
    
    (READ_ONLY_ROUTING_LIST=(’PrimaryDB01‘))); -- В данном случае все запросы будут направляться на узел PrimaryDB01;
    

    В процессе смены маршрутизации запросы штатно завершаются, а следующие идут сразу на главную реплику.


    За 4 года работы системы однажды случился сбой на RO реплике. Упал один из разделов с ошибкой CRC в файле с данными. Главный узел продолжал работать, а вот база на RO реплике нет.


    Обработку отказа на уровне базы на RO узле и автоматическое переключение трафика на главный узел можно реализовать все той же маршрутизацией:


    1. создаем задание с запросом к любой таблице в БД;


    2. если запрос завершается с ошибкой, проверяем таким же способом БД на главном узле;


    3. Если на главном узле все хорошо, то переключаем на него трафик. В выходные или ночью это позволит хорошо выспаться и на свежую голову разобраться с проблемой.

    Что касается восстановления базы после подобного сбоя, то большую БД проще удалить из группы доступности (на главном узле она продолжит работу даже через Listener) и восстановить резервную копию на Read-Only узле в режиме no-recovery. Потом достаточно добавить восстановленную базу в группу доступности в режиме Join Only.


    Дополнение:
    Медленно в приложении, быстро в SSMS… — Спасибо за ссылку AlanDenton

    Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

    А вы нашли для себя что-то полезное в советах?

    Яндекс.Деньги
    213,00
    Как мы делаем Деньги
    Поделиться публикацией

    Комментарии 24

      +1

      Стоило бы отметить колоночные индексы. То за что я люблю Ms Sql 2014.


      В банках кстати покупают обычно Oracle и Oracle Bi, потому что АБС обычно на оракле и пишут. Мол можно бизнес логику перенести!
      В качестве Bi инструмента используем Tableua. ну очень хороший Bi инструмент, самый первый !


      1.Вопрос каким Bi инструментом пользуетесь?
      2.Используете ли Tabular модель хранения, говорят она не умеет масштабироваться?
      3.Используете ли партиции для более эффективного расчета ??
      4.Как Масштабируетесь?
      5.Расскажите еще че нить интересное :)


      Ms поглотил DATAZEN и внедрил наработки в SSRS 2016, там тоже много нового и интересного !

        0
        Для построения BI мы используем стек от Microsoft. SQL Server для БД, SSAS для кубов,
        отчеты и API — на SSRS или ASP, и т.д.

        Tabular сейчас в продакшене не используем.

        Партиции в кубах — используем, но с ними есть свои особенности при росте объемов.

        А про масштабирование — в двух словах не расскажешь, планируем в следующих статьях осветить эту тему более подробно.
          0
          QlikView как минимум не хуже Tableau, а ассоциативный анализ вообще крут. Впрочем, всех съест MS Power BI всё равно равно или поздно. :)
          0
          Почему минутные запросы могут работать час

          Я бы еще добавил ссылку на отличный пост от Дмитрия Пилюгина: Медленно в приложении, быстро в SSMS… про parameter sniffing и не только. А так пост годный, прочитал с удовольствием.
            0

            Отличная идея, спасибо, добавили

            0
            Приветствую! Поглядите, пожалуйста, мой вопрос хоть и не вам, но вы думаю в курсе :) Он вот тут. Т.к. автор слился, а прояснить хочется — поэтому вопрошаю к вам! Если конечно не затруднит!
              0
              Привет!

              Существенное изменение плана выполнения запроса как правило подразумевает,
              что запросы будут выполнятся медленее, так же могут увеличиться обращения к диску, потребление памяти, процессора.
              Полагаю, что при таких требованиях от производителя ПО, в БД уже созданы все необходимые статистики и реализован штатный механизм их обннолвения.
                0
                в БД уже созданы все необходимые статистики и реализован штатный механизм их обннолвения.

                Ого :) Я думал статистику всегда собирает и актуализирует сам MS SQL. Мне просто не до конца ясно, зачем штатный механизм (ы) отключаются, а все это, насколько я понял, реализуется скриптами в самом ПО? Т.е. зачем нужны эти лишние телодвижения, никак не пойму!
                  0
                  Так и есть, по умолчанию, статистики создаются и обновляются автоматически.
                  Но автоматику можно отключить, создать все вручную, использовать хинты (подсказки) в запросах и обеспечивать тем самым оптимальные планы не полагаясь на автоматику.
                  Которая не редко дает сбои, о чем пишут множество статей, включая эту: )
              0
              Вопрос а если таблица большая но статичная что будет если обновить статистику и отключить ее для данной таблицы?
                0
                Отключение авто-обновлений статистик производится на уровне БД.
                Если другие таблицы в БД обновляются, делать этого не стоит.
                В статичной таблице статистики обновляться не будут т.к. нет необходимости.

                Идея о разовом обновлении статистик конкретной статичной таблицы вполне здравая.
                Сделать это на таблице можно таким запросом — update statistics TableName with resample

                Есть так же неплохая команда EXEC sp_updatestats;
                Она анализирует необходимость обновления статистик и обновляет только те, для которых это необхходимо.
                Применяется ко всем таблицам и их статистикам внутри БД в которой она запущена.
                  0
                  Спасибо. Но как же вот это NORECOMPUTE или я что то не догоняю
                  UPDATE STATISTICS '+@tablename+' WITH FULLSCAN, NORECOMPUTE

                  Отключить параметр автоматического обновления статистики AUTO_UPDATE_STATISTICS для указанной статистики. Если указан этот параметр, оптимизатор запросов завершает текущее обновление статистики и отключает обновление в будущем.
                  Чтобы возобновить действие параметра AUTO_UPDATE_STATISTICS, снова выполните инструкцию UPDATE STATISTICS без параметра NORECOMPUTE или выполните процедуру sp_autostats.
                    0
                    Пардон, да, вы правы.
                    Такую штуку на практике никогда не использовал, для статичной таблицы профит вероятно будет. Особенно если там высокая OLTP нагрузка.
                      0
                      Можно ли еще пару вопросов
                      1 используете ли вы партитирование
                      2 и используете ли вы sql clr
                        0
                        Партиционирование используем в паре таблиц, там где требуется их чистить.

                        SQL CLR используем
                          0
                          тут с sqlclr есть момент неприятный в них можно делать только небольшие вычисления. Если вдруг в ней начинает использоваться (распределятся) много памяти — все считайте тормоза.
                0
                какой клиент используют аналитики для работы с OLAP-кубами? разрабатываете ли MDX-запросы, или их генерирует клиентское приложение?
                  +1
                  Привет!

                  В качестве клиента используется привычный всем MS Excel, он же и генерирует все MDX запросы к кубам.
                    0
                    А кроме Excel — к кубам ходят некоторые отчеты и API, вот там MDX разрабатываем.
                  0
                  Никогда бы не подумал, что в Яндексе используется MS SQL Server! :)
                    +2
                    Поверьте очень неплохая система! Особенно версия по linux! По производительности удобству разработки отладки даст фору ораклу. Был опыт в разработки системы с которой оракл не справился а sql проглотил за милую душу. Размерчик так себе в оракле за 20 ТБ далеко :(
                      0
                      Я-то знаю, что хорошая, много лет работаю. Просто Яндекс с его обычно open source ландшафтом и продуктами с MS SQL у меня как-то воообще не ассоциировался.

                      И как, Linux версия правда работает? В продуктив уже можно?
                        +1
                        Ну у нас полгода работает на критически важном сервисе
                        из минусов usafe clr не будут пока работать
                        Одни сплошные +
                        очень хочется ин мемори функуии и процедуры чтобы расширили сплтами дистинктом (count distict не пашет
                        И update from тоже пока не реализовали. а так все инмемори лежит в си-шных длл вот думаю будет время разберусь похоже там много чего можно наковырять интересного

                        Да и производительность процентов на 30 выше
                    0
                    del

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое