高并发场景之一般解决方案
2017-04-02 02:13
260 查看
今天我们来了解一下一些高并发的业务场景如何做到数据一致性的。
2、初始值:ID=1,Total = 0
3、现要求每一次客户端请求Total + 1
2.1 按要求,正常情况下应该输出:100
2.2 运行结果
貌似没有问题。
3.2 我们预期应该是要输出200
3.3 运行结果
很遗憾,却是150,造成这个结果的原因是这样的:T1、T2获取Total(假设此时值为10),T1更新一次或多次后,T2才更新(Total:10)
这就造成之前T1提交的被覆盖了
3.4 如何避免呢?一般做法加锁就可以了,如Run改成如下
3.5 再次运行
4.2 Main改一下
4.3 预期输出200,看运行结果
4.4 集群环境下测试,2台机器
有问题!最后运行的那台机器居然是379,数据库也是379。
这超出了我们的预期结果,看来即便加锁,对于高并发场景也是不能解决所有问题的
5.2 Main也要改改
5.3 在集群里再试试,2个都是400,没有错(因为每个站点开了2个线程)
可以看到数据完全正确!
一、场景:
1、有数据表:ConCurrency,CREATE TABLE [dbo].[ConCurrency]( [ID] [int] NOT NULL, [Total] [int] NULL )
2、初始值:ID=1,Total = 0
3、现要求每一次客户端请求Total + 1
二、单线程
static void Main(string[] args) { ... new Thread(Run).Start(); ... } public static void Run() { for (int i = 1; i <= 100; i++) { var total = DbHelper.ExecuteScalar("Select Total from ConCurrency where Id = 1", null).ToString(); var value = int.Parse(total) + 1; DbHelper.ExecuteNonQuery(string.Format("Update ConCurrency Set Total = {0} where Id = 1", value.ToString()), null); Thread.Sleep(1); } }
2.1 按要求,正常情况下应该输出:100
2.2 运行结果
貌似没有问题。
三、多线程并发
3.1 Main改一下static void Main(string[] args) { ... new Thread(Run).Start(); new Thread(Run).Start(); ... }
3.2 我们预期应该是要输出200
3.3 运行结果
很遗憾,却是150,造成这个结果的原因是这样的:T1、T2获取Total(假设此时值为10),T1更新一次或多次后,T2才更新(Total:10)
这就造成之前T1提交的被覆盖了
3.4 如何避免呢?一般做法加锁就可以了,如Run改成如下
public static void Run() { for (int i = 1; i <= 100; i++) { lock (resource) { var total = DbHelper.ExecuteScalar("Select Total from ConCurrency where Id = 1", null).ToString(); var value = int.Parse(total) + 1; DbHelper.ExecuteNonQuery(string.Format("Update ConCurrency Set Total = {0} where Id = 1", value.ToString()), null); } Thread.Sleep(1); } }
3.5 再次运行
四、用队列实现
4.1、定义队列static ConcurrentQueue<int> queue = new ConcurrentQueue<int>();
/// <summary>生产者</summary> public static void Produce() { for (int i = 1; i <= 100; i++) { queue.Enqueue(i); } } /// <summary>消费者</summary> public static void Consume() { int times; while (queue.TryDequeue(out times)) { var total = DbHelper.ExecuteScalar("Select Total from ConCurrency where Id = 1", null).ToString(); var value = int.Parse(total) + 1; DbHelper.ExecuteNonQuery(string.Format("Update ConCurrency Set Total = {0} where Id = 1", value.ToString()), null); Thread.Sleep(1); } }
4.2 Main改一下
static void Main(string[] args) { ... new Thread(Produce).Start(); new Thread(Produce).Start(); Consume(); ... }
4.3 预期输出200,看运行结果
4.4 集群环境下测试,2台机器
有问题!最后运行的那台机器居然是379,数据库也是379。
这超出了我们的预期结果,看来即便加锁,对于高并发场景也是不能解决所有问题的
五、分布式队列
5.1 解决上边问题可以用分布式队列,这里用的是redis队列/// <summary>生产者</summary> public static void ProduceToRedis() { using (var client = RedisManager.GetClient()) { for (int i = 1; i <= 100; i++) { client.EnqueueItemOnList("EnqueueName", i.ToString()); } } } /// <summary>消费者</summary> public static void ConsumeFromRedis() { using (var client = RedisManager.GetClient()) { while (client.GetListCount("EnqueueName") > 0) { if (client.SetValueIfNotExists("lock", "lock")) { var item = client.DequeueItemFromList("EnqueueName"); var total = DbHelper.ExecuteScalar("Select Total from ConCurrency where Id = 1", null).ToString(); var value = int.Parse(total) + 1; DbHelper.ExecuteNonQuery(string.Format("Update ConCurrency Set Total = {0} where Id = 1", value.ToString()), null); client.Remove("lock"); } Thread.Sleep(5); } } }
5.2 Main也要改改
static void Main(string[] args) { ... new Thread(ProduceToRedis).Start(); new Thread(ProduceToRedis).Start(); Thread.Sleep(1000 * 10); ConsumeFromRedis(); ... }
5.3 在集群里再试试,2个都是400,没有错(因为每个站点开了2个线程)
可以看到数据完全正确!
相关文章推荐
- 高并发高性能场景(抢购、秒杀、抢票、限时竞答)解决方案
- java服务器集群高并发场景下发布导致load高的解决方案
- 处理高并发的一般解决方案
- 分布式、高并发、高性能场景(抢购、秒杀、抢票、限时竞答)数据一致性解决方案
- 万人抢红包高并发业务场景模拟常规解决方案
- java服务器集群高并发场景下发布导致load高的解决方案
- java并发值多线程同步业务场景以及解决方案
- 高并发场景下的缓存+数据库双写不一致问题分析与解决方案
- 【高并发解决方案】RabbitMQ的几种典型使用场景
- 淘宝下单高并发解决方案
- 企业高并发的成熟解决方案(一) ----负载均衡服务器
- MySQL在并发场景下的问题及解决思路
- 数据库并发解决方案
- 手把手让你实现开源企业级web高并发解决方案(lvs+heartbeat+varnish+nginx+eAccelerator+memcached)续 推荐
- 高并发解决方案
- 【转】并发场景下的中断处理
- 探讨SQL Server并发处理存在就更新七种解决方案
- 转载:关于生成并发唯一性流水号的解决方案
- mysql 高并发环境的解决方案