Redis是面向key-value存储速度最快的NoSql数据库之一,既可基于内存也可持久化。作为key-value数据存储,在一些非功能性需求中主要有以下特点:
1、数据集插入快,无异常
2、随机读:所有keys放内存因此读取速度非常快,Redis使用虚拟内存将keys存内存,将最小最近使用的values放磁盘来避免内存消耗
3、容错性和负载均衡:采用主从拓扑结构,master-slave;读请求均匀分布于从节点;
4、最终一致性:主节点更新从节点也会异步更新,存在短暂时间点不一致问题但会到达最终一致
5、分片和可扩展性:节点水平扩展使得数据集能大于内存
6、多数据中心支持+可管理+多客户端
一、Redis的安装和启动
1、下载zip安装文件解压即可
2、在redis安装目录下,直接运行Redis-server.exe或者Windows命令redis-server [redis.windows.conf]启动,默认端口号6379
3、conf文件是redis配置文件可以重名名和修改默认属性,修改后启动必需带上配置文件参数,否则还是以默认参数启动
二、客户端连接
1、进入redis安装目录,执行cmd命令:redis-cli [-h] [-p] 默认配置下IP和端口可以省略,可以连接多个客户端
2、连接客户端可进行各种数据操作,redis主要支持5种数据类型:String/List/Set/SortedSet/Hashes
3、基本命令:set/get等,不同数据类型set/get命令有不同前缀,可查询相关文档了解
例如String类型:set/get key [value]基本存取值操作;setNx N一般代表已存在key不做操作,不存在则新建;setEx E代表附加失效操作,单位秒set key value ex timeout
以下示例是几种数据类型在java中的简单使用,在以Redis作为数据库的时候会用到:
public class TestJedis {
private JedisPool jedisPool = new JedisPool(new JedisPoolConfig(), "localhost");
private Jedis jedis;
public Jedis getResource() {
jedis = jedisPool.getResource();
return jedis;
}
public void setResource() {
jedis.close();
}
/**
* redis sets类型基本操作
*/
private void testSetsType() {
Jedis jedis = getResource();
jedis.sadd("follow:cricket", "vinoo.das@junk-mail.com", "vinoo.das1@junk-mail.com",
"vinoo.das3@junk-mail.com");
System.out.println("follow:cricket" + " : " + jedis.smembers("follow:cricket"));
//元素个数统计
Assert.assertEquals(3, jedis.scard("follow:cricket").longValue());
jedis.sadd("follow:redis", "vinoo.das1@junk-mail.com", "vinoo.das2@junk-mail.com");
System.out.println("follow:redis" + " : " + jedis.smembers("follow:redis"));
Assert.assertEquals(2 , jedis.scard("follow:redis").longValue());
//获取集合中的公共元素
System.out.println(jedis.sinter("Cricket:followers", "follow:redis"));
//存储集合中的公共元素到指定list中
jedis.sinterstore("follow:redis+cricket", "follow:cricket", "follow:redis");
System.out.println("follow:redis+cricket" + " : " + jedis.smembers("follow:redis+cricket"));
System.out.println(jedis.sismember("follow:redis+cricket", "vinoo.das@junk-mail.com"));
System.out.println(jedis.sismember("follow:redis+cricket", "vinoo.das1@junk-mail.com"));
//将成员从源集合移到目标集合中,但源集合中不删除该成员
jedis.smove("follow:cricket", "follow:redis", "vinoo.das3@junkmail.com");
System.out.println("follow:cricket" + " : " + jedis.smembers("follow:cricket"));
System.out.println("follow:redis" + " : " + jedis.smembers("follow:redis"));
//随机返回一个元素
System.out.println(jedis.srandmember("follow:cricket"));
//随机移除
System.out.println(jedis.spop("follow:cricket"));
System.out.println(jedis.smembers("follow:cricket"));
jedis.sadd("follow:cricket","wrong-data@junk-mail.com");
System.out.println(jedis.smembers("follow:cricket"));
jedis.srem("follow:cricket","wrong-data@junk-mail.com");
System.out.println(jedis.smembers("follow:cricket"));
System.out.println(jedis.sunion("follow:cricket", "follow:redis"));
jedis.sunionstore("follow:cricket-or-redis", "follow:cricket", "follow:redis");
System.out.println("follow:cricket-or-redis" + " : " + jedis.smembers("follow:cricket-or-redis"));
System.out.println(jedis.sdiff("follow:cricket", "follow:redis"));
}
/**
* redis List类型基本操作
*/
private void testListType() {
Jedis jedis = getResource();
String key1 = "mykey4list1";
String key2 = "mykey4list2";
System.out.println("del key : " + jedis.del(key1, key2).longValue());
for (int i = 0; i < 3; i++) {
jedis.lpush(key1, "message" + i);
}
System.out.println(key1 + ":" + jedis.lrange(key1, 0, -1));
for (int i = 3; i < 6; i++) {
jedis.rpush(key2, "message" + i);
}
System.out.println(key2 + ":" + jedis.lrange(key2, 0, -1));
Assert.assertEquals("message2", jedis.lindex(key1, 0));
System.out.println(jedis.linsert(key1, LIST_POSITION.AFTER, "message2", "message7"));
System.out.println(key1 + ":" + jedis.lrange(key1, 0, -1));
Assert.assertEquals(4, jedis.llen(key1).longValue());
//从左边移除第一个元素
System.out.println("remove : " + jedis.lpop(key1));
System.out.println(key1 + ":" + jedis.lrange(key1, 0, -1));
System.out.println(jedis.lpushx(key1, "Message - 1.8"));
System.out.println(key1 + ":" + jedis.lrange(key1, 0, -1));
//按顺序和个数移除指定值,负数从右到左,0移除全部指定元素
System.out.println(jedis.lrem(key1, 0, "Message - 1.8"));
System.out.println(jedis.lrange(key1, 0, -1));
System.out.println(jedis.lset(key1, -1, "Message - 7"));
System.out.println(key1 + ":" + jedis.lrange(key1, 0, -1));
System.out.println(jedis.ltrim(key1, 0, -1));
System.out.println(key1 + ":" + jedis.lrange(key1, 0, -1));
//最右边的元素从list1移到list2中
jedis.rpoplpush(key1, key2);
System.out.println(key1 + ":" + jedis.lrange(key1, 0, -1));
System.out.println(key2 + ":" + jedis.lrange(key2, 0, -1));
}
/**
* redis hashes类型基本操作
*/
private void testHashesType() {
Jedis jedis = getResource();
String commonkey = "learning redis";
jedis.hset(commonkey, "publisher", "Packt Publisher");
jedis.hset(commonkey, "author", "Vinoo Das");
System.out.println(jedis.hgetAll(commonkey).get("author"));
Map<String, String> attributes = new HashMap<>();
attributes.put("ISBN", "XX-XX-XX-XX");
attributes.put("tags", "Redis,NoSQL");
attributes.put("pages", "250");
attributes.put("weight", "200.56");
jedis.hmset(commonkey, attributes);
System.out.println(jedis.hgetAll(commonkey));
System.out.println(jedis.hget(commonkey, "publisher"));
System.out.println(jedis.hmget(commonkey, "publisher", "author"));
System.out.println(jedis.hvals(commonkey));
System.out.println(jedis.hget(commonkey, "publisher"));
System.out.println(jedis.hkeys(commonkey));
System.out.println(jedis.hexists(commonkey, "cost"));
System.out.println(jedis.hlen(commonkey));
System.out.println(jedis.hincrBy(commonkey, "pages", 10));
System.out.println(jedis.hincrByFloat(commonkey, "weight", 1.1) + "gms");
System.out.println(jedis.hdel(commonkey, "weight-in-gms"));
System.out.println(jedis.hgetAll(commonkey));
setResource();
}
}
4、在客户端查看所有keys命令: keys * ,也支持通配表达式keys pattern
5、删除key:del key 删除该库所有key:flushDb 删除所有库所有数据:flushall
6、其它一些常用命令:
->append key value 追加值
->strlen key 查看value长度
->setRange key offset value 从offset位置开始替换或插入值,原长度不够的前置补'\xoo'
三、通信协议RESP(Redis Serialization Protocol)
Redis工作于C-S模式,客户端和服务端之前使用TCP/IP协议(应用层、传输层、网络层、网关接口),client向server发送请求数据包括:请求主体(Request Data)和请求头header(meta info),server向client返回数据包括:响应主体(Response Data)和响应头header;Header+Body共同构成信息块用于传输数据,Header主要包括协议名称、版本号、安全信息、参数个数和类型等,body存放的主要是真实数据。
以下是一个简单的使用实例:
抽象类Command主要是建立连接和定义通用命令执行方法:
public abstract class Command {
protected Socket socket;
public Command() {
try {
socket = new Socket(ConnectionProperties.host, ConnectionProperties.port);
} catch (IOException e) {
e.printStackTrace();
}
}
public String createPayload(ArrayList<String> messageList) {
int argumentSize = messageList.size();
StringBuffer payload = new StringBuffer();
payload.append('*');
payload.append(argumentSize);
payload.append("\r\n");
for (int cursor = 0; cursor < messageList.size(); cursor++) {
payload.append("$");
payload.append(messageList.get(cursor).length());
payload.append("\r\n");
payload.append(messageList.get(cursor));
payload.append("\r\n");
}
return payload.toString().trim();
}
public abstract String createPayload();
public abstract void execute() throws IOException;
接下来是set/get命令执行,SetCommand和GetCommand继承抽象类Command:
public class SetCommand extends Command {
private String key;
private String value;
public SetCommand(String key, String value) {
this.key = key;
this.value = value;
}
@Override
public String createPayload() {
ArrayList<String> messageList = new ArrayList<>();
messageList.add("SET");
messageList.add(key);
messageList.add(value);
return super.createPayload(messageList);
}
@Override
public void execute() throws IOException {
PrintWriter out = null;
BufferedReader in = null;
try {
out = new PrintWriter(super.socket.getOutputStream(), true);
out.println(createPayload());
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(in.readLine());
} catch (IOException e) {
e.printStackTrace();
} finally {
in.close();
out.flush();
out.close();
socket.close();
}
}
}
public class GetCommand extends Command {
private String key;
public GetCommand(String key) {
this.key = key;
}
@Override
public String createPayload() {
ArrayList<String> messageList = new ArrayList<>();
messageList.add("GET");
messageList.add(key);
return super.createPayload(messageList);
}
@Override
public void execute() throws IOException {
PrintWriter out = null;
BufferedReader in = null;
try {
out = new PrintWriter(super.socket.getOutputStream(), true);
out.println(this.createPayload());
//Reads from Redis server
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String msg = in.readLine();
if (!msg.contains("-1")) {
System.out.println(msg);
System.out.println(in.readLine());
} else {
// This will show the error message since the
// server has returned '-1'
System.out.println("This Key does not exist !");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
out.flush();
out.close();
in.close();
socket.close();
}
}
}
测试类如下:
public class TestClient {
public void execute(Command command) {
try {
command.execute();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
TestClient testClient = new TestClient();
SetCommand setCommand = new SetCommand("msg", "redis learning");
testClient.execute(setCommand);
GetCommand getCommand = new GetCommand("msg");
testClient.execute(getCommand);
}
}