Как я могу написать с параллельными потоками в одну таблицу
Я работаю над приложением, которое читает много данных с параллельными потоками в одну таблицу.
Я использую Microsoft SQL Server 2014 с одной оптимизированной для памяти таблицей и встроенной скомпилированной хранимой процедурой для записи.
Мое приложение считывает тонны данных (виртуальные сделки) в эту таблицу, оптимизированную для памяти:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Trades]
(
[asset_id] [bigint] NOT NULL,
[trade_id] [bigint] NOT NULL,
[BNP] [money] NOT NULL,
[LSP] [money] NULL,
[expires] [int] NULL,
[timestamp] [bigint] NULL,
[currentBid] [money] NULL,
[startingBid] [money] NULL,
[pos] [nchar](3) COLLATE Latin1_General_BIN2 NOT NULL,
[rating] [int] NOT NULL,
INDEX [asset] NONCLUSTERED HASH
(
[asset_id],
[BNP]
)WITH ( BUCKET_COUNT = 32),
CONSTRAINT [Trades_primaryKey] PRIMARY KEY NONCLUSTERED HASH
(
[trade_id]
)WITH ( BUCKET_COUNT = 64)
)WITH ( MEMORY_OPTIMIZED = ON , DURABILITY = SCHEMA_AND_DATA )
GO
Хранимая процедура вычисляет при каждом вызове среднюю цену для столбца. BNP
для всех строк с одинаковыми asset_id
чтобы выяснить, является ли текущая сделка очень дешевой:
CREATE PROCEDURE [dbo].[ADD_TRADE_]
@asset int, -- asset_id
@trade nchar(12), -- trade_id
@BNP int, -- BuyNowPrice
@LSP int, -- LastSellPrice
@expires nchar(16), -- expiration in seconds
@timestamp bigint, -- current timestamp
@currentBid int, -- current Bid on trade
@startingBid int, -- starting Bid on trade
@pos nchar(3), -- Player Position
@rating int, -- Card Rating
@MIN_TRADE_CNT int,
@BUY_PERCENTAGE int,
@AVG_BNP int
WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER
AS BEGIN ATOMIC WITH
(
TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english'
)
--CONFIG
/* RATING TO COLOR
DECLARE @BRONZE int = 64
DECLARE @SILVER int = 74
*/
/* 1 //// INSERT FRESH TRADES ////////////////////// */
--TMP VARS
DECLARE @trade_cnt int = 0
DECLARE @exists bit = 0
--CHECK IF TRADE EXISTS
SELECT @exists=1
FROM [dbo].[Trades]
WHERE trade_id = @trade
-- ADD IF NOT
IF @exists=0
BEGIN
INSERT INTO [dbo].[Trades]
(asset_id,trade_id,BNP,LSP,expires,timestamp,currentBid,startingBid,pos,rating)
VALUES(@asset, @trade,@BNP,@LSP,@expires,@timestamp,@currentBid,@startingBid,@pos,@rating)
//// CHECK IF TRADE IS A GOOD ONE TO BUY ////////////////////
DECLARE @MIN_BID_PRICE money = 0
SELECT @trade_cnt=COUNT(*) FROM dbo.Trades;
SET @MIN_BID_PRICE = (@AVG_BNP/100) * @BUY_PERCENTAGE
/// IF YES , ADD TO BID TABLE
IF @trade_cnt >= @MIN_TRADE_CNT AND @BNP > 0 AND @BNP < @MIN_BID_PRICE
BEGIN
INSERT INTO [dbo].[Bids](trade_id,bid,max) VALUES(@trade,@BNP,@MIN_BID_PRICE)
END
END
END
Так что он не только пишет параллельно, он также читает параллельно. Теперь моя проблема в том, что некоторые записи блокируются, и я получаю эту ошибку:A previous transaction that the current transaction took a dependency on has aborted, and the current transaction can no longer commit.
Если я обработаю эту ошибку и использую логику повторных попыток, некоторые из этих повторных попыток все еще блокируются до 10 раз, и это не только блокирует один конкретный поток работника, но и создает большой стек потоков, ожидающих своей очереди на запись,
Я искал и нашел страницу MSDN, где их описывают уровни изоляции И я думаю, read committed
это то, что мне нужно Но кажется, что я не могу использовать это в таблице, оптимизированной для памяти.
Что я могу изменить в своем коде или я могу использовать альтернативную систему БД для этого?
1 ответ
Как вы получаете ваши данные в вашей хранимой процедуре? Если это через.NET-приложение, вам, вероятно, лучше создать DataTable
в памяти для всех ваших столбцов - включая вычисленный столбец, а затем с помощью SqlBulkCopy
класс, чтобы написать все эти данные за один раз. если у вас есть слишком много, вы всегда можете разбить его на партии.
Это намного эффективнее, чем пытаться писать по одной строке за раз!