Як працюють у блокуванні читання на SQL Server?


17

Припустимо, у мене є такий тривалий запит

UPDATE [Table1]
SET [Col1] = 'some value'
WHERE [Col2] -- some clause which selects thousands of rows

і припустимо, що наступний запит виконується під час виконання вищезазначеного запиту

SELECT *
FROM [Table1]

Чи заважає перший запит запускати другий запит, поки перший запит не буде виконано? Якщо так, чи запобігає перший запит, щоб другий запит був запущений у всіх рядках або лише у рядках, що містяться в пункті WHERE?

Редагувати:

Припустимо, другий запит є

SELECT [Col1], [Col2]
FROM [Table1]
WHERE [Col2] -- some clause whose matching elements overlap those from
             -- the clause in the first query and which has additional matching elements

Відповіді:


14

Рекомендую прочитати Розуміння того, як SQL Server виконує запит , у ньому є пояснення того, як працює читання і запис, і як працює блокування.

Перегляд 10000 футів виглядає наступним чином:

  • перед читанням даних оператори читання отримують спільні блокування даних, які вони читають
  • Оператори запису набувають ексклюзивні блокування даних, які вони змінюють, перш ніж змінювати дані
  • блокування даних - це лише рядки, наприклад. хеш ключа, який зчитується, визначається базою даних та об'єктом.
  • менеджер блокування підтримує список усіх наданих блокувань та виявляє несумісність відповідно до матриці сумісності блокування
  • несумісні запити призупиняються до тих пір, поки не буде звільнено несумісний грант, який їх блокує
  • оператори використовують ієрархію блокування, щоб оголосити про намір читати або оновлювати дані на більш високому рівні (рівень сторінки або таблиці, ігноруючи параметри рівня розділів). Це дозволяє операторам блокувати цілі таблиці без блокування кожного окремого рядка
  • термін експлуатації та фіксація діапазону використовуються для забезпечення більш високого рівня ізоляції

Це справді лише верхівка крижаного бергу. Тема величезна. У вашому прикладі ніхто не може відповісти на ваше запитання про те, що насправді заблоковано, оскільки це буде залежати від багатьох факторів. Звичайно, жодна програма не повинна видавати це, SELECT * FROM Table1 оскільки в ньому відсутнє положення WHERE і використовується *. Це погані практики, оскільки, крім усього іншого, вони призведуть саме до фіксації суперечок.

Якщо ви стикаєтеся з блоками читання проти запису, вам потрібно вивчити версію версій та ізоляцію знімків. Прочитайте розуміння рівнів ізоляції на основі версій .


Що робити, якщо мені потрібен весь вміст таблиці (скажімо, у мене всього 14 рядків)? Як це погана практика, SELECT * FROM Table1якщо це саме те, що мені потрібно?
Азимут

1
*сама по собі є поганою практикою, тому що при зміні структури таблиці програма зазвичай розбивається (у результаті з’являються несподівані стовпці).
Ремус Русану

3

Редагувати: як зазначає @MaxVernon , наступне - це жодним чином не пропонувати NOLOCK , і я дуже добре повинен був би згадати про встановлення рівня транзакції, READ UNCOMMITEDі щоб негативна конотація стояла там, а не NOLOCKпіднімалася на першому місці. Отже, як було розміщено спочатку:

Швидкий і простий - "Так, перший запит заблокує другий запит, якщо не вказано конкретний підказку індексу ( NOLOCK , іноді називається" брудним зчитуванням ") або встановлено рівень ізоляції транзакцій другого запиту READ UNCOMMITED(який працює однаково), ні, це не є."

У відповідь на додаткову деталь, надану у питанні, що передбачає включення а WITH пункту на другий SELECT, будучи взаємовиключним або іншим чином, взаємодії між двома запитами будуть в основному однаковими.

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'Foo'
                    AND type = 'U' )
BEGIN
    --DROP TABLE dbo.Foo;
    CREATE TABLE dbo.Foo
    (
        Foo_PK          BIGINT IDENTITY( 1, 1 ) NOT NULL,
                            PRIMARY KEY ( Foo_PK ),
        Bar             BIT,
        x               BIT,
        y               BIT,
        z               BIT
    );

    CREATE NONCLUSTERED INDEX IX_Foo_x
        ON  dbo.Foo ( x );

    INSERT INTO dbo.Foo ( Bar, x, y, z )
    VALUES ( 1, 1, 1, 1 ), ( 0, 0, 0, 0 );
END;    
GO

BEGIN TRANSACTION;

UPDATE  dbo.Foo
    SET y = 0
WHERE   x = 1;

-- COMMIT TRANSACTION;

В окремому сеансі виконайте наступне:

SELECT  *
FROM    dbo.Foo WITH ( NOLOCK );
GO

SELECT  *
FROM    dbo.Foo;

Ви можете перевірити блокування, які зараз утримуються, запустивши sp_lock , бажано, в інший окремий сеанс:

EXECUTE dbo.sp_lock;

Ви повинні бачити KEYблокування типу, яке тримає павук, який виконує транзакцію з вставкою в X(ексклюзивному) режимі, і не плутати його з іншими IXблоками (ексклюзивні намір). Документація щодо блокування вказує на те, що в той час, коли KEYзамок є специфічним для діапазону, він також запобігає іншим транзакціям вставляти або оновлювати порушені стовпці, змінюючи дані, що містяться в ньому, щоб він міг потрапляти в цей діапазон вихідного запиту. Оскільки замок, який утримується, є ексклюзивним, перший запит запобігає доступу до ресурсу з будь-якої іншої одночасної транзакції. Насправді всі рядки стовпця заблоковані, незалежно від того, потрапляють вони чи ні в діапазон, визначений першим запитом.

Таким чином, Sблокування, проведене на другому сеансі, триватиме WAITдо моментуX блокування не очиститься, не запобігаючи прийому іншого X(або U) блокування на цьому ресурсі від іншого одночасного шпіона до того, як другий сеанс завершить свою операцію читання, виправдовуючи існування Sблокування.

Тепер редагування для ясності: Якщо я не помиляюся з тим, що брудне прочитання - це короткий опис згаданих тут ризиків ... Edit 3 : Я щойно зрозумів, що не розглядаю ефект фонової контрольної точки, яка пише як ще не підтвердженої транзакції на диску, так що так, моє пояснення було вводити в оману.

У другому запиті перша партія може (і в цьому випадку буде) повертати незапущені дані. Друга партія, що працює на рівні ізоляції транзакцій за замовчуванням READ COMMITED, повернеться лише після того, як фіксація або відкат буде виконано в першому сеансі.

Звідси ви можете переглянути свої плани запитів та пов'язані з ними рівні блокування, але ще краще, про все про блокування на SQL Server ви можете прочитати тут .


1
Слово попередження про використання WITH (NOLOCK)було б корисним у цьому випадку. Дивіться brentozar.com/archive/2011/11/… та brentozar.com/archive/2013/02/… для більшого читання.
Макс Вернон

3
О, WITH (NOLOCK)підказка не повертає брудні сторінки з пам'яті, які не були вчинені. Він фактично читає рядки з таблиці (будь то на диску чи в кешеній пам'яті), не перешкоджаючи авторам оновлювати чи додавати рядки до сторінок, які використовуються в таблиці.
Макс Вернон

2
Я збентежений. Якщо відповідь на "чи заважає 1-й запит запускати другий?" є "Ні", як відповідь на друге питання може бути "Так"? Чи можете ви уточнити, на які питання ви відповідаєте, та розширити відповіді?
Йон усіх торгів

Редагує чудово, вибачте, люди! Дайте мені знати, чи є ще щось незрозуміле!
Avarkx
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.