redis的watch和构建的细粒度锁对比测试

该博客通过对比测试分析了在Redis中,使用Watch命令与构建细粒度锁在处理频繁修改集合场景下的性能差异。测试结果显示,细粒度锁在性能上优于Watch,但加锁操作存在较高成本,平均需要3次请求。此外,测试环境对性能影响显著,虚拟机环境下性能仅为直连Redis的四分之一,暗示了进一步的优化潜力。
摘要由CSDN通过智能技术生成

本测试参考了《redis实战》中的例子

依赖

		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
			<version>2.9.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
			<version>2.0.6.RELEASE</version>
		</dependency>

初始化代码

	private static StringRedisTemplate redisTemplate = new StringRedisTemplate();
	
	private static JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
	
	static {
		//配置
		redisConnectionFactory.setHostName("192.168.2.128");
		redisConnectionFactory.setPort(6379);
		redisConnectionFactory.setUsePool(true);
		JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
		jedisPoolConfig.setMinIdle(50);
		jedisPoolConfig.setMaxIdle(50);
		jedisPoolConfig.setMaxTotal(50);
		redisConnectionFactory.setPoolConfig(jedisPoolConfig);//用jedis的默认值初始化8个连接
		redisTemplate.setConnectionFactory(redisConnectionFactory);
		redisTemplate.setEnableTransactionSupport(true);//开启redis事务
		redisTemplate.afterPropertiesSet();
	}

main方法中代码

	public static void main(String[] args) throws InterruptedException {
		
		//测试 
		String[] sellers = new String[]{"小东1","小东2","小东3","小东4","小东5"} ;
		String[] consumers = new String[] {"小马1","小马2","小马3","小马4","小马5"};
		for(String seller:sellers) {
			System.out.println("seller:"+seller);
			Runnable selling = new Runnable() {

				@Override
				public void run() {
					sell(seller);
				}
				
			};
			new Thread(selling).start();
		}
		
		for(String consumer:consumers) {
			System.out.println("consumer:"+consumer);
			Runnable buying = new Runnable() {

				@Override
				public void run() {
					// TODO Auto-generated method stub
					buy(consumer);
				}
				
			};
			new Thread(buying).start();
		}
		
		//验证多个包裹里面是否有重复商品
		Thread.sleep(12*1000);
		Collection<String> inventory = new HashSet<String>();
		//inventory.add("inventory:小马1");
		inventory.add("inventory:小马2");
		inventory.add("inventory:小马3");
		inventory.add("inventory:小马4");
		inventory.add("inventory:小马5");
		//redisTemplate.opsForSet().add("inventory:sum", "");
		redisTemplate.opsForSet().unionAndStore("inventory:小马1", inventory, "inventory:sum");
		
	}

卖家代码

总共一次请求

	/**
	 * @param seller
	 * @description 小东,有数不尽的商品可以卖,而且不要钱
	 * 
	 */
	public static void sell(String seller) {
		int i=0;
		//只卖十秒
		long end = System.currentTimeMillis() + 10*1000;
		while(System.currentTimeMillis()<end) {
			i++;
			redisTemplate.opsForZSet().add("market", seller+":"+i, i);
		}
		System.out.println("selling完毕:"+seller);
	}

买家代码

用watch总共3次请求,用细粒度锁总共6次请求

	/**
	 * @param consumer
	 * @description 小马,有数不尽的钱购物,随便花
	 * 
	 */
	public static void buy(String consumer) {
		//只买十秒
		long end = System.currentTimeMillis() + 10*1000;
		
		// 使用redis的watch加锁(乐观锁)
		while(System.currentTimeMillis()<end) {
			redisTemplate.watch("market");
			Set<String> buyItems = redisTemplate.opsForZSet().range("market", 0, 0);
			redisTemplate.multi();
			for(String item:buyItems) {
				redisTemplate.opsForZSet().remove("market", item);
				redisTemplate.opsForSet().add("inventory:"+consumer, item);
			}
			redisTemplate.exec();
		}
		
		/*
		//构建的细粒度锁
		while(System.currentTimeMillis()<end) {
			Random random = new Random();
			//生成一个200以内的随机数,防止多个买家总是购买同一个商品
			int nextInt = random.nextInt(200);
			
			Set<String> buyItems = redisTemplate.opsForZSet().range("market", nextInt, nextInt);
			for(String item:buyItems) {
				//对商品加锁
				if(acquireLock(item)) {
					Long rank = redisTemplate.opsForZSet().rank("market",item);
					//判断商品是否还存在市场中
					if(rank!=null) {
						redisTemplate.multi();
						redisTemplate.opsForZSet().remove("market", item);
						redisTemplate.opsForSet().add("inventory:"+consumer, item);
						redisTemplate.exec();
						//释放锁
						releaseLock(item);
					}
				}
			}
		}
		*/
		System.out.println("consuming完毕:"+consumer);
	}

细粒度锁加锁代码

	/**
	 * @param item
	 * @return true为获取商品锁成功,false为失败
	 * @throws InterruptedException 
	 */
	public static boolean acquireLock(String item) {
		Boolean lock = false;
		try {
			lock = redisTemplate.opsForValue().setIfAbsent("itemLock:"+item, "lock");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			//防止系统奔溃导致锁无法释放,锁有效期10s
			if(lock) {
				redisTemplate.expire("itemLock:"+item, 10, TimeUnit.SECONDS);
			}
		}
		return lock;
	}

细粒度锁释放锁代码

	/**
	 * @param item
	 * @return true为成功释放锁,false为失败
	 * 
	 */
	public static boolean releaseLock(String item) {
		return redisTemplate.delete("itemLock:"+item);
	}

测试结果

在这里插入图片描述

redis性能测试

$ redis-benchmark.exe -h 192.168.2.128 -p 6379 -c 1 -q
PING_INLINE: 2632.62 requests per second
PING_BULK: 2575.06 requests per second
SET: 2455.31 requests per second
GET: 2606.75 requests per second
INCR: 2515.03 requests per second
LPUSH: 2479.91 requests per second
RPUSH: 2477.27 requests per second
LPOP: 2458.94 requests per second
RPOP: 2474.82 requests per second
SADD: 2436.29 requests per second
SPOP: 2448.46 requests per second
LPUSH (needed to benchmark LRANGE): 2409.93 requests per second
LRANGE_100 (first 100 elements): 2252.46 requests per second
LRANGE_300 (first 300 elements): 1274.50 requests per second
LRANGE_500 (first 450 elements): 846.59 requests per second
LRANGE_600 (first 600 elements): 675.57 requests per second
MSET (10 keys): 2390.40 requests per second

总结

在对redis中频繁修改的集合加锁时,构建细粒度锁比使用watch能够大幅提高性能!加锁需要一定成本,在本次测试中,加锁操作(成功时)需要耗费三次请求!

** 性能对比时,也发现一个问题,在本机连虚拟机测redis性能如上图,但是在虚拟机直接测redis性能几乎是上面结果的四倍,说明优化空间还很大! **

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值