Я использовал профилировщик ANTS, чтобы определить остающееся узкое место в моем приложении C #: хранимая процедура SQL Server. Я использую SQL Server 2008. Может ли кто-нибудь здесь помочь мне повысить производительность или дать подсказки о том, что я могу сделать, чтобы сделать его лучше или более производительным?
Во-первых, вот процедура:
PROCEDURE [dbo].[readerSimilarity]
-- Add the parameters for the stored procedure here
@id int,
@type int
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
IF (@type=1) --by Article
SELECT id1, id2, similarity_byArticle FROM similarity WHERE (id1 = @id OR id2 = @id)
AND similarity_byArticle != 0
ELSE IF (@type=2) --by Parent
SELECT id1, id2, similarity_byParent FROM similarity WHERE (id1 = @id OR id2 = @id)
AND similarity_byParent != 0
ELSE IF (@type=3) --by Child
SELECT id1, id2, similarity_byChild FROM similarity WHERE (id1 = @id OR id2 = @id)
AND similarity_byChild != 0
ELSE IF (@type=4) --combined
SELECT id1, id2, similarity_combined FROM similarity WHERE (id1 = @id OR id2 = @id)
AND similarity_combined != 0
END
Таблица 'similarity
' состоит из двух id
s (id1
и id2
) и ряда столбцов, в которых хранятся значения double
. Ограничением является то, что id1 < id2
.
Column Data
----- ----
ID1 PK, Indexed
ID2 PK, Indexed
The table contains 28.5 million entries.
Фон хранимой процедуры
Задача хранимой процедуры - получить все строки, имеющие параметр id
, в id1
или id2
. Кроме того, столбец, указанный в параметре типа, не может быть нулем.
Хранимая процедура вызывается несколько раз для разных ids
. Несмотря на то, что он принимает ~1.6 ms
за звонок, он суммирует, когда звонит 17000 раз.
Процессор работает только на 25%, что, по-видимому, связано с тем, что приложение ожидает возврата вызова процедуры.
Видите ли вы способ ускорить процесс?
Вызов хранимой процедуры C# Code Snippet
private HashSet<NodeClustering> AddNeighbourNodes(int id)
{
HashSet<NodeClustering> resultSet = new HashSet<NodeClustering>();
HashSet<nodeConnection> simSet = _graphDataLoader.LoadEdgesOfNode(id);
foreach (nodeConnection s in simSet)
{
int connectedId = s.id1;
if (connectedId == id)
connectedId = s.id2;
// if the corresponding node doesn't exist yet, add it to the graph
if (!_setNodes.ContainsKey(connectedId))
{
NodeClustering nodeToAdd = CreateNode(connectedId);
GraphAddOuter(nodeToAdd);
ChangeWeightIntoCluster(nodeToAdd.id, s.weight);
_bFlowOuter += s.weight;
resultSet.Add(nodeToAdd);
}
}
// the nodes in the result set have been added
to the outernodes -> add to the outernodes count
_setNodes[id].countEdges2Outside += resultSet.Count;
return resultSet;
}
C # Код Справочная информация
Этот метод вызывается каждый раз, когда в кластер добавляется новый id
. Он получает все подключенные узлы этого id
(они связаны, когда есть запись в БД с id1=id
или id2=id
) через
_graphDataLoader.LoadEdgesOfNode(id);
Затем проверяются все подключенные ids
и, если они еще не загружены:
if (!_setNodes.ContainsKey(connectedId))
Это загружает их:
CreateNode(connectedId);
Метод:
_graphDataLoader.LoadEdgesOfNode(id);
вызывается снова, на этот раз с connectedId
.
Мне нужно это, чтобы получить все соединения новых узлов с теми узлами, которые уже находятся в наборе.
Возможно, я смогу собрать ids
всех узлов, которые мне нужно добавить, и вызвать мою хранимую процедуру только один раз со списком идентификаторов.
Идеи
Я мог бы, вероятно, загрузить подключенное соединение идентификаторов сразу через что-то вроде
SELECT id1, id2, similarity_byArticle FROM similarity WHERE
(id1 = @id OR id2 = @id OR
id1 IN (SELECT id1 FROM similarity WHERE id2 = @id) OR
id2 IN (SELECT id1 FROM similarity WHERE id2 = @id) OR
id1 IN (SELECT id2 FROM similarity WHERE id1 = @id) OR
id2 IN (SELECT id2 FROM similarity WHERE id1 = @id))
AND similarity_byArticle != 0
но тогда я получу больше записей, чем мне нужно, потому что я получу их также для уже загруженных узлов (что из моих тестов составило бы около 75% от общего числа вызовов).
Вопросы
- Как я могу ускорить хранимую процедуру?
- Могу ли я сделать это по-другому, есть ли более эффективный способ?
- Могу ли я использовать
List<int>
в качестве SP-параметра?
- Есть еще мысли?