您的位置:首页 > 数据库 > Redis

Java版的Redis

2012-06-20 16:12 183 查看
Redis是一个基于Key-value结构的Nosql数据库,它支持各种常见的数据结构以及非常方便的操作,
与其说它是一个数据库,不如说它是一个保存各种数据结构的服务器。今天闲来没事,用Java集合类
实现了Redis的一些基本功能,算是温习下Java了。

1.Redis入门

Redis的Key键值为字符串,但是Value值支持许多种类型,如String字符串,List链表,Set无序集合,
SortedSet有序集合,甚至是Hash表。

各种数据结构通过不同的存取方法来区分。如Set/Get直接将值存为String,LPush/LPop/LRange将
值存到一个链表中,SAdd/ZAdd则区分了无序和有序集合。

下面我们来看下在Java中使用基本的集合类如何实现这些简单而方便的操作。

2.Java版的Redis

代码的组织结构如下图:



package com.cdai.studio.redis;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.TreeSet;

@SuppressWarnings("unchecked")
public class RedisDB {

private Persistence persistence = new Persistence();

private Serializer serializer = new Serializer();

private static final Object[] NULL = new Object[0];

// =================================================
//					String value
// =================================================

public void Set(String key, Object value) {
persistence.put(key, serializer.marshal(value));
}

public Object Get(String key) {
return serializer.unmarshal(persistence.get(key));
}

public Object[] MGet(String... keys) {
Object[] values = new Object[keys.length];
for (int i = 0; i < keys.length; i++)
values[i] = Get(keys[i]);
return values;
}

public int Incr(String key) {
Object value = Get(key);
Integer valueRef = (value == null) ? 1 : (Integer) value;
Set(key, valueRef + 1);
return valueRef;
}

// =================================================
//					List value
// =================================================

public void LPush(String key, Object... values) {
Object list = persistence.get(key);
if (list == null)
list = new LinkedList<Object>();
else
list = serializer.unmarshal(list);

LinkedList<Object> listRef = (LinkedList<Object>) list;
for (Object value : values)
listRef.addFirst(value);
persistence.put(key, serializer.marshal(list));
}

public void RPush(String key, Object... values) {
Object list = persistence.get(key);
if (list == null)
list = new LinkedList<Object>();
else
list = serializer.unmarshal(list);

LinkedList<Object> listRef = (LinkedList<Object>) list;
for (Object value : values)
listRef.addLast(value);
persistence.put(key, serializer.marshal(list));
}

public Object[] LRange(String key, int start, int end) {
Object list = persistence.get(key);
if (list == null)
return NULL;

LinkedList<Object> listRef = (LinkedList<Object>) serializer.unmarshal(list);
if (end > listRef.size())
end = listRef.size();
return listRef.subList(start, end).toArray();
}

// =================================================
//					Unsorted Set value
// =================================================

public void SAdd(String key, Object... values) {
Object set = persistence.get(key);
if (set == null)
set = new HashSet<Object>();
else
set = serializer.unmarshal(set);

HashSet<Object> setRef = (HashSet<Object>) set;
for (Object value : values)
setRef.add(value);
persistence.put(key, serializer.marshal(set));
}

public Object[] SMembers(String key) {
Object set = persistence.get(key);
if (set == null)
return NULL;

set = serializer.unmarshal(set);
return ((HashSet<Object>) set).toArray();
}

public Object[] SInter(String key1, String key2) {
Object set1 = persistence.get(key1);
Object set2 = persistence.get(key2);
if (set1 == null || set2 == null)
return NULL;

HashSet<Object> set1Ref = (HashSet<Object>) serializer.unmarshal(set1);
HashSet<Object> set2Ref = (HashSet<Object>) serializer.unmarshal(set2);
set1Ref.retainAll(set2Ref);
return set1Ref.toArray();
}

public Object[] SDiff(String key1, String key2) {
Object set1 = persistence.get(key1);
Object set2 = persistence.get(key2);
if (set1 == null || set2 == null)
return NULL;

HashSet<Object> set1Ref = (HashSet<Object>) serializer.unmarshal(set1);
HashSet<Object> set2Ref = (HashSet<Object>) serializer.unmarshal(set2);
set1Ref.removeAll(set2Ref);
return set1Ref.toArray();
}

// =================================================
//					Sorted Set value
// =================================================

public void ZAdd(String key, Object... values) {
Object set = persistence.get(key);
if (set == null)
set = new TreeSet<Object>();
else
set = serializer.unmarshal(set);

TreeSet<Object> setRef = (TreeSet<Object>) set;
for (Object value : values)
setRef.add(value);
persistence.put(key, serializer.marshal(set));
}

public Object[] SRange(String key, Object from) {
Object set = persistence.get(key);
if (set == null)
return NULL;

set = serializer.unmarshal(set);
return ((TreeSet<Object>) set).tailSet(from).toArray();
}

}
package com.cdai.studio.redis;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Arrays;

class Serializer {

Object marshal(Object object) {
if (object == null)
return null;
return new BytesWrapper((Serializable) object);
}

Object unmarshal(Object object) {
if (object == null)
return null;
return ((BytesWrapper) object).readObject();
}

}

class BytesWrapper {

private byte[] bytes;

<T extends Serializable> BytesWrapper(T object) {
writeBytes(object);
}

<T extends Serializable> void writeBytes(T object) {
try {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
ObjectOutputStream output = new ObjectOutputStream(buffer);
output.writeObject(object);
output.flush();
bytes = buffer.toByteArray();
output.close();
}
catch (IOException e) {
e.printStackTrace();
throw new IllegalStateException(e);
}
}

Object readObject() {
try {
ObjectInputStream input = new ObjectInputStream(new ByteArrayInputStream(bytes));
Object object = input.readObject();
input.close();
return object;
}
catch (Exception e) {
e.printStackTrace();
throw new IllegalStateException(e);
}
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(bytes);
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
BytesWrapper other = (BytesWrapper) obj;
if (!Arrays.equals(bytes, other.bytes))
return false;
return true;
}

}
package com.cdai.studio.redis;

import java.util.HashMap;

class Persistence {

private HashMap<String, Object> storage =
new HashMap<String, Object>();

void put(String key, Object value) {
storage.put(key, value);
}

Object get(String key) {
return storage.get(key);
}

}


3.简单的客户端
package com.cdai.studio.redis;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;

public class RedisServer {

private RedisDB redis;

public RedisServer(RedisDB redis) {
this.redis = redis;
}

@SuppressWarnings("unchecked")
public void start() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(1234);
while (true) {
Socket socket = serverSocket.accept();

ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
List<Object> request = (List<Object>) input.readObject();

Object response = null;
if ("Set".equals(request.get(0))) {
redis.Set((String) request.get(1), request.get(2));
}
else if ("Get".equals(request.get(0))) {
response = redis.Get((String) request.get(1));
}

ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
output.writeObject(response);

input.close();
output.close();
socket.close();
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
}
}
}

}

}
package com.cdai.studio.redis;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.Socket;
import java.util.Arrays;
import java.util.List;

public class RedisClient {

public <T extends Serializable> void Set(String key, Object value) {
sendRequest(Arrays.asList("Set", key, value));
}

public Object Get(String key) {
return sendRequest(Arrays.<Object>asList("Get", key));
}

private Object sendRequest(List<Object> payload) {
Socket socket = null;
try {
socket = new Socket("localhost", 1234);

ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
output.writeObject(payload);
output.flush();

ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
Object response = input.readObject();

output.close();
input.close();
return response;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (Exception e) {
}
}
}
return null;
}

}


4.实现简单的Twitter
package com.cdai.studio.redis;

import java.util.Arrays;

public class RedisTest {

public static void main(String[] args) {

RedisDB redis = new RedisDB();

// 1.Create user follow relationship
redis.SAdd("users", "A", "B", "C");

// User A follows B, C
redis.SAdd("users:A:following", "B", "C");
redis.SAdd("users:B:followers", "A");
redis.SAdd("users:C:followers", "A");

// User C follows B
redis.SAdd("users:C:following", "B");
redis.SAdd("users:B:followers", "C");

// 2.1 B send tweet
int tid = redis.Incr("tweets:next_id");
redis.Set("tweets:" + tid, "B publish hello");
redis.LPush("global:timeline", tid);
redis.LPush("users:B:timeline", tid);
for (Object follower : redis.SMembers("users:B:followers"))
redis.LPush("users:" + follower + ":timeline", tid);

// 2.2 C send tweet
tid = redis.Incr("tweets:next_id");
redis.Set("tweets:" + tid, "C publish world");
redis.LPush("global:timeline", tid);
redis.LPush("users:C:timeline", tid);
for (Object follower : redis.SMembers("users:C:followers"))
redis.LPush("users:" + follower + ":timeline", tid);

Object[] tids = redis.LRange("global:timeline", 0, 9);
String[] tweetids = new String[tids.length];
for (int i = 0; i < tids.length; i++)
tweetids[i] = "tweets:" + tids[i];
System.out.println(Arrays.toString(redis.MGet(tweetids)));
}

}


5.需要注意的问题

byte数组的equals和hashcode默认实现比较对象地址的,要借助于Arrays的equals和hashcode方法。

String字符串序列化和反序列化时要注意编码格式的问题,编码解码时应该使用相同的编码。

HashSet上的操作,removeAll补集,retainAll交集,addAll并集。

6.更加强大的Redis

Redis自己实现了各种数据结构,可以非常方便地增删改查,并且效率很高。这里我们只是用
Java来简单的学习了下Redis基本功能,其实Redis还支持很多其他的高级功能,如消息订阅、
数据过期设置、事务、数据持久化。想要进一步学习的话可以试着用Java实现它们。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: