首页 文章

违反插入时的PRIMARY KEY约束

提问于
浏览
1

我有一个简单的表:

IPAddress (PK, string)
Requests (int)

这是一个洪水限制器 . 每分钟删除表数据 . 每个页面请求,给定 IPAddressRequests 计数增量 .

由于我们的产品和网站的性质,我们确实遭受了一些偶然/有意的有效DDOS,因此效果很好,我们的网站性能显着提高 .

唯一的问题是,当IP因某种原因每分钟向我们的网站发送数千个请求时,我们会突然出现这些错误:

违反PRIMARY KEY约束'PK_v2SiteIPRequests' . 无法在对象'dbo.v2SiteIPRequests'中插入重复键 . 重复键值为([IP_ADDRESS]) . 该语句已终止 .

制作插入的代码是:

/// <summary>
/// Call everytime a page view is requested
/// </summary>
private static void DoRequest(string ipAddress)
{
    using (var db = new MainContext())
    {
        var rec = db.v2SiteIPRequests.SingleOrDefault(c => c.IPAddress == ipAddress);
        if (rec == null)
        {
            var n = new v2SiteIPRequest {IPAddress = ipAddress, Requests = 1};
            db.v2SiteIPRequests.InsertOnSubmit(n);
            db.SubmitChanges();
        }
        else
        {
            rec.Requests++;
            db.SubmitChanges();

            // Ban?
            if (rec.Requests >= Settings.MAX_REQUESTS_IN_INTERVAL)
            {
                BanIP(ipAddress);
            }
        }
    }
}

处理此异常的最佳方法是什么,为什么会被抛出?这是 try catch 最好吗?

3 回答

  • 5

    如果您同时收到两个请求,则会发生以下情况:

    Request one: is it in the database?
    Request two: is it in the database?
    
    Request one: No, not yet
    Request two: No, not yet
    
    Request one: INSERT
    Request two: INSERT
    
    Request one: WORKS
    Request two: FAILS (already inserted a split second before)
    

    你无法在这里做任何事情,但 grab 异常并优雅地处理它 . 也许通过使用简单的“再试一次”逻辑 .

  • 0

    你在那里遇到了一些竞争条件,特别是在有并发连接时 .

    您可能需要更改方法,并始终存储每个请求,然后查询时间范围内是否有更多请求,并采取您需要的任何操作

  • 1

    这是基于建议的解决方案 . 它很丑,但据我所知可行 .

    /// <summary>
    /// Call everytime a page view is requested
    /// </summary>
    private static void DoRequest(string ipAddress)
    {
        using (var db = new MainContext())
        {
            var rec = db.v2SiteIPRequests.SingleOrDefault(c => c.IPAddress == ipAddress);
            if (rec == null)
            {
                // Catch insert race condition for PK violation.  Especially susceptible when being hammered by requests from 1 IP
                try
                {
                    var n = new v2SiteIPRequest {IPAddress = ipAddress, Requests = 1};
                    db.v2SiteIPRequests.InsertOnSubmit(n);
                    db.SubmitChanges();
                }
                catch (Exception e)
                {
                    try
                    {
                        // Can't reuse original context as it caches
                        using (var db2 = new MainContext())
                        {
                            var rec2 = db2.v2SiteIPRequests.Single(c => c.IPAddress == ipAddress);
                            rec2.Requests++;
                            db2.SubmitChanges();
                            if (rec2.Requests >= Settings.MAX_REQUESTS_IN_INTERVAL)
                            {
                                BanIP(ipAddress);
                            }
                        }
                    }
                    catch (Exception ee)
                    {
                        // Shouldn't reach here
                        Error.Functions.NewError(ee);
                    }
                }
            }
            else
            {
                rec.Requests++;
                db.SubmitChanges();
    
                // Ban?
                if (rec.Requests >= Settings.MAX_REQUESTS_IN_INTERVAL)
                {
                    BanIP(ipAddress);
                }
            }
        }
    }
    

相关问题