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

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

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

    一般的方法是: "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   ·   实用小工具   ·   4138 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 743ms · UTC 05:27 · PVG 13:27 · LAX 21:27 · JFK 00:27
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.