定义:持久化的高性能key-value内存型数据库
应用场景:数据缓存、聊天系统、访问量统计、消息队列、分布式锁等等
优势对比:
•轻量级 : redis非常轻量(源码仅1.5M),就个人而言我更喜欢小而轻的东西,对于一些尽量想要完美而庞大的东西来说我是非常反感的,
有些东西尽可能的追求完美反而忽略了最本质的东西或者说是产品设计的初衷
•持久化 : redis 提供了非常有好的持久化支持,保证数据完整性
•高性能 : redis 是基于内存型的中间件,内存型意味着更优秀的性能(实际测试,单机单线程内网环境下可完成10000次/s的写操作),中见间的特性使他对分布式有着很好的支持
•丰富的数据类型和命令 : redis共有String、List、Hash、Set、SortSet 5种数据类型和丰富的操作命令,这些特性使redis除了基本功能之外,可以应用于多种场景
•丰富的客户端语言支持
•使用简单
redis应用之分一 分布式锁实现:
原理:分布式锁主要应用了redis 的 setnx 命令特性,setnx key value,将key的值设为value,当key已存在时则不做处理,我们利用其不能同时赋值的特性实现锁的效果
代码实现:获取锁
1 /** 2 * 获取分布式锁 DLock 3 * @param key 锁标志 4 * @param holder 锁持有者(一般为uuid,唯一标识当前锁的持有者) 5 * @param timeout 获取锁的超时时间 毫秒 6 * @return 7 */ 8 public static boolean getDLock(String key, String holder, long timeout){ 9 Jedis jedis = DRJedis.getJedisPool(); 10 //设置获取锁的超时时间 11 long end = System.currentTimeMillis() + timeout; 12 while (System.currentTimeMillis() < end){ 13 if (jedis.setnx(LOCK_PREFIX + key, holder) == 1) return true; 14 try { 15 Thread.sleep(10); 16 } catch (InterruptedException e) { 17 log.error("获取 DLock 异常", e); 18 return false; 19 } 20 } 21 log.error("获取 DLock 超时"); 22 return false; 23 }
这个方法表示如果setnx成功,表示当前没有别的线程持有key这个锁,则获取锁,同时阻止其他线程再次获取这个锁,如果获取所失败,则在timeout时间范围内尝试尝试重新获取,否则返回获取锁失败
释放锁:释放锁即删除key操作,以便于其它的线程可以setnx
1 /** 2 * 释放锁 3 * @param key 锁标志 4 * @param holder 锁持有者(一般为uuid,唯一标识当前锁的持有者) 5 */ 6 public static void releaseDLock(String key, String holder){ 7 Jedis jedis = DRJedis.getJedisPool(); 8 while (true){ 9 try { 10 jedis.watch(LOCK_PREFIX + key); 11 String value = jedis.get(LOCK_PREFIX + key); 12 if(value != null && value.equals(holder)){// 保证释放的是当前持有者的锁 13 Transaction tran = jedis.multi(); 14 jedis.del(LOCK_PREFIX + key); 15 tran.exec(); 16 } 17 jedis.unwatch(); 18 break; 19 }catch (Exception e){ 20 log.error("释放锁异常", e); 21 break; 22 } 23 } 24 }
这个方法表示释放锁,通过事务保证同时时候放锁操作
redis应用之分二 MQ实现:
原理:mq是基于redis list 列表数据类型的实现,右端入队,左端出队的先入先出特性
生产消息代码
1 /** 2 * 生产消息 3 * @param key 消息key 4 * @param message 消息体,支持批量生产消息,至少生产一条消息 5 */ 6 public static void product(String key, String... message){ 7 try{ 8 DRJedis.getJedisPool().rpush(MQ_PREFIX + key, message); 9 }catch(Exception e){ 10 log.error("jedis product message", e); 11 } 12 }
rpush 从右端推入队列
消费消息:
1 /** 2 * 消费消息 3 * @param key 消息key 4 * @param customer 消息消费业务,需要实现Customer接口的customer方法进行业务处理 5 */ 6 public static void customer(String key, ICustomer customer){ 7 try{ 8 Jedis jedis = DRJedis.getJedisPool(); 9 while (true){ 10 //弹出消息 11 List<String> messages = jedis.blpop(0, MQ_PREFIX + key); 12 if (messages.size() >= 2){ 13 try { 14 //消费消息 15 customer.customer(messages.get(0), messages.get(1)); 16 }catch (RollbackException e){ 17 //消息消费失败则要重新生产消息,并有左侧压入队列,保证下次第一个被消费 18 jedis.lpush(messages.get(0), messages.get(1)); 19 } 20 } 21 } 22 }catch(Exception e){ 23 log.error("jedis customer message", e); 24 } 25 }
blpop 从左端弹出消息,如果队列为空则阻塞消息等待,此方法使用了while(true)无限循环的方式模拟监听器,以便于及时的消费消息,ICustomer 定义具体的消费业务,并抛出RollbackException异常表示,消息消费失败,需要回滚,则重新从左端推入队列,保证下次被第一个消费
1 /** 2 * Created by xiao on 2016/5/20. 3 */ 4 public interface ICustomer { 5 6 void customer(String key, String message) throws RollbackException; 7 8 }
消费消息的业务需要实现ICustomer的customer方法,消费消息
1 /** 2 * 回滚异常 3 */ 4 public class RollbackException extends Exception { 5 6 public RollbackException(String message) { 7 super(message); 8 } 9 10 public RollbackException() { 11 super(); 12 } 13 14 public RollbackException(String message, Throwable cause) { 15 super(message, cause); 16 } 17 18 public RollbackException(Throwable cause) { 19 super(cause); 20 } 21 22 protected RollbackException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 23 super(message, cause, enableSuppression, writableStackTrace); 24 } 25 }