公司有个抢单的业务,要网站实现。
我自己设计的解决方案想问问大家是否可行,如果不可行有更成熟的方案吗?
我的方案: 请求进来之后首先判断当前时间是否在抢单时间内,如果在就修改缓存中的抢单人id,表明此订单被抢单人抢到,然后修改抢单允许时间为false。
欢迎选择我的课程,让我们一起见证您的进步~~
为什么要扯上12306呢?即使是12306的抢票目前来看也有很大的问题。
你的方案如果在并发小的情况下是可以的,如果并发很大的情况下,会造成绝对第一人事实上抢不到订单,甚至系统异常,我们看一下你的流程
step1:通过了抢单时间判断
step2:保存该抢单人ID
step3:执行修改抢单时间为false
但是如果并发量特别大的情况呢?有1000个请求同时到达,第一个请求通过了step1,然后在执行step2和step3的同时,服务器接受到的请求会依然通过step1判断,直至第一个请求执行完step3,这就造成这期间所有的请求返回的结果都是成功抢到了单,但事实上服务器保存的是这期间里的最后一个请求。
解决办法就是使用队列。
把所有请求全部的用户id全部加入队列(表,redis等),在接收到抢单请求时触发任务,在队列中取出第一个,返回结果,并结束请求。
排队系统就行了。所有请求,设置一个临界值(比如100张票,临界值200,只有200个人能进入队列,其他人就直接提示,没有可售的票。),优先进入队列(redis,不处理数据库)。真正处理数据库的是服务器异步处理redis队列数据!
楼上几个说得对,排队,将所有请求排队,然后挨个处理。相当于把并发请求(多线程)转换成单线程,但是挨个处理会不会很慢,这个简直是一定的。
具体会涉及到以下几个细节:
用队列,楼上已经讲了
用缓存,你自己已经提到了,不过这里需要注意的是,如果对性能有很高要求,那么应当使用内存内的缓存,而不是用redis,memcached之类的外部缓存,因为与这些外部缓存通信有IO开销,这个要尽量避免
缓存和数据库的数据保持一致,这个比较难,在高并发情况下我们不会同步的去修改数据库,而是将数据库的操作延异步化,从而做到最终一致
数据库写入的时候要做优化,否则在大量交易的情况下将结果从缓存中写入数据库都要花大半天这个也太不合理了
试试这个呢 rabbitmq
说的好像12306不是排队似的,但是12306是超大并发,而且商品是不断变化中,1张上海到北京的票呗卖了南京到徐州,就变成3张票啦,退票的话,还要验证是否能拼接为更大的一张票。。。。。适合自己就好,不要搞大新闻
对,用消息队列比较好,如果系统内暂时还没有接入消息队列的话,我们可以曲线救国:
比方说10点10分开始抢票,从10点10分开始,把每一个请求的用户id全部记录到redis里面或者是数据库里面,然后按时间倒序排序,取第一个就OK了
12306 的并发量并没有你们想象的那么大,每次消费的最大并发量不超过一个车次的总票数。一辆列车总乘客数量最多也就几百人。也就是说最大并发量也就几百个。 那不同的车次的锁是分开的,也就是说直接锁车次就够了。用不着搞队列、缓存这么麻烦。
我说一个和楼上都不同的观点,那就是real time programming,每个线程都有自己的priority,scheduler用SCHED_RR,也可以设置mutex属性,得到mutex的线程有相应的priority。不同priority的线程是在不同的FIFO队列里,虽然不能做到一定是先到先得,但是总体样本来说,是先到先得。
为什么要扯上12306呢?即使是12306的抢票目前来看也有很大的问题。
你的方案如果在并发小的情况下是可以的,如果并发很大的情况下,会造成绝对第一人事实上抢不到订单,甚至系统异常,
我们看一下你的流程
step1:通过了抢单时间判断
step2:保存该抢单人ID
step3:执行修改抢单时间为false
但是如果并发量特别大的情况呢?有1000个请求同时到达,第一个请求通过了step1,然后在执行step2和step3的同时,服务器接受到的请求会依然通过step1判断,直至第一个请求执行完step3,这就造成这期间所有的请求返回的结果都是成功抢到了单,但事实上服务器保存的是这期间里的最后一个请求。
解决办法就是使用队列。
把所有请求全部的用户id全部加入队列(表,redis等),在接收到抢单请求时触发任务,在队列中取出第一个,返回结果,并结束请求。
排队系统就行了。所有请求,设置一个临界值(比如100张票,临界值200,只有200个人能进入队列,其他人就直接提示,没有可售的票。),优先进入队列(redis,不处理数据库)。真正处理数据库的是服务器异步处理redis队列数据!
楼上几个说得对,排队,将所有请求排队,然后挨个处理。相当于把并发请求(多线程)转换成单线程,但是挨个处理会不会很慢,这个简直是一定的。
具体会涉及到以下几个细节:
用队列,楼上已经讲了
用缓存,你自己已经提到了,不过这里需要注意的是,如果对性能有很高要求,那么应当使用内存内的缓存,而不是用redis,memcached之类的外部缓存,因为与这些外部缓存通信有IO开销,这个要尽量避免
缓存和数据库的数据保持一致,这个比较难,在高并发情况下我们不会同步的去修改数据库,而是将数据库的操作延异步化,从而做到最终一致
数据库写入的时候要做优化,否则在大量交易的情况下将结果从缓存中写入数据库都要花大半天这个也太不合理了
试试这个呢 rabbitmq
说的好像12306不是排队似的,但是12306是超大并发,而且商品是不断变化中,1张上海到北京的票呗卖了南京到徐州,就变成3张票啦,退票的话,还要验证是否能拼接为更大的一张票。。。。。
适合自己就好,不要搞大新闻
对,用消息队列比较好,如果系统内暂时还没有接入消息队列的话,我们可以曲线救国:
比方说10点10分开始抢票,从10点10分开始,把每一个请求的用户id全部记录到redis里面或者是数据库里面,然后按时间倒序排序,取第一个就OK了
12306 的并发量并没有你们想象的那么大,每次消费的最大并发量不超过一个车次的总票数。一辆列车总乘客数量最多也就几百人。也就是说最大并发量也就几百个。 那不同的车次的锁是分开的,也就是说直接锁车次就够了。用不着搞队列、缓存这么麻烦。
我说一个和楼上都不同的观点,那就是real time programming,每个线程都有自己的priority,scheduler用SCHED_RR,也可以设置mutex属性,得到mutex的线程有相应的priority。不同priority的线程是在不同的FIFO队列里,虽然不能做到一定是先到先得,但是总体样本来说,是先到先得。