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

PHP 在高并发下,怎么正确处理,才能保持数据的一致性?

  •  
  •   madaima · 2018-03-15 10:05:13 +08:00 · 7144 次点击
    这是一个创建于 2449 天前的主题,其中的信息可能已经有所发展或是发生改变。
    
    //流程一
    class Factory
    {
        public static function get($item)
        {
            switch ($item->status){
                case 1;
                    return new a($item);
                case 2;
                    return new b($item);
                ...
            }
        }
    }
    
    //流程二
    class  a
    {
        public function aa($item)
        {
            //DB BEGIN
            //set status = 2
            //DB COMMIT
            //catch DB ROLLBACK
        }
    }
    

    首先我们排除使用悲观锁这种方法。

    如代码所示,现在处理数据时会先进入工厂类判断数据状态,然后到不同的类中去处理数据。

    但是在高并发下,流程二没走完的情况下,数据状态没更新,第二次请求还会走到流程二中去处理。

    这种情况 有什么好的方法去处理?

    36 条回复    2018-03-16 10:07:44 +08:00
    jack010love
        1
    jack010love  
       2018-03-15 10:08:54 +08:00
    PHP 的话,我们一般都是在 Redis 加个 flag。
    extreme
        3
    extreme  
       2018-03-15 10:19:33 +08:00
    才疏学浅,坐等高人回答
    TIGERB
        4
    TIGERB  
       2018-03-15 10:23:32 +08:00
    和语言无关吧

    方案 1: 串行操作
    方案 2: 锁
    vus520
        5
    vus520  
       2018-03-15 10:34:18 +08:00
    高并发下,还用数据库的锁来实现,我感觉这是要炸啊,数据库的性能得多好?
    madaima
        6
    madaima  
    OP
       2018-03-15 10:43:00 +08:00
    @jack010love 做类似乐观锁的操作吗?
    kimmykuang
        7
    kimmykuang  
       2018-03-15 10:46:03 +08:00   ❤️ 1
    你的问题是要实时一致性还是最终一致性?现在高并发场景下一般都是舍弃实时一致性保证吞吐,借助队列或 redis 锁或幂等表等方式来实现最终一致性
    robinchina
        8
    robinchina  
       2018-03-15 10:51:09 +08:00 via Android
    因为搞不定别的方法··········所以·锁表···········一锁解千愁·······
    puritania
        9
    puritania  
       2018-03-15 10:52:42 +08:00 via iPhone
    Redis setnx 了解一下
    puritania
        10
    puritania  
       2018-03-15 10:53:11 +08:00 via iPhone
    或者串行消息队列
    solaya
        11
    solaya  
       2018-03-15 10:53:47 +08:00
    MVCC ?
    madaima
        12
    madaima  
    OP
       2018-03-15 10:55:41 +08:00
    @l57t7q 谢谢你提供的资料 我一会研究一下。
    madaima
        13
    madaima  
    OP
       2018-03-15 10:59:46 +08:00
    @vus520 对啊 所以问一下有没有其他好的实现方法学习一下。
    harborM
        14
    harborM  
       2018-03-15 11:04:46 +08:00
    之前看了点文章,一般都是串行或者锁实现的,文件锁数据库锁在高并发情况下都挺粗糙的,可以用 redis 来锁,毕竟锁作为一个同步原语只要实现 acquire 和 release 理论上就成立了,而且 redis 本身可以分布式部署,应该是个可行的方案;串行的话对于部分语言有些难言之隐,php 的话如果回调形式返回结果有点难以主动推.只是我的一点思路
    dilu
        15
    dilu  
       2018-03-15 11:06:48 +08:00
    加锁
    如果需要更高的性能 我才疏学浅 看别的大神怎么回答吧
    rqrq
        16
    rqrq  
       2018-03-15 11:09:43 +08:00
    缓存加标志怎么弄都可以,并发了直接返回一个提示给用户,操作失败,让用户自己重试。
    数据库乐观锁有个好处是并发了会挂着等前面的处理完毕,然后自动再读取,只要不超时,用户只需要等待。
    看实际情况用哪种。
    这是我的理解,也不知道对不对,这也应该就是 @kimmykuang 说的实时一致性和最终一致性。
    rqrq
        17
    rqrq  
       2018-03-15 11:11:48 +08:00
    我还没试过 redis 的锁是不是可以像数据库锁那样,如果可以肯定优先用 redis 来实现。
    madaima
        18
    madaima  
    OP
       2018-03-15 11:16:38 +08:00
    感谢 楼上各位热心回复 我现在大概有个思路了 现在去实现下 试试效果。
    eslizn
        19
    eslizn  
       2018-03-15 11:16:40 +08:00
    事务化,每次请求一个 id,然后这个请求中的任何原子操作,都需要额外具备确认(用于超时等情况下的重试)、回滚接口。不能确定的失败操作一律丢入队列进行重试、回滚或人工处理
    Junjunya
        20
    Junjunya  
       2018-03-15 11:17:32 +08:00
    码一下, 等高人回答完了, 来学习
    onepunch
        21
    onepunch  
       2018-03-15 11:43:44 +08:00
    Redis 锁,感觉 aa 应该被保护起来吧 ,如果并发很容易就达到 aa,那么数据库的压力还是很大的吧。

    战略 mark 下
    yao978318542
        22
    yao978318542  
       2018-03-15 11:58:13 +08:00
    队列?我觉得可以吧
    RorschachZZZ
        23
    RorschachZZZ  
       2018-03-15 12:17:59 +08:00
    目前我用队列来解决,坐等高手回答 码之
    lqtriks
        24
    lqtriks  
       2018-03-15 12:23:32 +08:00
    Mark.
    prolic
        25
    prolic  
       2018-03-15 13:03:38 +08:00 via Android
    redlock 了解一下
    kobe123
        26
    kobe123  
       2018-03-15 13:41:44 +08:00
    异步 work
    Xrong
        27
    Xrong  
       2018-03-15 13:46:45 +08:00
    楼主弄完,分享下方案撒。
    wekw
        28
    wekw  
       2018-03-15 14:08:56 +08:00   ❤️ 1
    1. 分布式系统的核心功能是“队列消息传递”。
    2. 并发量不太高的时候,用原子化锁:redis 字段标记。
    3. 并发量太高的时候就得时间换性能了:用高性能消息队列。本质依然是原子化,不过将简单的标记又抽象成了队列数据存储+后期执行,实现了单机更高性能。
    4. 当单机消息队列都扛不住的时候就要上真正的分布式消息队列了,如 kafka
    90safe
        29
    90safe  
       2018-03-15 14:46:15 +08:00
    @qfdk 来研究这个啊,整天研究速度优化有啥用
    youqiantu
        30
    youqiantu  
       2018-03-15 18:28:06 +08:00
    高并发 redis 也不行啊。。。
    shisang
        31
    shisang  
       2018-03-15 18:45:38 +08:00
    乐观锁
    liuxu
        32
    liuxu  
       2018-03-15 18:58:48 +08:00
    要不楼主别两次 get,直接 case1 执行完后回调 case2?
    vtwoextb
        33
    vtwoextb  
       2018-03-15 19:17:31 +08:00
    队列
    afeicool
        34
    afeicool  
       2018-03-15 21:59:30 +08:00 via Android
    不知道是不是我的理解能力太弱,完全没看懂你的业务,如果只是改状态的话 完全可以用 set status=2 where status = 1, 何必又是这个锁那个队列,甚至跑到分布式上去了…
    madaima
        35
    madaima  
    OP
       2018-03-16 08:56:25 +08:00
    @afeicool 这里只是举个大概的流程,每个流程里面有很多业务方法,单改变状态的话就没有这么复杂了。
    th00000
        36
    th00000  
       2018-03-16 10:07:44 +08:00
    redis 串行
    否则 乐观锁 悲观锁
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   6040 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 02:12 · PVG 10:12 · LAX 18:12 · JFK 21:12
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.