• 借助Redis做秒杀和限流的思考


    最近群里聊起秒杀和限流,我自己没有做过类似应用,但是工作中遇到过更大的数据和并发。

    于是提出了一个简单的模型:

    var count = rds.inc(key);

    if(count > 1000) throw "已抢光!"

    借助Redis单线程模型,它的inc是安全的,确保每次加一,然后返回加一后的结果。如果原来是234,加一了就是235,返回的一定是235,在此中间,不会有别的请求来打断从而导致返回236或者其它。

    其实我们可以理解为inc的业务就是占坑排队,每人占一个坑,拿到排队小票后看看是不是超额了,再从业务层面输出秒杀结果,甚至做一些更加复杂的业务。

    六条提到限流,可能基于某种考虑,希望把key对应的count给限制在1000附近,可以接受1%偏差。

    于是有了改进模型:

    var count = rds.inc(key);

    if(count > 1000){

        rds.dec(key);

        throw "超出限额!"

    就加了一句,超出限额后,把小票给减回去^_^

    采用Redis有一个好处,比如支持很多应用服务器一起抢……

    当然,对于很大量的秒杀,这个模型也不一定合理,比如要枪10万部手机,然后来了300万用户,瞬间挤上来。

    这里有个变通方法可以试一下,那就是准备10个Redis实例,每个放1万。用户请求过来的时候,可以随机数或者散列取模,找对应实例来进行抢购。

    同理可以直接更多用户的场景。总的来说,在数据较大的时候,随机和散列就具有一定统计学意义,相对来说是比较均衡的。

    上面是大量秒杀的简单场景,那么小数据场景呢?比如就只有几万并发的场景

    小数据场景,单应用实例,可以考虑把Redis都给省了。

    初级模型:

    Interlocked.Increase(ref count);

    if(count >= 1000) throw "抢光啦!"

    中级模型:

    private volatile Int32 count;

    var old = 0;

    do {

        old = count;

        if(old >= 1000) throw "抢光啦!"

    }while(Interlocked.CompareExchange(ref count, old + 1, old) != old);

    这个CAS原子操作可是好东西,在x86指令集下有专门指令CMPXCHG来处理,在处理器级别确保比较和交换数据的原子性。大多数系统想要迈过10万tps的门槛向100万tps靠齐,就必须得实现无锁操作lock-free,其中CAS是最为简单易懂,尽管有时候有ABA问题,但我们可以找到许多解决办法。

    在实际使用场景中,可能有更复杂的需求,那就另当别论,这里只能班门弄斧几个简单易用的模型。

  • 相关阅读:
    HBase 文件读写过程描述
    Kafka 部署指南-好久没有更新博客了
    《Python高性能编程》——列表、元组、集合、字典特性及创建过程
    Ansible常用功能
    vim内替换文件内容
    线程队列-queue
    Python多进程
    python多线程知识-实用实例
    夜间模式的实现
    本地通知的实现
  • 原文地址:https://www.cnblogs.com/nnhy/p/Seckill.html
Copyright © 2020-2023  润新知