对于一个互联网平台来讲,高并发是常常会遇到的场景。最有表明性的好比秒杀和抢购。高并发会出现三个特色:html
一、高并发读取前端
二、高并发写入(一致性)java
三、出现超卖问题mysql
前端如何应对?nginx
一、缓存静态数据,例如图片,html页面,js等redis
二、搭建负载均衡集群,目前采用较多的为nginxsql
三、进行ip限制,限制同一个ip单位时间内发起的请求数量。或者创建ip黑名单,避免恶意攻击数据库
四、考虑系统降级。好比当达到系统负载的时候返回一个静态处理页面后端
后端如何应对?缓存
一、采用mysql读写分离,可是当高并发的时候mysql性能会下降。 通常来讲,MySQL的处理性能会随着并发thread上升而上升,可是到了必定的并发度以后会出现明显的拐点,以后一路降低,最终甚至会比单thread的性能还要差。好比加减库存的操做,一般并发量不高的作法为:update xxx set count=count-xx where curcount>xx;这样能够充分利用mysql的事务锁来避免出现超卖的状况。可是并发量上了后,会由于排他锁等待而大大下降性能。
二、采用redis数据库,前置到mysql。思路以下:
2.1系统启动后,初始化sku信息到redis数据库,记录其可用量和锁定量
2.2使用乐观锁,采用redis的watch机制。逻辑为:
1.定义门票号变量,设置初始值为0。watchkey
2.watch该变量,watch(watchkey);
3.使用redis事务加减库存。首先获取可用量和抢购量比较,若是curcount>buycount,那么正常执行减库存和加锁定量操做:
Redis用法详细说明
一、Pipeline
利用pipeline的方式从client打包多条命令一块儿发出,不须要等待单条命令的响应返回,而Redis服务端会处理完多条命令后会将多条命令的处理结果打包到一块儿返回给客户端。因此pipeline适合批处理做业能够提高效率如:
- public static void testMget() {
- Jedis jedis = RedisCacheClient.getInstrance().getClient();
- Set<String> keys = jedis.keys("cvfeedBackHandl_*");
- List<String> result = Lists.newArrayList();
- long t1 = System.currentTimeMillis();
- for (String key : keys) {
- result.add(jedis.get(key));
- }
- for (String src : result) {
- System.out.println(src);
- }
- System.out.println(System.currentTimeMillis() - t1);
- }
-
- public static void testPipline() {
- Jedis jedis = RedisCacheClient.getInstrance().getClient();
- Set<String> keys = jedis.keys("cvfeedBackHandl_*");
- List<Object> result = Lists.newArrayList();
- Pipeline pipelined = jedis.pipelined();
- long t1 = System.currentTimeMillis();
- for (String key : keys) {
- pipelined.<span style="font-family: Arial;">get</span>("testabcd");
- }
- result = pipelined.syncAndReturnAll();
- for (Object src : result) {
- System.out.println(src);
- }
- System.out.println(System.currentTimeMillis() - t1);
- }
如第一个方法执行的时间是82ms
第二个方法执行的时间是9ms
注意:pipeline和事务都是异步调用返回结果的,即并非等待每条命令执行完立马返回结果而是等待全部命令执行完以后再返回结果。pipelined.syncAndReturnAll()返回的是参与打包执行的每条命令的结果。若是上面改为:
- for (String key : keys) {
- pipelined.get(key);
- pipelined.del("testabcd");
- }
返回结果将是
- "test1"
- 1
- "test2"
- 0
- "test2"
- 0
- "test4"
- 0
- "test5"
- 0
二、事务
事务是保证事务内的全部命令是原子操做,通常配合watch使用,事务的执行结果和pipeline同样都是采用异步的方式获取结果,multi.exec()提交事务,若是执行成功,其返回的结果和pipeline同样是全部命令的返回值,若是事务里面有两个命令那么事务的exec返回值会把两个命令的返回值组合在一块儿返回。若是事务被取消返回null。
三、watch
通常是和事务一块儿使用,当对某个key进行watch后若是其余的客户端对这个key进行了更改,那么本次事务会被取消,事务的exec会返回null。jedis.watch(key)都会返回OK
eg:
- public static void testWach(){
- Jedis jedis = RedisCacheClient.getInstrance().getClient();
- String watch = jedis.watch("testabcd");
- System.out.println(Thread.currentThread().getName()+"--"+watch);
- Transaction multi = jedis.multi();
- multi.set("testabcd", "23432");
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- List<Object> exec = multi.exec();
- System.out.println("---"+exec);
- jedis.unwatch();
- }
- public static void testWatch2(){
- Jedis jedis = RedisCacheClient.getInstrance().getClient();
- String watch = jedis.watch("testabcd2");
- System.out.println(Thread.currentThread().getName()+"--"+watch);
- Transaction multi = jedis.multi();
- multi.set("testabcd", "125");
- List<Object> exec = multi.exec();
- System.out.println("--->>"+exec);
- }
Thread-2--OK
Thread-0--OK
--->>[OK]
---null//事务取消
四、事务与管道
当对某个key进行watch时,若是其余的客户端对key进行了更改事务能够作到取消事务操做可是管道不能够