在测试代码时发现一个问题:
要实现一个数据库表中user_id和手机号两个字段觉得数据的唯一性,所以我要先进行查询,如果数据存在,则不插入,数据不存在则插入数据。可是当前台打开多个页面时,同时触发这个操作之后,第一个访问来了判断没有数据便进行插入,但还没有插入完成时第二个访问也来了,此时查询还没有数据,便也插入,但这样一来就有两条重复数据了。
所以要需要控制一下这样的问题我找到一些方法:
1、 可以用sql语句进行控制,有插入的不允许有重复的值插入:
INSERT INTO marketing_vote (user_id, mobile_number, type)
SELECT '18210141111my', '18210141111', 'vote' FROM DUAL WHERE
NOT EXISTS (
SELECT
*
FROM
marketing_vote
WHERE
mobile_number = '18210141111my' and user_id='18210141111');
这样在插入的时候可以先时行判断,如果条件里的用户存在数据库表中,那么not exists里返加false,则条件不存在不会插入数据,反则会将数据插入到数据库表中。
2、 可以在表中利用唯一索引进行控制,这样不就可以插 入重复数据;
3、. 还可以在插入之前做一个删除操作,当然这种方法不是特别合理;
4、后经过测试,最终选择用redis锁的方式来确保一段代码的串行执行,redis 单进程单线程模式,采用队列模式将并发访问变成串行访问,且在多个请求在对redis进行访问的时间也不存在竞争关系 ;代码如下:
/**
setnx命令: set if not eXists 如果不存在,则Set的简写
隐藏的意思是:key存在的情况下,不操作redis内存;也就是返回值是0; Long result = jedis.setnx(key, value);
返回值result :设置成功,返回 1 。设置失败,返回 0
这种加锁的思路是,如果 key 不存在,将 key 设置为 value,如果 key 已存在,则 SETNX 不做任何动作.
expire命令:redis通过expire命令来设置key的过期时间。
语法:redis.expire(key, expiration)
**/
public static boolean getDLock(String key, String holder, long timeout){
Jedis jedis = DRJedis.getJedisPool();
long end = System.currentTimeMillis() + timeout;
while(System.currentTimeMillis() < end) {
try {
if(jedis.setnx(LOCK_PREFIX + key, holder).longValue() == 1L) {
jedis.expire(LOCK_PREFIX + key, 60);
return true;
}
Thread.sleep(100L);
} catch (Exception var11) {
log.error("获取 DLock 异常", var11);
} finally {
returnResource(jedis);
}
}
returnResource(jedis);
System.out.println("获取 DLock 超时");
return false;
}
public static Jedis getJedisPool()
{
int total = 1;
do
{
try
{
Jedis jedis = pool.getResource();
total = 1;
return jedis;
} catch (JedisConnectionException e) {
log.error("redis 链接失败已超过" + total + "次,请检查服务器...", e);
total++;
try {
Thread.sleep(1000L);
} catch (InterruptedException e1) {
log.error(e);
}
}
}
while (
total < 3);
log.error("redis 链接失败已超过3次,redis已失效");
return null;
}