not much to sell.
Seconds to kill the system need to ensure that things do not sell more, the key is in the inventory of multiple clients to reduce the operation, must be locked. Watch in Redis is just one thing to achieve. First we need to get the current inventory, only the food in the inventory is less than the number of shopping carts to reduce inventory. In the case of high concurrency there will be a time when the query inventory is enough, but the next time another thread orders, the inventory to reduce operations, just less than the last thread of the shopping cart number. The current state is not able to order success, because the inventory is not enough, but the first thread still think the quantity is enough, the inventory is reduced operations, resulting in a negative inventory situation. How to avoid.
Watch in Redis can monitor the data before the transaction, and if the data changes before the transaction executes, the transaction does not execute. Just to meet our requirements. Read a lot of code, the watch function is not very understanding, because many of the online posts are not explicitly pointed out that the multi-client (after understanding the discovery or write), so do not understand can see the following example, is written in Java. The following code can guarantee that the inventory is not sold much.
Set a variable with a key of MyKey and a value of 100 in Redis
package com.test.redis;
import java.util.List;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
public class RedisLocal {
public static void main (String [] args) {
RedisUtil.getJedis (). Set ("mykey", "100");
new MyThread (). start ();
new MyThread (). start ();
new MyThread (). start ();
new MyThread (). start ();
new MyThread (). start ();
new MyThread (). start ();
new MyThread (). start ();
new MyThread (). start ();
new MyThread (). start ();
new MyThread (). start ();
}
}
class MyThread extends Thread {
Jedis jedis = null;
@Override
public void run () {
// TODO Auto-generated method stub
while (true) {
System.out.println (Thread.currentThread (). GetName ());
jedis = RedisUtil.getJedis ();
try {
int stock = Integer.parseInt (jedis.get ("mykey"));
if (stock> 0) {
jedis.watch ("mykey");
Transaction transaction = jedis.multi ();
transaction.set ("mykey", String.valueOf (stock-1));
List <Object> result = transaction.exec ();
if (result == null || result.isEmpty ()) {
System.out.println ("Transaction error ..."); // It may be that the watch-key is modified externally, or the data operation is rejected
}
} else {
System.out.println ("Inventory is 0");
break;
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace ();
RedisUtil.returnResource (jedis);
} finally {
RedisUtil.returnResource (jedis);
}
}
}
}
does not work for redis transactions, because all commands are cached by Redis before the EXEC command, and the value of balance is not reached at all. That's similar to how a transaction that needs to be based on a value that already exists is implemented in Redis. The answer is the Watch command:
Redis.watch (' balance ')
balance = redis.get (' balance ')
if (Balance < amttosubtract) {
redis.unwatch ()
} else {
redis.multi ()
Redis.decrby (' balance ', amttosubtract)
Redis.incrby (' Debt ', amttosubtract)
redis.exec ()
}
In layman's words, the Watch command marks a key, and if a key is marked, the transaction will fail if the key is modified by someone else before committing the transaction, which can usually be done again in the program. Like the example above, first mark the key balance, and then check whether the balance is sufficient, not enough to cancel the mark, do not make a deduction, enough to start a transaction to update the operation, if the key balance by others during this period, the commit transaction (EXEC) will be error, This type of error can usually be caught in a program and re-executed again until it succeeds.
A key difference between a Redis transaction that does not support rollback and database transactions is that Redis transactions do not roll back after an error occurs during execution. After the EXEC command, Redis server starts executing a cached command one at a time, and the previous command will not be rolled back if a command execution fails.
Redisutil
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public final class RedisUtil {
// Redis server IP
private static String ADDR = "192.168.1.8";
// Redis port number
private static int PORT = 6379;
// access password
private static String AUTH = "cl";
// The maximum number of available connection instances, the default value is 8;
// If the value is -1, it means unlimited; if maxActive jedis instances have been allocated to the pool, then the state of the pool is exhausted.
private static int MAX_ACTIVE = 1024;
// Control how many jedis instances in a pool are idle (default). The default value is also 8.
private static int MAX_IDLE = 200;
// The maximum time to wait for an available connection, in milliseconds. The default value is -1, which means that it will never time out. If the waiting time is exceeded, JedisConnectionException is directly thrown;
private static int MAX_WAIT = 10000;
private static int TIMEOUT = 10000;
// Whether to validate before boring a jedis instance; if true, the jedis instances obtained are all available;
private static boolean TEST_ON_BORROW = true;
private static JedisPool jedisPool = null;
/ **
* Initialize Redis connection pool
* /
static {
try {
JedisPoolConfig config = new JedisPoolConfig ();
config.setMaxIdle (MAX_IDLE);
config.setTestOnBorrow (TEST_ON_BORROW);
jedisPool = new JedisPool (config, ADDR, PORT, TIMEOUT, AUTH);
} catch (Exception e) {
e.printStackTrace ();
}
}
/ **
* Get Jedis instance
*
* @return
* /
public synchronized static Jedis getJedis () {
try {
if (jedisPool! = null) {
Jedis resource = jedisPool.getResource ();
return resource;
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace ();
return null;
}
}
/ **
* Release jedis resources
*
* @param jedis
* /
public static void returnResource (final Jedis jedis) {
if (jedis! = null) {
jedisPool.returnResource (jedis);
}
}
}