V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
frozenway
V2EX  ›  PHP

PHP 遇到了一个匪夷所思的问题

  •  
  •   frozenway · 2017-11-01 15:56:01 +08:00 · 3290 次点击
    这是一个创建于 2580 天前的主题,其中的信息可能已经有所发展或是发生改变。
    写一个用于统计网站访问信息的功能时,遇到了个问题:
    -->我的思路是这样的:当网站有人访问时,判断数据表里是否有记录今天访问的信息,如果没有着新建一条记录,然后就在访问量上+1 ;如果已经有记录了,着直接在访问量上+1 ;代码如下:

    //统计访问次数
    protected function ttVisit($aid){
    if(is_numeric($aid)){
    $model = M('monitor');
    $row = $model->where(['aid'=>$aid, 'xdate'=>date('Y-m-d')])->field('moid')->find();
    if(!$row){
    $row['moid'] = $model->data(['xdate'=>date('Y-m-d'), 'aid'=>$aid])->add();
    if(!$row['moid']){
    return false;
    }
    }
    $model->where(['moid'=>$row['moid']])->setInc('top');
    return $row['moid'];
    }
    return false;
    }

    其中$aid 是对应某个网站的 ID,top 是数据库字段,表示访问量。xdate 是要统计访问量的日期。
    好了,这样的逻辑一点问题都没有,但是真正运行起来,理想状态数据表应该是这样的:

    id aid xdate top
    1 1 2017-11-01 20
    2 2 2017-11-01 100
    3 5 2017-11-01 96

    然而现实运行起来是这样的

    id aid xdate top
    1 1 2017-11-01 1
    2 1 2017-11-01 19
    3 2 2017-11-01 100
    4 5 2017-11-01 94
    5 5 2017-11-01 1
    6 5 2017-11-01 1

    为什么理想和现实差距这么大?
    是我的逻辑有问题了吗?
    13 条回复    2017-11-01 17:29:26 +08:00
    b821025551b
        1
    b821025551b  
       2017-11-01 16:02:19 +08:00
    并发问题,要加锁。
    zjsxwc
        2
    zjsxwc  
       2017-11-01 16:08:56 +08:00 via Android
    用队列处理更方便
    frozenway
        3
    frozenway  
    OP
       2017-11-01 16:11:01 +08:00
    @b821025551b 数据库是 MyISAM,加不了锁
    frozenway
        4
    frozenway  
    OP
       2017-11-01 16:12:38 +08:00
    @zjsxwc 没接触过队列,能说清楚点吗?
    b821025551b
        5
    b821025551b  
       2017-11-01 16:14:15 +08:00
    两个解决方案:
    1:上队列;
    2:做个定时任务,比如今天凌晨 3 点把明天的数据顺序的 add 一遍,保证明天的数据在明天只有 update 操作。
    sagaxu
        6
    sagaxu  
       2017-11-01 16:24:28 +08:00
    加唯一索引(aid, xdate),然后用 upsert(insert-on-duplicate)

    顺便问一句,楼主你是来黑 PHP 的吗?
    vescape920
        7
    vescape920  
       2017-11-01 16:24:33 +08:00
    不知道我理解的对不对 就是每个用户每天第一次访问时 insert 记录 之后是 update 该条记录
    你可以把 user_id 和 date 建一个 unique 索引 在写 sql 的时候使用 ON DUPLICATE KEY UPDATE
    akira
        8
    akira  
       2017-11-01 16:28:35 +08:00
    aid 和 xdate 做个复合主键 ,代码什么的完全不用动了。
    缺点是会丢部分数据
    frozenway
        9
    frozenway  
    OP
       2017-11-01 16:34:46 +08:00
    @akira
    @vescape920
    @sagaxu @b821025551b
    多谢大神指点
    fcten
        10
    fcten  
       2017-11-01 17:01:28 +08:00
    mysql 是 php 不可分割的一部分!(滑稽.jpg
    fcten
        11
    fcten  
       2017-11-01 17:04:23 +08:00
    顺便,这样统计访问量性能非常差,建议做合理缓存
    yxn1910
        12
    yxn1910  
       2017-11-01 17:16:10 +08:00
    访问次数这样的非敏感数据建议先缓存,定时入库,在这里使用事务性价比太低。
    linpf
        13
    linpf  
       2017-11-01 17:29:26 +08:00
    并发量太大,在第一次请求时,数据库判断没有记录,然后再插入新纪录的时候,第二次请求也通过了有无记录的判断,导致重复插入。

    最好的办法是加锁。但是加锁也不必依赖数据库。使用 TP 框架的缓存做一个简单的锁也可以。不妨试一下。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2851 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 08:49 · PVG 16:49 · LAX 00:49 · JFK 03:49
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.