在一般的java应用中,都会使用redis作为缓存,但是redis相对来说一般存储的数据量不会太多,热点数据才会使用redis暂存用于热读;但是在一些场景下,数据量比较大,对读取的性能又比较高的时候,aerospike可能会显得更加的高效,特别是在并发高的时候;下面对aerospike做了一些简单的尝试⬇️⬇️
1.基本java客户端操作
1.1 client
封装一下client
public class AsClient {
public static Map<String, Object> operate(String recordKey, Operation... opes) {
Record record = client.operate(new WritePolicy(), key(recordKey), opes);
return null != record ? record.bins : new HashMap<>();
}
static Key key(String recordKey) {
return new Key(namespace, asSet, recordKey);
}
static Bin[] toBins(Map<String, Object> binMap) {
Bin[] bins = new Bin[binMap.size()];
int index = 0;
for (String key : binMap.keySet()) {
bins[index++] = new Bin(key, binMap.get(key));
}
return bins;
}
public static AerospikeClient getClient() {
return client;
}
private static final AerospikeClient client;
private static final String namespace = "ns1";//建议一个project对应一个库就好了
private static final String asSet = "cyb_set";//个人感觉一个表也够用了,当做redis的键值来存数据
static {
//这是单节点的配置方法,如果后续加入其它节点,会自动发现
client = new AerospikeClient("10.57.30.214", 3000);
//这是多个节点的配置方式
/* Host[] hosts = new Host[]{
new Host("10.57.30.214", 3000)
};
ClientPolicy policy = new ClientPolicy();
// policy.user = "";
// policy.password = "";
// policy.loginTimeout = 3000;
client = new AerospikeClient(policy, hosts);*/
}
}
1.2基本操作
public class BaseOpe {
private static final AerospikeClient client = AsClient.getClient();
/**
* 如果不指定库和表,会默认使用配置文件中的默认库(test)和表
*
* @param recordKey 行键
* @param binMap 列与值
* @param ttl -2:数据更新时不更新ttl,-1永不过期,0根据配置文件的默认配置,注:单位是s
*/
public static void putRecord(String recordKey, Map<String, Object> binMap, Integer ttl) {
WritePolicy policy = new WritePolicy();//写策略可以配置比如:失效时间,存在值时是否覆盖,还是不操作等,默认是UPDATE,即存在覆盖,不存在插入,还有REPLACE,替换掉所有的bins
if (null != ttl) {
policy.expiration = ttl;//-2:数据更新时不更新ttl,-1永不过期,0根据配置文件的默认配置,注:单位是s
}
client.put(policy, key(recordKey), toBins(binMap));
}
/**
* 根据key查询记录,不传binName查询出所有的bin
*/
public static Map<String, Object> getRecord(String recordKey, String... binNames) {
Map<String, Object> result = new HashMap<>();
if (null == binNames || binNames.length == 0) {
Record record = client.get(new QueryPolicy(), key(recordKey));
if (null != record) {
result = record.bins;
}
} else {
Record record = client.get(new QueryPolicy(), key(recordKey), binNames);
if (null != record) {
result = record.bins;
}
}
return result;
}
public static List<Map<String, Object>> batchGet(String... recordKeys) {
Key[] keys = new Key[recordKeys.length];
for (int i = 0; i < keys.length; i++) {
keys[i] = key(recordKeys[i]);
}
Record[] records = client.get(new BatchPolicy(), keys);
List<Map<String, Object>> result = new ArrayList<>();
for (Record record : records) {
result.add(record.bins);
}
return result;
}
/**
* 根据key删除记录
*/
public static void delRecord(String recordKey) {
client.delete(new WritePolicy(), key(recordKey));
}
/**
* 通过设置为NULL来删除bin(列)
*/
public static void deleteBin(String recordKey, String binName) {
Bin bin = Bin.asNull(binName);
client.put(new WritePolicy(), key(recordKey), bin);
}
1.3列表List操作
public class ListOpe {
private static final AerospikeClient client = AsClient.getClient();
//List
public static void add2List(String rowKey, String colKey, Object value) {
operate(rowKey, ListOperation.append(colKey, Value.get(value)));
}
public static Object getOneFromList(String rowKey, String colKey, int index) {
Map<String, Object> result = operate(rowKey, ListOperation.get(colKey, index));
if (null == result) {
return null;
}
return result.get(colKey);
}
public static Long getListSize(String rowKey, String colKey) {
Map<String, Object> result = operate(rowKey, ListOperation.size(colKey));
if (null == result) {
return 0L;
}
return (Long) result.get(colKey);
}
}
1.4 Map操作
public class MapOpe {
private static final AerospikeClient client = AsClient.getClient();
//Map
@SuppressWarnings("unchecked")
public static Map<String, Object> getSubMap(String rowKey, String binName, Object startKey, Object endKey) {
Map<String, Object> result = operate(rowKey, MapOperation.getByKeyRange(binName, Value.get(startKey), Value.get(endKey), MapReturnType.KEY_VALUE));
if (null == result) {
return null;
}
List<AbstractMap.SimpleEntry<String, Object>> list = (List<AbstractMap.SimpleEntry<String, Object>>) result.get(binName);
Map<String, Object> map = new HashMap<>();
for (AbstractMap.SimpleEntry<String, Object> entry : list) {
map.put(entry.getKey(), entry.getValue());
}
return map;
}
public static void putOrderMap(String rowKey, String colKey, Map<Object, Object> data) {
MapPolicy policy = new MapPolicy(MapOrder.KEY_ORDERED, MapWriteMode.UPDATE);
Map<Value, Value> valueMap = new HashMap<>();
for (Object key : data.keySet()) {
valueMap.put(Value.get(key), Value.get(data.get(key)));
}
operate(rowKey, MapOperation.putItems(policy, colKey, valueMap));
}
public static Object getOneFromMap(String rowKey, String colKey, Object valueKey) {
Map<String, Object> result = operate(rowKey, MapOperation.getByKey(colKey, Value.get(valueKey), MapReturnType.VALUE));
if (null == result) {
return null;
}
return result.get(colKey);
}
public static Long getMapSize(String rowKey, String colKey) {
Map<String, Object> result = operate(rowKey, MapOperation.size(colKey));
if (null == result) {
return null;
}
return (Long) result.get(colKey);
}
}
1.5 操作总结
一般我们把as(aerospike)的namespace对应为db(database)的库,as的set对应db的表,as的bin对应表的列,as的set没有列的限制,也无需预先定义,每行的同一列可以是不同的数据类型,类的数量每一列也可以不一样,类似大多数NoSql设计;还有就是as的批量操作,可以将一批操作维持在一个事物内;List和Map的相关操作是对列的操作,我们通过普通操作插入一条数据,如果某列是List或者Map类型的话,可以使用ListOperation和MapOperation对其进行一些集合的操作。官方文档里面有很多其他细的操作,大家可以参考自己的项目是否可以使用,地址:https://www.aerospike.com/docs