我目前正在開發一個廣告系統,這個廣告系統現在運行得很好,除了最近我們每天的觀看次數已經從大約7k上升到了328k。我們的服務器不能再承受這種壓力 - 並且知道我並不是最好的SQL人(嘿,我可以讓它工作,但並不總是以最好的方式)我在這裏要求一些優化指南。我希望你們中的一些人能夠就如何改進這個問題提出粗略的想法 - 我並不特別需要代碼,只是爲了看清光明:)。廣告系統上的Sql優化
就像現在一樣,當一個廣告應該被顯示出來時,一個PHP腳本被調用,這個腳本會調用一個存儲過程。這個存儲過程會執行多次檢查,它會根據我們的客戶數據庫進行測試,看看顯示廣告的人(由主鍵ID給出)是否是給定語言環境下的實際客戶(我們的系統正在運行幾種語言作爲單獨的網站)。接下來是取出的所有廣告詳細信息(圖像位置作爲網址,廣告的高度和寬度) - 並免除一步調用單獨的存儲過程以測試是否允許顯示廣告(廣告活動是在任一日期過期或允許展示的廣告數量?),如果客戶有權訪問它(我們有2個訪問系統正在運行,黑名單和白名單),最後是我們正在運行的是哪種類型的廣告系列,該視圖是唯一的,等等。
該代碼由兩個存儲過程組成,我將在這裏發佈。
---程序從PHP
稱爲CREATE PROCEDURE [dbo].[ExecView]
(
@publisherId bigint,
@advertId bigint,
@localeId int,
@ip varchar(15),
@ipIsUnique bit,
@success bit OUTPUT,
@campaignId bigint OUTPUT,
@advert varchar(500) OUTPUT,
@advertWidth int OUTPUT,
@advertHeight int OUTPUT
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @unique bit
DECLARE @approved bit
DECLARE @publisherEarning money
DECLARE @advertiserCost money
DECLARE @originalStatus smallint
DECLARE @advertUrl varchar(500)
DECLARE @return int
SELECT @success = 1, @advert = NULL, @advertHeight = NULL, @advertWidth = NULL
--- Must be valid publisher, ie exist and actually be a publisher
IF dbo.IsValidPublisher(@publisherId, @localeId) = 0
BEGIN
SELECT @success = 0
RETURN 0
END
--- Must be a valid advert
EXEC @return = FetchAdvertDetails @advertId, @localeId, @advert OUTPUT, @advertUrl OUTPUT, @advertWidth OUTPUT, @advertHeight OUTPUT
IF @return = 0
BEGIN
SELECT @success = 0
RETURN 0
END
EXEC CanAddStatToAdvert 2, @advertId, @publisherId, @ip, @ipIsUnique, @success OUTPUT, @unique OUTPUT, @approved OUTPUT, @publisherEarning OUTPUT, @advertiserCost OUTPUT, @originalStatus OUTPUT, @campaignId OUTPUT
IF @success = 1
BEGIN
INSERT INTO dbo.Stat (AdvertId, [Date], Ip, [Type], PublisherEarning, AdvertiserCost, [Unique], Approved, PublisherCustomerId, OriginalStatus)
VALUES (@advertId, GETDATE(), @ip, 2, @publisherEarning, @advertiserCost, @unique, @approved, @publisherId, @originalStatus)
END
END
--- IsValidPublisher
CREATE FUNCTION [dbo].[IsValidPublisher]
(
@publisherId bigint,
@localeId int
)
RETURNS bit
AS
BEGIN
DECLARE @customerType smallint
DECLARE @result bit
SET @customerType = (SELECT [Type] FROM dbo.Customer
WHERE CustomerId = @publisherId AND Deleted = 0 AND IsApproved = 1 AND IsBlocked = 0 AND LocaleId = @localeId)
IF @customerType = 2
SET @result = 1
ELSE
SET @result = 0
RETURN @result
END
- 獲取廣告信息
CREATE PROCEDURE [dbo].[FetchAdvertDetails]
(
@advertId bigint,
@localeId int,
@advert varchar(500) OUTPUT,
@advertUrl varchar(500) OUTPUT,
@advertWidth int OUTPUT,
@advertHeight int OUTPUT
)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT @advert = T1.Advert, @advertUrl = T1.TargetUrl, @advertWidth = T1.Width, @advertHeight = T1.Height FROM Advert as T1
INNER JOIN Campaign AS T2 ON T1.CampaignId = T2.Id
WHERE T1.Id = @advertId AND T2.LocaleId = @localeId AND T2.Deleted = 0 AND T2.[Status] <> 1
IF @advert IS NULL
RETURN 0
ELSE
RETURN 1
END
--- CanAddStatToAdvert
CREATE PROCEDURE [dbo].[CanAddStatToAdvert]
@type smallint, --- Type of stat to add
@advertId bigint,
@publisherId bigint,
@ip varchar(15),
@ipIsUnique bit,
@success bit OUTPUT,
@unique bit OUTPUT,
@approved bit OUTPUT,
@publisherEarning money OUTPUT,
@advertiserCost money OUTPUT,
@originalStatus smallint OUTPUT,
@campaignId bigint OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DECLARE @campaignLimit int
DECLARE @campaignStatus smallint
DECLARE @advertsLeft int
DECLARE @campaignType smallint
DECLARE @campaignModeration smallint
DECLARE @count int
SELECT @originalStatus = 0
SELECT @success = 1
SELECT @approved = 1
SELECT @unique = 1
SELECT @campaignId = CampaignId FROM dbo.Advert
WHERE Id = @advertId
IF @campaignId IS NULL
BEGIN
SELECT @success = 0
RETURN
END
SELECT @campaignLimit = Limit, @campaignStatus = [Status], @campaignType = [Type], @publisherEarning = PublisherEarning, @advertiserCost = AdvertiserCost, @campaignModeration = ModerationType FROM dbo.Campaign
WHERE Id = @campaignId
IF (@type <> 0 AND @type <> 2 AND @type <> @campaignType) OR ((@campaignType = 0 OR @campaignType = 2) AND (@type = 1)) -- if not a click or view type, then type must match the campaign (ie, only able to do leads on lead campaigns, no isales or etc), click and view campaigns however can do leads too
BEGIN
SELECT @success = 0
RETURN
END
-- Take advantage of the fact that the variable only gets touched if there is a record,
-- which is supposed to override the existing one, if there is one
SELECT @publisherEarning = Earning FROM dbo.MapCampaignPublisherEarning
WHERE CanpaignId = @campaignId AND PublisherId = @publisherId
IF @campaignStatus = 1
BEGIN
SELECT @success = 0
RETURN
END
IF NOT @campaignLimit IS NULL
BEGIN
SELECT @advertsLeft = AdvertsLeft FROM dbo.Campaign WHERE Id = @campaignId
IF @advertsLeft < 1
BEGIN
SELECT @success = 0
RETURN
END
END
IF @campaignModeration = 0 -- blacklist
BEGIN
SELECT @count = COUNT([Status]) FROM dbo.MapCampaignModeration WHERE CampaignId = @campaignId AND PublisherId = @publisherId AND [Status] = 3
IF @count > 0
BEGIN
SELECT @success = 0
RETURN
END
END
ELSE -- whitelist
BEGIN
SELECT @count = COUNT([Status]) FROM dbo.MapCampaignModeration WHERE CampaignId = @campaignId AND PublisherId = @publisherId AND [Status] = 2
IF @count < 1
BEGIN
SELECT @success = 0
RETURN
END
END
IF @ipIsUnique = 1
BEGIN
SELECT @unique = 1
END
ELSE
BEGIN
IF (SELECT COUNT(T1.Id) FROM dbo.Stat AS T1
INNER JOIN dbo.IQ_Advert AS T2
ON T1.AdvertId = T2.Id
WHERE T2.CampaignId = @campaignId
AND T1.[Type] = @type
AND T1.[Unique] = 1
AND T1.PublisherCustomerId = @publisherId
AND T1.Ip = @ip
AND DATEADD(SECOND, 86400, T1.[Date]) > GETDATE()
) = 0
SELECT @unique = 1
ELSE
BEGIN
SELECT @unique = 0, @originalStatus = 1 -- not unique, and set status to be ip conflict
END
END
IF @unique = 0 AND @type <> 0 AND @type <> 2
BEGIN
SELECT @unique = 1, @approved = 0
END
IF @originalStatus = 0
SELECT @originalStatus = 5
IF @approved = 0 OR @type <> @campaignType
BEGIN
SELECT @publisherEarning = 0, @advertiserCost = 0
END
END
我認爲這需要的不僅僅是幾個索引來幫助它,而是對如何處理它的總體反思。我聽說把這個作爲批處理運行會有所幫助,但我不知道如何實現這個實現,並且真的不確定我是否可以在實際插入之前保持所有這些很好的檢查的方式實現它,或者如果我必須放棄這一些。
無論如何,所有的幫助將不勝感激,如果你需要任何表格佈局,讓我知道:)。
感謝您花時間看看它:)
@kastermester:優化取決於特定的RDBMS。你會指定它是什麼嗎? – Sung 2009-04-20 12:20:52
我很抱歉,我假設你的意思是SQL Server?這是一個MS SQL Server 2008,相信它是具體的網絡版。 – kastermester 2009-04-21 14:21:28