第15课:高并发数据操作

高并发情况下,读一般没有什么问题,主要是在写上,写分为insert和update,下面我们分开详述

Insert  操作
为了不入库重复数据,我们经常在insert数据的时候,需要先查询数据是否存在,正常情况没有问题,但是如果并发一旦高起来
会出现多个进程同时读同时写,导致入库重复数据
这个时候的解决方案
1.数据库设置唯一索引,或者联合唯一索引,保证入库数据的唯一性。这个方案是最简单高效的。
2.如果因为业务原因不能设置唯一索引,比如我发优惠券,有些券是每个人只能领一张,有些券每个人可领多张
针对每人只能领一张这种场景,可以使用分布式锁来解决问题
分布式锁:
	1.	互斥性:在任意一个时刻,只有一个客户端持有锁。
	2.	无死锁:即便持有锁的客户端崩溃或者其他意外事件,锁仍然可以被获取。
	3.	容错:只要大部分Redis节点都活着,客户端就可以获取和释放锁
分布式锁的常用实现:
	1.	数据库
	2.	Memcached(add命令)
	3.	Redis(setnx命令)
	4.	Zookeeper(临时节点)
数据库:
可以新建一个表,表里只有一2个字段,一个主键,一个only字段,only设置唯一索引,值为可唯一标识业务数据的字段组合
如果这样的组合无法给出,就再加个时间,精确到分钟,拼成一个唯一的字符串。
数据入库的时候用事务,先写这个唯一表然后数据入库,如果唯一表写入失败,会抛出异常
Memcached(Redis与之类似,不再举例)
用一个Java的例子来说明吧:
public void doTestForInsert(Order order){
       String key=null;
       try{
           Order orderInDB = orderDao.findByName(order.getOrderNo());
           //查DB,如果数据库已经有则抛出异常
           if(null != orderInDB)
              throw new Exception("the order has been exist!");
           key=order.getOrderNo();
           //插缓存,原子性操作,插入失败 表明已经存在
           if(!MemcacheUtil.add(key, order, MemcacheUtil.getExpiry(UNIT.MINUTE, 1)))
              throw new Exception("the order has been exist!");
           //插DB
           orderDao.save(order);
       }catch (Exception e) {
           e.printStackTrace();
       }finally{
           MemcacheUtil.del(key);
       }
    }
不过这段代码有个逻辑问题,删除缓存key的时候应该有个判断,因为是高并发,有可能在删除的时候,删除的是别人生成的key(虽然这种可能性非常低,因为key有效期是1分钟,一分钟还执行不完也是厉害了)聪明的你来优化吧

Update操作
update操作处理起来更加的容易,成熟方案也更多
1.使用悲观锁
主要是用select … for update来锁定数据,缺点也很明显,不能够支持足够的并发

2.使用乐观锁
在表里增加一个version版本号字段,每次更新就把版本号+1,如果update操作version大于数据库中已存version就更新,反之,证明是有问题,就不更新,此方案是大部分人选择的一个方案

打赏  如对你有帮助,请我喝杯咖啡吧!