我目前正在开发一个广告系统,这个广告系统现在运行得很好,除了最近我们每天的观看次数已经从大约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