使用Redisson模拟Redis单实例下的分布式锁处理业务数据
文字图片描述
模拟描述
多个进程同时处理业务数据,每处理一条业务数据,把业务数据中被处理的次数累加1,初始处理次数为0,SQL为update loan set deal_instance = #{deal_instance}, deal_count = deal_count + 1 where id = #{id}
。处理结束后,看是否存在被处理次数大于1的情况发生,若存在则表明业务数据被重复处理了,分布式锁不起作用。
模拟情况说明
-
MySQL数据库中有50000条业务数据需要处理
-
部署了4个进程(模拟生产上的4个集群实例)
预期结果
- 业务数据无重复处理的情况发生
结果说明
开始时间:01:50:17.400
结束时间:02:03:13.601
TPS:50000/(02:03:13-01:50:17之间的秒数)=64
无重复处理情况发生
代码
MainClass
package com.creasy.redis;
import com.creasy.dao.ILoanDao;
import com.creasy.pojo.Loan;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.lang.management.ManagementFactory;
import java.util.Random;
import java.util.concurrent.TimeUnit;
/**
* @author laicreasy
*/
public class MainLockTest {
private static final Logger log = LoggerFactory.getLogger(MainLockTest.class);
private static final long LEASE_TIME = 10;
public static void main(String[] args) throws InterruptedException, IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//Redis配置
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
RLock lock = redisson.getLock("myLock");
while (true){
//获取锁
boolean res = lock.tryLock(new Random().nextInt(10), LEASE_TIME, TimeUnit.SECONDS);
if (res) {
//模拟获得锁之后处理业务数据
try {
log.info("Get lock");
SqlSession session = sqlSessionFactory.openSession();
ILoanDao mapper = session.getMapper(ILoanDao.class);
Loan loan = mapper.queryLoanWithoutDeal();
if( loan == null ) {
break;
}
loan.setDeal_instance("PID:" + ManagementFactory.getRuntimeMXBean().getPid());
mapper.updateLoan(loan);
session.commit();
session.close();
} finally {
log.info("Release lock");
try{
//释放锁
lock.unlock();
} catch (Exception e){
log.error(e.getMessage(), e);
}
}
}
// TimeUnit.MILLISECONDS.sleep(new Random().nextInt(10));
}
redisson.shutdown();
}
}
SQL
-- 建表语句
DROP TABLE IF EXISTS `loan`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `loan` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`deal_instance` varchar(40) DEFAULT NULL,
`deal_count` int(10) DEFAULT '0',
`loan_amount` int(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=50003 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping routines for database 'usersys'
--
/*!50003 DROP PROCEDURE IF EXISTS `insert_loan_loop` */;
/*!50003 SET @saved_cs_client = @@character_set_client */ ;
/*!50003 SET @saved_cs_results = @@character_set_results */ ;
/*!50003 SET @saved_col_connection = @@collation_connection */ ;
/*!50003 SET character_set_client = utf8mb4 */ ;
/*!50003 SET character_set_results = utf8mb4 */ ;
/*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ;
/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
/*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ;
-- 插入50000条数据
DELIMITER ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `insert_loan_loop`()
BEGIN
DECLARE x INT;
SET x = 1;
WHILE x <= 50000 DO
insert into loan(loan_amount) values (x);
SET x = x + 1;
END WHILE;
END ;;
DELIMITER ;
具体代码可看:github