创建表格存储 client,在接口代码中直接注入 即可使用 ( @Autowired @Qualifier("createClient") private SyncClient client;) package com.mimidai.common.utils.table;import com.alicloud.openservices.tablestore.ClientConfigurati
package com.mimidai.common.utils.table;
import com.alicloud.openservices.tablestore.ClientConfiguration;
import com.alicloud.openservices.tablestore.SyncClient;
import com.alicloud.openservices.tablestore.model.AlwaysRetryStrategy;
import com.mimidai.common.utils.PropertiesUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 创建表格存储client Created by EVE on 2017-06-22.
*/
@Configuration
//@PropertySource("classpath:application-develop.properties")
public class TableClientConfig {
private static Logger logger = LoggerFactory.getLogger("log.hbase.TableClientConfig");
//测试 环境 start
//内网地址
private String endPoint = "http://XXXXXX:80/";
//外网地址 网段地址保留一个即可
// private static final String endPoint = "http://XXXXXX.cn-beijing.ots.aliyuncs.com";
private String accessId = "XXXXXX";
private String accessKey = "XXXXXXXX";
private String instanceName = "test";
//测试
@Bean
public SyncClient createClient() {
try{
logger.info("开始调用createClient方法");
// ClientConfiguration提供了很多配置项,以下只列举部分。
ClientConfiguration clientConfiguration = new ClientConfiguration();
// 设置建立连接的超时时间。
clientConfiguration.setConnectionTimeoutInMillisecond(5000);
// 设置socket超时时间。
clientConfiguration.setSocketTimeoutInMillisecond(5000);
// 设置重试策略,若不设置,采用默认的重试策略。
clientConfiguration.setRetryStrategy(new AlwaysRetryStrategy());
logger.info("返回创建表格存储cleint");
return new SyncClient(endPoint, accessId, accessKey, instanceName, clientConfiguration);
} catch (Exception e) {
logger.warn("----------------------表格存储client初始化失败");
}
return null;
}
}
创建表格存储表的工具类,可通过此创建带版本的表。(在官方客户端好像不可以创建版本,我是没发现)
package com.mimidai.common.utils.table;
import com.alicloud.openservices.tablestore.SyncClient;
import com.alicloud.openservices.tablestore.model.CapacityUnit;
import com.alicloud.openservices.tablestore.model.DeleteTableRequest;
import com.alicloud.openservices.tablestore.model.DescribeTableRequest;
import com.alicloud.openservices.tablestore.model.DescribeTableResponse;
import com.alicloud.openservices.tablestore.model.PrimaryKeySchema;
import com.alicloud.openservices.tablestore.model.PrimaryKeyType;
import com.alicloud.openservices.tablestore.model.ReservedThroughput;
import com.alicloud.openservices.tablestore.model.ReservedThroughputDetails;
import com.alicloud.openservices.tablestore.model.TableMeta;
import com.alicloud.openservices.tablestore.model.TableOptions;
import com.alicloud.openservices.tablestore.model.UpdateTableRequest;
import com.alicloud.openservices.tablestore.model.internal.CreateTableRequestEx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* Created by 孟庆艺 on 2017-08-03.
*/
@Service
public class OperateTableUtils {
@Autowired
@Qualifier("createClient")
private SyncClient client;
private static final Logger logger = LoggerFactory.getLogger("log.tableStore.OperateTableUtils");
/**
* 创建表
*
* @param createTableName 表名
* @param timeToLive 数据过期时间 单位:秒 例如:一年 365*24*3600 -1表示永不过期
* @param maxVersions 保存最大版本数 设置为3即代表每列最多保存 N 个最新的版本
* @param primaryKey 可变参数类型 主键列名 可变参数最大为4
*/
public void createTable(String createTableName, Integer timeToLive, Integer maxVersions, String... primaryKey) {
if (primaryKey.length > 4) {
logger.info("注意:表格存储主键最多可设置4个,可变参数个数最大为4");
return;
}
TableMeta tableMeta = new TableMeta(createTableName);
for (String PRIMARY_KEY : primaryKey) {
tableMeta.addPrimaryKeyColumn(new PrimaryKeySchema(PRIMARY_KEY, PrimaryKeyType.STRING));
}
TableOptions tableOptions = new TableOptions(timeToLive, maxVersions);
CreateTableRequestEx request = new CreateTableRequestEx(tableMeta, tableOptions);
//设置读写预留值 容量型示例 只能设置为0 高性能示例可以设置为非零值
request.setReservedThroughput(new ReservedThroughput(new CapacityUnit(0, 0)));
client.createTable(request);
}
/**
* 创建表
*
* @param timeToLive 数据过期时间 单位:秒 例如:一年 365*24*3600 -1表示永不过期
* @param maxVersions 保存最大版本数 设置为3即代表每列最多保存 N 个最新的版本
*/
public void createTable(String createTableName, Integer timeToLive, Integer maxVersions,
List
primaryKey) {
if (primaryKey.size() > 4) {
logger.info("注意:表格存储主键最多可设置4个,可变参数个数最大为4");
return;
}
TableMeta tableMeta = new TableMeta(createTableName);
tableMeta.addPrimaryKeyColumns(primaryKey);
TableOptions tableOptions = new TableOptions(timeToLive, maxVersions);
CreateTableRequestEx request = new CreateTableRequestEx(tableMeta, tableOptions);
//设置读写预留值 容量型示例 只能设置为0 高性能示例可以设置为非零值
request.setReservedThroughput(new ReservedThroughput(new CapacityUnit(0, 0)));
client.createTable(request);
}
/**
* 构建list
*/
private List
primaryKeySchemaList(Object... primaryKey) { if (primaryKey.length > 4) { logger.info("注意:表格存储主键最多可设置4个,可变参数个数最大为4"); return null; } List
primaryKeySchemaList = new ArrayList<>(); for (Object PRIMARYKEY : primaryKey) { if (PRIMARYKEY instanceof String) { String PRIMARY_KEY = (String) PRIMARYKEY; primaryKeySchemaList.add(new PrimaryKeySchema(PRIMARY_KEY, PrimaryKeyType.STRING)); } } return primaryKeySchemaList; } /** * 更新表 * * @param timeToLive 数据过期时间 单位:秒 例如:一年 365*24*3600 -1表示永不过期 * @param maxVersions 保存最大版本数 设置为3即代表每列最多保存 N 个最新的版本 * @param maxTimeDeviation 有效版本偏差 [数据写入时间-有效版本偏差,数据写入时间+有效版本偏差) */ public void updateTable(Integer timeToLive, Integer maxVersions, Long maxTimeDeviation, String TableName) { if (maxVersions == null || TableName == null) { return; } TableOptions tableOptions = new TableOptions(); if (maxVersions != null) { tableOptions = new TableOptions(maxVersions); } else if (timeToLive != null) { tableOptions = new TableOptions(timeToLive, maxVersions); } else if (maxTimeDeviation != null) { tableOptions = new TableOptions(timeToLive, maxVersions, maxTimeDeviation); } UpdateTableRequest updateTableRequest = new UpdateTableRequest(TableName); updateTableRequest.setTableOptionsForUpdate(tableOptions); client.updateTable(updateTableRequest); } /** * 获取表相关信息 */ public void describeTable(String TableName) { DescribeTableRequest describeStreamRequest = new DescribeTableRequest(TableName); DescribeTableResponse describeTableResponse = client.describeTable(describeStreamRequest); TableMeta tableMeta = describeTableResponse.getTableMeta(); List
primaryKeySchemaList = tableMeta.getPrimaryKeyList(); for (PrimaryKeySchema primaryKeySchema : primaryKeySchemaList) { logger.info("表:{}主键:{}", TableName, primaryKeySchema); } TableOptions tableOptions = describeTableResponse.getTableOptions(); ReservedThroughputDetails reservedThroughputDetails = describeTableResponse.getReservedThroughputDetails(); logger.info("表:{},数据过期时间timeToLive:{},最大版本数maxVersions:{},预留读吞吐量:{},预留写吞吐量:{}", TableName, tableOptions .getTimeToLive(), reservedThroughputDetails.getCapacityUnit().getReadCapacityUnit(), reservedThroughputDetails.getCapacityUnit().getWriteCapacityUnit()); } /** * 删除表 */ public void deleteTable(String TableName) { DeleteTableRequest deleteTableRequest = new DeleteTableRequest(TableName); client.deleteTable(deleteTableRequest); } }
该方法将重点着眼于 分区键的创建,一个良好的表结构设计尤为重要,分区键关系到最大化的利用大数据量情况下表格存储,自动分区,数据均匀散列分布,有利于分布式的操作数据
package com.mimidai.common.utils.table;/**
* Created by 孟庆艺 on 2017-08-31.
*/
import com.alicloud.openservices.tablestore.model.PrimaryKeyBuilder;
import com.alicloud.openservices.tablestore.model.PrimaryKeyValue;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Encoder;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import com.alicloud.openservices.tablestore.model.PrimaryKey;
/**
* 表格存储生成构造主键方法
*
* @author mengqingyi
* @create 2017-08-31 10:13
**/
public class PrimaryKeyUtils {
//日志
private static final Logger logger = LoggerFactory.getLogger(PrimaryKey.class);
/**
* 使用md5算法加密算法 对字符串进行加密
*/
private String EncodeByMd5(String string) throws NoSuchAlgorithmException, UnsupportedEncodingException {
//计算方法
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
BASE64Encoder base64Encoder = new BASE64Encoder();
//加密后的字符串
String newStr = base64Encoder.encode(messageDigest.digest(string.getBytes("UTF-8")));
return newStr;
}
/**
* 如有明确的分区键,则可以使用分区键+主键格式构造主键
*/
public PrimaryKey createPrimaryKey(Integer partitionKey, String userId) {
logger.debug("进入tableStore(core1)生成构造主键通用方法createPrimaryKey");
// 首先对入参进行判断 若为空直接返回 不再查询
if (StringUtils.isBlank(partitionKey + "") || StringUtils.isBlank(userId)) {
logger.warn("表格存储短信详单主键为空,达不到构造主键要求,返回空值");
return null;
}
// 构造主键
PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
primaryKeyBuilder.addPrimaryKeyColumn("partitionKey", PrimaryKeyValue.fromLong(partitionKey));
primaryKeyBuilder.addPrimaryKeyColumn("userId", PrimaryKeyValue.fromString(userId));
logger.info("tableStore(core1)生成构造主键通用方法createPrimaryKey:partitionKey={},userId={}", partitionKey, userId);
return primaryKeyBuilder.build();
}
/**
* 如无明确的分区键,方案1.由大小为5随机桶(实际就是随机数)作为分区键 以桶的随机数作为分片键(表格存储,hbase中也有类似方案,salted key)
*/
public PrimaryKey createPrimaryKeyByRandomBucket(String userId) {
logger.debug("进入tableStore(core1)生成构造主键通用方法createPrimaryKey");
// 首先对入参进行判断 若为空直接返回 不再查询
if (StringUtils.isBlank(userId)) {
logger.warn("表格存储短信详单主键为空,达不到构造主键要求,返回空值");
return null;
}
Integer partitionKey = new Random().nextInt(5);
// 构造主键
PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
primaryKeyBuilder.addPrimaryKeyColumn("partitionKey", PrimaryKeyValue.fromLong(partitionKey));
primaryKeyBuilder.addPrimaryKeyColumn("userId", PrimaryKeyValue.fromString(userId));
logger.info("tableStore(core1)生成构造主键通用方法createPrimaryKey:partitionKey={},userId={}", partitionKey, userId);
return primaryKeyBuilder.build();
}
/**
* 如无明确的分区键,方案1.指定大小的随机桶(实际就是随机数)作为分区键 以桶的随机数作为分片键(表格存储,hbase中也有类似方案,salted key)
*/
public PrimaryKey createPrimaryKeyByRandomBucket(String userId, Integer randomBucketSize) {
logger.debug("进入tableStore(core1)生成构造主键通用方法createPrimaryKey");
// 首先对入参进行判断 若为空直接返回 不再查询
if (StringUtils.isBlank(userId)) {
logger.warn("表格存储短信详单主键为空,达不到构造主键要求,返回空值");
return null;
}
Integer partitionKey = new Random().nextInt(randomBucketSize);
// 构造主键
PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
primaryKeyBuilder.addPrimaryKeyColumn("partitionKey", PrimaryKeyValue.fromLong(partitionKey));
primaryKeyBuilder.addPrimaryKeyColumn("userId", PrimaryKeyValue.fromString(userId));
logger.info("tableStore(core1)生成构造主键通用方法createPrimaryKey:partitionKey={},userId={}", partitionKey, userId);
return primaryKeyBuilder.build();
}
/**
* 无明确的分区键,取散裂化后的userId的前4位作为分区键
*/
public PrimaryKey createPrimaryKeyByUserId(String userId) {
logger.debug("进入tableStore(core1)生成构造主键通用方法createPrimaryKey");
// 首先对入参进行判断 若为空直接返回 不再查询
if (StringUtils.isBlank(userId)) {
logger.warn("表格存储短信详单主键为空,达不到构造主键要求,返回空值");
return null;
}
String partitionKey = null;
try {
partitionKey = EncodeByMd5(userId);
logger.info("userId={},MD5之后的md5UserId={}", userId, partitionKey);
} catch (Exception e) {
e.printStackTrace();
}
// 构造主键
PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
primaryKeyBuilder.addPrimaryKeyColumn("partitionKey", PrimaryKeyValue.fromString(partitionKey));
primaryKeyBuilder.addPrimaryKeyColumn("userId", PrimaryKeyValue.fromString(userId));
logger.info("tableStore(core1)生成构造主键通用方法createPrimaryKey:partitionKey={},userId={}", partitionKey, userId);
return primaryKeyBuilder.build();
}
}
最后奉上 官方表格存储的最佳实践
https://yq.aliyun.com/articles/57102
