Код: Выделить всё
CREATE TABLE master.mytest.control_table (
id int IDENTITY(1,1) NOT NULL,
uuid varchar(36) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
status int NOT NULL,
CONSTRAINT PK__control___3213E83F4F29C19D PRIMARY KEY (id)
);
CREATE NONCLUSTERED INDEX IControlTable_Uuid ON master.mytest.control_table ( uuid ASC )
WITH ( PAD_INDEX = OFF ,FILLFACTOR = 100 ,SORT_IN_TEMPDB = OFF , IGNORE_DUP_KEY = OFF , STATISTICS_NORECOMPUTE = OFF , ONLINE = OFF , ALLOW_ROW_LOCKS = ON , ALLOW_PAGE_LOCKS = ON )
ON [PRIMARY ] ;
< /code>
Таблица содержит 3 столбца: id (pk, автоматическое сгенерирование), Uuid (varchar (36)) и Status (int). Я создал не кластеризованный индекс на uuid.DECLARE @i INT = 1;
WHILE @i
Моя таблица содержит 4M записи со случайными Uuids. Затем я провел следующие тесты, я запустил запрос «Выбрать» с таблицами подсказки updlock, holdlock и запрашивал замки, сгенерированные в таблице DM_TRAN_LOCKS. Поиск UUID не существует в таблице.SELECT resource_type, request_session_id, resource_associated_entity_id, request_mode, request_type, request_status, COUNT(*) as 'count' FROM sys.dm_tran_locks
GROUP BY resource_type, request_session_id, resource_associated_entity_id, request_mode, request_type, request_status
ORDER BY request_session_id;
< /code>
Первый тестовый пример запустил запрос с задержкой до коммита: < /p>
BEGIN TRANSACTION
SELECT * FROM mytest.control_table WITH(UPDLOCK, HOLDLOCK)
WHERE uuid = 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX';
WAITFOR DELAY '00:00:10';
COMMIT TRANSACTION;
Блокирует таблицу:
Как мы видим, мы получили блокировку IX на объекте, блокировку IU на странице и диапазоны-U на ключе. Другие запросы все еще могут работать нормально, за исключением случаев, когда они также ищут один и тот же UUID, с теми же подсказками запроса. Класс следующим образом: < /p>
@Service
@RequiredArgsConstructor
@Slf4j
public class StatusService {
private final StatusRepository repository;
@Transactional(isolation = Isolation.READ_COMMITTED)
public void getUUID(StatusDomain statusDomain) {
log.info("Thread {} entered get method", currentThread().getName());
StatusDomain current = repository.findByUuidWithHoldLock(statusDomain.getUuid());
log.info("Thread {} selected registry {}", currentThread().getName(), current);
ThreadUtils.sleep(10000); //pauses execution for 10s
log.info("Thread {} exited get method", currentThread().getName());
}
}
< /code>
Класс StatusDomain просто представляет таблицу, имеющая идентификатор, UUID и статус. Только для этого теста. Я использую JDBI для реализации. < /P>
@Repository
@UseClasspathSqlLocator
public interface StatusRepository {
@SqlQuery
@RegisterBeanMapper(StatusDomain.class)
StatusDomain findByUuidWithHoldLock(@Bind("uuid") String uuid);
}
< /code>
Конфигурация JDBI: < /p>
@Configuration
public class JdbiConfiguration {
@Bean
public JdbiPlugin sqlObjectPlugin() {
return new SqlObjectPlugin();
}
@Bean
public Jdbi jdbi(DataSource dataSource, List jdbiPlugins) {
TransactionAwareDataSourceProxy dataSourceProxy = new TransactionAwareDataSourceProxy(dataSource);
Jdbi jdbi = Jdbi.create(dataSourceProxy);
jdbiPlugins.forEach(jdbi::installPlugin);
return jdbi;
}
@Bean
public StatusRepository statusRepository(Jdbi jdbi) {
return jdbi.onDemand(StatusRepository.class);
}
}
< /code>
Запрос findbyuuidwithholdlock определяется как: < /p>
SELECT * FROM mytest.control_table WITH(UPDLOCK, HOLDLOCK)
WHERE uuid = :uuid
< /code>
Цель состоит в том, чтобы просто запросить таблицу замков после выбора и перед выходом на метод, прежде чем совершить транзакцию.@ActiveProfiles(profiles = {"local"})
@SpringBootTest(properties = "spring.main.allow-bean-definition-overriding=true")
class StatusServiceTest {
@Autowired
public ApplicationContext applicationContext;
@Autowired
public StatusService service;
@MockitoSpyBean
public StatusRepository repository;
private final StatusDomain status4 = new StatusDomain(55L, "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", 4);
@BeforeEach
void setUp() {
repository.deleteByUuid(status4.getUuid());
}
@Test
void testGet() {
assertDoesNotThrow(() -> {
Thread t1 = new Thread(() -> service.getUUID(status4), "THREAD1");
t1.start();
t1.join();
});
Mockito.verify(repository, times(1)).findByUuidWithHoldLock(anyString());
}
}
< /code>
@beforeeach Удаляет запрос UUID, чтобы убедиться, что он не существует в таблице перед каждым тестом. Репозиторий аннотируется с помощью Mockitospybean, поэтому я могу сделать утверждение о том, сколько раз его вызывали. https://www.brentozar.com/pastetheplan/?id=pcpxa0cpeo
Таблица блокировки:
Подробнее здесь: https://stackoverflow.com/questions/797 ... nsactional
Мобильная версия