PHP 高并发 统计网页点击次数 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
hugee
V2EX    PHP

PHP 高并发 统计网页点击次数

  •  
  •   hugee 2019-03-02 00:30:15 +08:00 8471 次点击
    这是一个创建于 2419 天前的主题,其中的信息可能已经有所发展或是发生改变。

    一般的方法是: "UPDATE base SET hits = hits+1 WHERE id = '$id'";

    一般的访问量都没什么问题,但高并发的情况下怎么做? 有什么好的方法可以实现,降低数据库的读写?

    39 条回复    2019-03-05 16:52:34 +08:00
    yuikns
        1
    yuikns  
       2019-03-02 00:32:41 +08:00
    stabc
        2
    stabc  
       2019-03-02 00:46:18 +08:00
    首推 redis。非要放在 SQL 里的话,如果真的达到高并发,可以用个 random,随机每 10 次写入一次数据库,每次加 10。
    hilbertz
        3
    hilbertz  
       2019-03-02 00:46:29 +08:00
    限制数据库的写入频率,比如说在每个节点上,每隔 10s 才把 hits 写入数据库
    hugee
        4
    hugee  
    OP
       2019-03-02 00:55:18 +08:00 via Android
    @stabc @yuikns 谢谢,用 redis 防止频繁读取数据库这个我知道,但隔一段时间由 redis 写到数据库还没弄过。
    还有,假如 id 为 233 的页面在某一个时间点后再也没人访问了,那缓存内的数据岂不是写不进数据库了?
    KasuganoSoras
        5
    KasuganoSoras  
       2019-03-02 00:57:14 +08:00
    PHP 搞个队列系统就好了
    SQL 语句丢过去,异步执行
    ichou
        6
    ichou  
       2019-03-02 03:31:35 +08:00 via iPhone
    队列
    zjsxwc
        7
    zjsxwc  
       2019-03-02 08:42:11 +08:00
    用 nginx 的话,lua 有个接口叫做:`ngx.shared.stats:incr()` 可以直接用
    gouchaoer
        8
    gouchaoer  
       2019-03-02 08:59:43 +08:00 via Android
    在 fpm 里面用 apcu 统计,然后每隔一段时间从 apcu 往数据库写,由于 apcu 在同一台主机上所以不同的 fpm 的时间戳是一样的。比如存 hits 变量,每次访问 apcu_incr 一下 hits,然后去检查 hits_timestamp 是否大于 60s,大于了的话就去 apcu
    gouchaoer
        9
    gouchaoer  
       2019-03-02 09:06:34 +08:00 via Android   2
    在 fpm 里面用 apcu 统计,然后每隔一段时间从 apcu 往数据库写,由于 apcu 在同一台主机上所以不同的 fpm 的时间戳是一样的。比如存 hits 变量,每次访问 apcu_incr 一下 hits,然后去 apcu_add 检查 hits_lock 变量来实现锁的更新数据库逻辑,ttl 参数就是每隔多长时间往数据库里面刷

    在 fpm 里面的话,我有自信这个是最优方案,连 Redis 走 tcp 有 io 的,这个没 io
    gouchaoer
        10
    gouchaoer  
       2019-03-02 09:08:53 +08:00 via Android   3
    用队列不觉得扯么?你在 fpm 里面往队列写是不是有 io,然后这个队列还只是 incr 一下数据库一个字段。。。稍微靠谱一点的是直接发 udp 发完就走,不过还得写个接受 udp 的 server 太麻烦了
    Maboroshii
        11
    Maboroshii  
       2019-03-02 09:12:18 +08:00 via Android
    进程同步,linux 不是有文件锁么。不知在 php 上是否可行
    lestat
        12
    lestat  
       2019-03-02 09:13:45 +08:00 via Android
    我这边的实现是:redis 设置一个 key,每次访问+1,在后台开一个定时任务,每隔一定时间批量写入一次数据库,然后清空 key
    gouchaoer
        13
    gouchaoer  
       2019-03-02 09:22:41 +08:00 via Android
    不可以用文件锁,此外往 Redis incr 一个 key 的方案,然后后台开 cron 运维麻烦一点,觉得麻烦可以用 Redis 的 add expire 机制来把更新到数据库逻辑写到业务逻辑里
    loveCoding
        14
    loveCoding  
       2019-03-02 09:41:03 +08:00
    放队列 , 批量获取,合并更新
    dawniii
        15
    dawniii  
       2019-03-02 09:46:56 +08:00
    @gouchaoer redis + cron 算是很简单的了。使用队列也还不错,因为往往不只是一个简单的+1 的操作,虽然有 io,但也是为了访问数据库削峰。您说的 udp 还是不建议的,毕竟可靠性要比其他方案低,之前听人说 udp 本机丢包都不稀奇。apcu 这种假如有 10w 个文章,是需要 10w 个 key 来计数吗? redis 的话用一个 hash 结构的 key 就够了,假如一篇文章被访问几次,然后长期不被访问,岂不是计数一直落不了库了,然后 fpm 重启这些计数都会丢失。
    skymei
        16
    skymei  
       2019-03-02 09:47:12 +08:00
    不去重去 redis 的 string,hash 即可,要去重用 hyperloglog,redis 是单线程的,不会出现并发的问题
    leis1015
        17
    leis1015  
       2019-03-02 09:52:41 +08:00 via iPhone   1
    也可以用第三方的啊,挂个 js 就行了,还能很方便的查看数据

    百度腾讯都有…
    penghong
        18
    penghong  
       2019-03-02 11:24:11 +08:00
    hyperloglog
    bokchoys
        19
    bokchoys  
       2019-03-02 11:26:15 +08:00 via iPhone
    我发现,我之前思考的问题,在 v 站都会出现
    ibegyourpardon
        20
    ibegyourpardon  
       2019-03-02 11:33:28 +08:00
    朋友,我觉得真的高并发的话,可能你的问题首先不是出在 MySQL 上,而是 php-fpm 上……

    当然我不知道你怎么部署的,如果真的若干机器堆住,一般 hold 住也没问题,当然真是这样的话那 Redis 啥的也不算事了。

    毕竟不知道你的高并发首先有多高 - -#
    goodspb
        21
    goodspb  
       2019-03-02 11:38:52 +08:00
    收钱你要确定一下“高并发”的定义哦,如果是 2K 以下的 QPS,mysql 读写分离想怎么玩怎么玩~
    goodspb
        22
    goodspb  
       2019-03-02 11:39:01 +08:00
    @goodspb 首先
    chinvo
        23
    chinvo  
       2019-03-02 11:46:09 +08:00 via iPhone
    @leis1015 #16 第三方可能被投毒,比如某大炮
    mrdemonson
        24
    mrdemonson  
       2019-03-02 12:30:52 +08:00 via Android
    高并发统计直接上数据库不好吧,应该在进数据库之前完成“高并发”,数据库只是记录最终值,过程要由上一层完成
    t6attack
        25
    t6attack  
       2019-03-02 13:32:11 +08:00
    宗旨就是:尽量在内存中进行。具体实现方式,有内存数据库、缓存系统等。
    还有其他姿势,比如在临时文件中计数,文件放进 /dev/shm 目录。每隔一段时间写一次数据库。
    Joyboo
        26
    Joyboo  
       2019-03-02 14:19:33 +08:00
    redis+woker 异步
    liuguang
        27
    liuguang  
       2019-03-02 14:40:07 +08:00
    redis incr 命令
    hxt
        28
    hxt  
       2019-03-02 14:48:16 +08:00
    先用 redis 或 memcache 计数,然后计数合并,定期同步到数据库。
    z5864703
        29
    z5864703  
       2019-03-02 15:02:26 +08:00
    用 Redis 实现自增和存储,如果有需要定时同步到 Mysql。
    但其实可以直接用 Redis 存储就好了,没必要再同步到 Mysql,本身 Redis 就是一个数据库,比如要查询点击次数,直接查 Redis 对应 key 就好了。
    whatsmyip
        30
    whatsmyip  
       2019-03-02 15:47:28 +08:00
    azh7138m
        31
    azh7138m  
       2019-03-02 17:00:06 +08:00 via Android
    @dawniii 秀啊,这种不应该阻塞主流程的统计,我一个请求超时了咋整,万一全部超时了咋整?
    opengps
        32
    opengps  
       2019-03-02 18:51:38 +08:00
    存入缓存里,定时写入到数据库
    jzmws
        33
    jzmws  
       2019-03-02 19:12:59 +08:00
    这个肯定要保存在缓存中,隔一段事件写入库中.一直写库库也是吃不消的
    KasuganoSoras
        34
    KasuganoSoras  
       2019-03-02 20:32:27 +08:00
    https://github.com/kasuganosoras/SomeCodes/blob/master/v2ex_540212_client.php
    https://github.com/kasuganosoras/SomeCodes/blob/master/v2ex_540212_server.php

    Client 是客户端,嵌入在你的网站里,访问一次就执行一下
    Server 是服务端,用命令行运行即可,然后它会每 60s 写入一次数据库
    需要 Swoole 才能运行服务端

    别总扯理论扯概念,拿出代码来
    Talk is cheap, I show you the code.
    KasuganoSoras
        35
    KasuganoSoras  
       2019-03-02 20:39:17 +08:00
    上面这个 demo,高并发应该是没问题的,要挂也是 php-fpm 先挂掉
    不过高并发场景我一般选择 OpenLiteSpeed
    hefish
        36
    hefish  
       2019-03-02 22:10:00 +08:00
    肯定是用缓存啊。 先记录在缓存里,然后满到一定次数再写入数据库。。
    队列的同学,不是不可以,有些折腾了。
    缓存可以用文件,apcu,也可以 memcached, redis。
    hugee
        37
    hugee  
    OP
       2019-03-03 11:49:46 +08:00
    @KasuganoSoras 赞,感谢哥!
    qsbaq
        38
    qsbaq  
       2019-03-05 16:51:33 +08:00
    你把 IP 什么的都记录到数据库另外的表里,然后 cron 定期统计出来再 update 这个 hits。
    qsbaq
        39
    qsbaq  
       2019-03-05 16:52:34 +08:00
    当然用 redis 记录这些信息也行。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5397 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 39ms UTC 07:56 PVG 15:56 LAX 00:56 JFK 03:56
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86