Redis除了提供key-value存储还提供了以结构化方式存储数据的语义,能够以同样的语义在程序中例如map/list等中存储信息。以下主要探讨redis的提供的一些功能,这些功能使得redis相对于数据存储更像是框架构建:
1、Real time message(实时消息)
Redis虽然不存储信息,但是支持消息的实时发送和接收,多个消息是以消息队列按顺序发送的。redis提供了publish/subscribe命令,发布订阅的简单示例如下:
/**
* 订阅者处理器
* @author fangcong on 2017/11/29.
*/
public class SubscriberProcessor implements Runnable {
private Subscriber subscriber = new Subscriber();
private Thread simpleThread;
public Thread getSimpleThread() {
return simpleThread;
}
public void setSimpleThread(Thread simpleThread) {
this.simpleThread = simpleThread;
}
/**
* 取消订阅
*/
public void unSubscribe() {
simpleThread.interrupt();
if (subscriber.isSubscribed()) {
subscriber.punsubscribe();
}
}
/**
* 启动线程
*/
public void subscribeProcessor() {
simpleThread = new Thread(this);
simpleThread.start();
}
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
RedisUtils.getResource().psubscribe(subscriber, "news*");
}
}
}
批量订阅以news开头的channel,RedisUtils.getResource()是获取Jedis对象,new JedisPool(new JedisPoolConfig(), "localhost").getRedource()
Subscriber是订阅者,监听发布者消息并输出信息,代码如下:
public class Subscriber extends JedisPubSub {
@Override
public void onMessage(String channel, String message) {
System.out.println("on message : " + channel + " value : " + message);
}
@Override
public void onPMessage(String pattern, String channel, String message) {
System.out.println("on pMessage : " + pattern + " channel : " + channel + " message : " + message);
}
@Override
public void onSubscribe(String channel, int subscribedChannels) {
System.out.println("onSubscribe : " + channel + " num : " + subscribedChannels);
}
@Override
public void onUnsubscribe(String channel, int subscribedChannels) {
System.out.println("onUnsubscribe : " + channel + " num " + subscribedChannels);
}
@Override
public void onPUnsubscribe(String pattern, int subscribedChannels) {
System.out.println("on pattern unSubscribe : " + pattern + " num " + subscribedChannels);
}
@Override
public void onPSubscribe(String pattern, int subscribedChannels) {
System.out.println("on pattern subscribe : " + pattern + " num " + subscribedChannels);
}
}
接下来新建发布者并发布消息到指定channel供订阅者获取消息,带main方法的Publisher类如下:
public class Publisher implements Runnable {
private String channel;
public Publisher(String channel) {
this.channel = channel;
}
@Override
public void run() {
Jedis jedis = RedisUtils.getResource();
jedis.publish(channel, "this is the message that sent by publisher");
}
/**
* 测试消息发布和消息订阅
* @param args
*/
public static void main(String[] args) throws InterruptedException {
SubscriberProcessor processor = new SubscriberProcessor();
processor.subscribeProcessor();
for (int i = 0; i < 5; i++) {
Publisher publisher = new Publisher("news" + i);
Thread thread = new Thread(publisher);
thread.start();
thread.join();
}
try {
processor.getSimpleThread().sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
processor.unSubscribe();
}
}
输出信息如下:
on pattern subscribe : news* num 1
on pMessage : news* channel : news0 message : this is the message that sent by publisher
on pMessage : news* channel : news1 message : this is the message that sent by publisher
on pMessage : news* channel : news2 message : this is the message that sent by publisher
on pMessage : news* channel : news3 message : this is the message that sent by publisher
on pMessage : news* channel : news4 message : this is the message that sent by publisher
on pattern unSubscribe : news* num 0
2、Pipeline
管道提供更快速的执行机制,所有的命令组成命令块在服务端执行并以队列返回结果集。假定命令的发送、执行和响应所需的时间均为x秒,则批量执行5个命令的时间为3*5x秒,如果使用管道以命令块执行,则发送和响应时间和为2x,执行时间为5x,总耗时7x。因此批量执行命令时,以Pipeline方式能大大节省时间。pipeline保证原子性但仅仅是多个命令都使用独立的连接但是会在一个响应块中返回结果集。
使用pipeline和不使用pipeline的时间对比,注意sleep时间要能保证上一个操作执行完毕,且注意操作完成清除测试数据释放内存:
public class PipelineCommandsTest {
Jedis jedis = RedisUtils.getResource();
long starttime_withoutpipeline = 0;
long endtime_withoutpipeline = 0;
long starttime_withpipeline = 0;
long endtime_withpipeline = 0;
/**
* 是否使用pipeline花费时间对比
*/
private void getStats() {
System.out.println(" time taken for test without pipeline " +
(endtime_withoutpipeline - starttime_withoutpipeline));
System.out.println(" time taken for test with pipeline " +
(endtime_withpipeline - starttime_withpipeline));
}
/**
* 不使用执行10 * 100次hash值存取操作
*/
private void checkWithoutPipeline() {
starttime_withoutpipeline = System.currentTimeMillis();
for (int keys = 0; keys < 10; keys++) {
for (int nv = 0; nv < 100; nv++) {
jedis.hset("keys" + keys, "name" + nv, "value" + nv);
}
for (int nv = 0; nv < 100; nv++) {
jedis.hget("keys" + keys, "name" + nv);
}
}
endtime_withoutpipeline = System.currentTimeMillis();
//清除所有数据
jedis.flushDB();
}
/**
* 使用pipeline执行10 * 100次hash值存取操作
*/
private void checkWithPipeline() {
starttime_withpipeline = System.currentTimeMillis();
for (int keys = 0; keys < 10; keys++) {
Pipeline pipeline = jedis.pipelined();
for (int nv = 0; nv < 100; nv++) {
pipeline.hset("keys" + keys, "name" + nv, "value" + nv);
}
List<Object> results = pipeline.syncAndReturnAll();
for (int nv = 0; nv < results.size(); nv++) {
results.get(nv);
}
}
endtime_withpipeline = System.currentTimeMillis();
jedis.flushDB();
}
public static void main(String[] args) throws InterruptedException {
PipelineCommandsTest test = new PipelineCommandsTest();
test.checkWithoutPipeline();
Thread.currentThread().sleep(10000);
test.checkWithPipeline();
Thread.currentThread().sleep(10000);
test.getStats();
}
}
3、Transaction
Redis提供松散的事务处理,与传统的RDBMS事务以begin开始,commit/rollback不同,Redis以multi命令开启事务,exec命令执行事务。
public static class TransactionCommand implements Runnable {
@Override
public void run() {
Jedis jedis = RedisUtils.getResource();
long start = System.currentTimeMillis();
Transaction transaction = jedis.multi();
for (int nv = 0; nv < 300000; nv++) {
transaction.sadd("keys-1", "name" + nv);
}
transaction.exec();
Set<String> data = jedis.smembers("keys-1");
System.out.println("The return value nv1 after tx [ " + data.size() + " ]");
System.out.println("The time taken for executing client "+ (System.currentTimeMillis()-start));
RedisUtils.setResource(jedis);
}
}
4、Scripting:Redis支持LUA脚本
5、Connection management
默认情况下,redis是不需要密码就可以连接的,但是可以通过修改配置文件的方式设置密码认证,同样的也可以通过命令执行修改,如下:
->config set requirepass "yourpassword"
设置密码之后再次执行操作时会提示权限不足,需要进行认证,认证命令:
->auth "yourpassword"
Redis还提供了一些其它一些命令如:
->echo
->ping
->quit
->select index :Redis划分了多个DB,从DB1~DBn,通过select index选择不同的db执行操作