Sql сервер тупик при вставке и обновлении в одной транзакции - PullRequest
0 голосов
/ 18 февраля 2019

Я использую Azure SQL + Java + Spring Boot 2. В настоящее время я пытаюсь понять причину возникновения взаимоблокировок.В транзакции я делаю вставку и последующее обновление для той же таблицы, но разных строк.Насколько я понял, SQL Server по умолчанию использует блокировку строки и изоляцию read_commited.

Вот подробности взаимоблокировки:

<deadlock>
    <victim-list>
        <victimProcess id="process2b8618644e8"/>
    </victim-list>
    <process-list>
        <process id="process2b8618644e8" taskpriority="0" logused="352"
                 waitresource="KEY: 16:72057594043760640 (5f601e0257f1)" waittime="1321" ownerId="9246067"
                 transactionname="implicit_transaction" lasttranstarted="2019-02-17T13:03:49.683" XDES="0x2b868778458"
                 lockMode="U" schedulerid="2" kpid="288932" status="suspended" spid="118" sbid="0" ecid="0" priority="0"
                 trancount="2" lastbatchstarted="2019-02-17T13:03:49.793" lastbatchcompleted="2019-02-17T13:03:49.760"
                 lastattention="1900-01-01T00:00:00.760" clientapp="Microsoft JDBC Driver for SQL Server"
                 hostname="TZ-MacBook-Pro.local" hostpid="0" loginname="master" isolationlevel="read committed (2)"
                 xactid="9246067" currentdb="16" currentdbname="test" lockTimeout="4294967295" clientoption1="671088672"
                 clientoption2="128058">
            <executionStack>
                <frame procname="unknown" queryhash="0x431f2517c2d3feb8" queryplanhash="0x3a793ad664472011" line="1"
                       stmtstart="110" stmtend="238"
                       sqlhandle="0x02000000060c530e608cef3b3e4e4712f48c40d2efd9b04d0000000000000000000000000000000000000000">
                    unknown
                </frame>
                <frame procname="unknown" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="1"
                       sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
                    unknown
                </frame>
            </executionStack>
            <inputbuf>
                (@P0 int,@P1 nvarchar(4000),@P2 nvarchar(4000),@P3 int)update bug set lock=@P0, status=@P1 where
                bug_id=@P2 and lock=@P3
            </inputbuf>
        </process>
        <process id="process2b84f702108" taskpriority="0" logused="700"
                 waitresource="KEY: 16:72057594043760640 (185e9bfc7320)" waittime="1287" ownerId="9246090"
                 transactionname="implicit_transaction" lasttranstarted="2019-02-17T13:03:49.727" XDES="0x2b868764458"
                 lockMode="U" schedulerid="1" kpid="334812" status="suspended" spid="115" sbid="0" ecid="0" priority="0"
                 trancount="2" lastbatchstarted="2019-02-17T13:03:49.830" lastbatchcompleted="2019-02-17T13:03:49.790"
                 lastattention="1900-01-01T00:00:00.790" clientapp="Microsoft JDBC Driver for SQL Server"
                 hostname="TZ-MacBook-Pro.local" hostpid="0" loginname="master" isolationlevel="read committed (2)"
                 xactid="9246090" currentdb="16" currentdbname="test" lockTimeout="4294967295" clientoption1="671088672"
                 clientoption2="128058">
            <executionStack>
                <frame procname="unknown" queryhash="0x431f2517c2d3feb8" queryplanhash="0x3a793ad664472011" line="1"
                       stmtstart="110" stmtend="238"
                       sqlhandle="0x02000000060c530e608cef3b3e4e4712f48c40d2efd9b04d0000000000000000000000000000000000000000">
                    unknown
                </frame>
                <frame procname="unknown" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="1"
                       sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
                    unknown
                </frame>
            </executionStack>
            <inputbuf>
                (@P0 int,@P1 nvarchar(4000),@P2 nvarchar(4000),@P3 int)update bug set lock=@P0, status=@P1 where
                bug_id=@P2 and lock=@P3
            </inputbuf>
        </process>
    </process-list>
    <resource-list>
        <keylock hobtid="72057594043760640" dbid="16" objectname="b51ce3c4-02f0-4dfa-89dd-3ab2f45beac7.dbo.bug"
                 indexname="bug_pk" id="lock2b8658c6f00" mode="X" associatedObjectId="72057594043760640">
            <owner-list>
                <owner id="process2b84f702108" mode="X"/>
            </owner-list>
            <waiter-list>
                <waiter id="process2b8618644e8" mode="U" requestType="wait"/>
            </waiter-list>
        </keylock>
        <keylock hobtid="72057594043760640" dbid="16" objectname="b51ce3c4-02f0-4dfa-89dd-3ab2f45beac7.dbo.bug"
                 indexname="bug_pk" id="lock2b856aa7f80" mode="X" associatedObjectId="72057594043760640">
            <owner-list>
                <owner id="process2b8618644e8" mode="X"/>
            </owner-list>
            <waiter-list>
                <waiter id="process2b84f702108" mode="U" requestType="wait"/>
            </waiter-list>
        </keylock>
    </resource-list>
</deadlock>

Также я использую кластерный индекс для моего столбца idи нет никаких проблем со случайным порядком операций.Более того, когда я только вставляю внутри транзакции все работает нормально, без тупика. Если я переключаюсь на PostgreSql, проблем с блокировкой не возникает.

Так почему я получаю тупик в Azure SQL?

Вот исходный код Java:

    @Transactional
    public void bug() {
        BugEntity bugEntity = bugRepo.findById("1234")
                .orElseGet(() -> createBugEntity());

        if (bugEntity.getStatus().equals("SomeStatus")) {
            bugEntity.setStatus("NewStatus");
            bugRepo.save(bugEntity);
        }

    }

    private BugEntity createBugEntity() {
        BugEntity bugEntity = BugEntity.builder()
                .bugId(UUID.randomUUID().toString())
                .status("SomeStatus")
                .data("SomeData")
                .build();
        return bugRepo.save(bugEntity);
    }

Класс сущности:

@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
@Entity(name = "bug")
@DynamicUpdate
public class BugEntity {

    @Id
    String bugId;
    String status;
    String data;

    LocalDateTime dateTime;

    @Version
    int lock;


}

Для воспроизведения тупика я использую симуляцию гатлинга с несколькими параллельными запросами.

Заранее спасибо!

1 Ответ

0 голосов
/ 19 февраля 2019

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

Трассировка тупика показывает, что параметры передаются в виде nvarchar(4000) типов данных.Это может помешать эффективному использованию индексов для столбцов varchar, поскольку nvarchar имеет более высокий приоритет типов данных, чем varchar.Эта проблема особенно распространена, когда типы данных параметров выводятся из типов данных приложения, поскольку строки часто являются Unicode, например, в языках Java и .NET, что приводит к параметрам nvarchar независимо от базового типа данных столбца.

Решение состоит в том, чтобы использовать параметры varchar для строк вместо nvarchar, если только нижний столбец не равен nvarchar.Если вы не используете nvarchar в базе данных, укажите параметр строки соединения JDBC sendStringParametersAsUnicode, как описано в в этом ответе .Либо измените код приложения или конфигурацию платформы, чтобы использовать тип параметра varchar для столбцов varchar.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...