Hbase 分页设计
hbase 数据获取方式
- 直接根据 rowkey 查找,速度最快
- scan,指定 startrowkey、endrowkey 和 limit获取数据,在 rowkey 设计良好的情况下,效率也不错
- 全表扫,强烈不推荐这种做法,效率极差,在线业务不用考虑这种方式
hbase 数据排序怎么做?
我觉得这个分两种情况,一是数据量比较少,业务上每次拉取所有的数据,可以在客户端做排序,二是数据比较多,需要分页,这种情况下客户端做显然不合适,因为要从服务器拉取所有数据,排序完成,获取某一页,剩余的数据全都不用,资源损耗比较严重,比较推荐做法是充分利用 hbase rowkey 的特性,数据是按照 rowkey 字典序排列的,如果排序字段是不变的,可以把排序字段加到 rowkey 里,这样吐出的数据自然就是有序的。
如何排序的字段是可以变的呢?
假设业务上有个查询,排序的字段是可以变得,这样放到 rowkey 里就不合适了,因为排序字段变,意味着 rowkey 也会变,不是推荐的做法。这时候考虑的一种实现就是使用 redis 维护一个zset 索引,score 是排序字段,value 是对应记录的 rowkey,每次排序的字段变了,就去更新 zset 对应的数据。查询的话就相当于先去 redis 查出 rowkey 列表,然后根据 rowkey 列表去 hbase 批量查。
产品分页的方式?
常见的两种方式,一种是更多这种按钮,不支持跳转到某一页,一种是可以选择某个特定的页进行跳转。这两种方式 hbase 在实现上会有区别,下面会分别介绍下。
更多方式的分页
这种分页不支持跳转到某一页,只能不断地下一页下一页,使用 hbase 可以以一种比较简单的方式实现。由服务端告诉 app 端或者 web 端下一页请求的参数,假设某一页获取20条数据,服务端去获取21条,第21条数据的 rowkey 就是下一次扫描的 startrowkey,把它加到返回给 web 或者 app 的参数里,这样就可以实现分页。有人可能会说,假设下一页的 startrowkey 返回给前端之后,这时候有新的数据插入,不是会有问题吗?这种情况其实还好,首先互联网应用大多是读多写少,你浏览某个列表时,列表内容更新的概率本来就小,就算真的发生,数据会按照排序方式插入到列表首,你不刷新首屏内容,仅仅也就是新加的内容没展现出来,不影响其他内容的展示,而只要你一刷新首屏,新的内容就出来了。
直接跳转到某一页
假设分页可以直接跳转到某一页呢?这个用 hbase 实现确认比较尴尬,hbase scan 扫描的时候本来就是根据 rowkey 范围和 limit 扫描,想到的实现方式依旧是 zset,score 排序字段,value 是 rowkey。也不一定非用 redis,反正就是要有一个地方维护查询索引。去 hbase 直接根据 rowkey 查询。
简单Hbase分页方案
某位仁兄发给的,不知道怎么样::::
网上大多数分页方案分为从服务端分页或者从客户端分页 服务端分页方式主要利用PageFilter过滤器,首先太复杂,其次针对集群的兼容性不是很好,作者利用服务端分页+客户端分页结合方式给出一种简单易行的中间方案。
Filter pageSize = new PageFilter(pageSize *pageNo);
List<Result> resultList = new ArrayList<>();
// 计算起始页和结束页
Integer firstPage = (pageNo) *pageSize;
Integer endPage = firstPage + pageSize; //客户端分页
int i = ; for (Result rs : scanner) {
if (!rs.isEmpty() && i >= firstPage && i < endPage) {
resultList.add(rs);
}
if (resultList.size() == log.getPageSize()) {
break;
}
i++;
}
public static void mai(String[] args){
Connection connection = HBaseClientUtil.getConnection();
table = connection.getTable(TableName.valueOf(ProcessLogUtil.HB_TB_NAME));
Scan scan = new Scan();
Filter pageSizeFilter = new PageFilter(pageSize *pageNo);
scan.setFilter(pageSizeFilter );
ResultScanner scanner = table.getScanner(scan);
List<Result> resultList = new ArrayList<>();
// 计算起始页和结束页
Integer firstPage = (pageNo) *pageSize;
Integer endPage = firstPage + pageSize; //客户端分页
int i = 0; for (Result rs : scanner) {
if (!rs.isEmpty() && i >= firstPage && i < endPage) {
resultList.add(rs);
}
if (resultList.size() == log.getPageSize()) {
break;
}
i++;
}
}
package cp.app.service.impl; import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.MasterNotRunningException;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.TableNotFoundException;
import org.apache.hadoop.hbase.ZooKeeperConnectionException;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.client.HConnection;
import org.apache.hadoop.hbase.client.HConnectionManager;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.HTableInterface;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.client.coprocessor.AggregationClient;
import org.apache.hadoop.hbase.client.coprocessor.LongColumnInterpreter;
import org.apache.hadoop.hbase.filter.BinaryComparator;
import org.apache.hadoop.hbase.filter.CompareFilter;
import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.PageFilter;
import org.apache.hadoop.hbase.filter.RowFilter;
import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
import org.apache.hadoop.hbase.filter.SubstringComparator;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service; import cp.app.batch.utils.ConfigUtil;
import cp.app.comm.CpConstants;
import cp.app.service.HBaseService; /**
* HBase查询与插入操作工具类
*
* @author author
*
*/
//采用注入方式,HBaseService为定义的查询接口,可不需要。
@Service
public class HBaseServiceImpl implements HBaseService{ private static Logger log = Logger.getLogger(HBaseServiceImpl.class.getName()); static ConfigUtil util = new ConfigUtil("conf/zookeeper.properties");
private static final String HBASE_ZOOKEEPER_QUORUM = util
.getString("hbase_zookeeper_quorum");
private static final String ZOOKEEPER_ZNODE_PARENT = util
.getString("zookeeper_znode_parent");
private static Configuration conf = HBaseConfiguration.create();
static {
conf.set("hbase.zookeeper.quorum", HBASE_ZOOKEEPER_QUORUM);
conf.set("zookeeper.znode.parent", ZOOKEEPER_ZNODE_PARENT);
} /**
* 创建表
*
* @param tableName
* 表名
* @param columnFamily
* 列簇集合
* @return 成功-true 失败-false
*/
@SuppressWarnings("resource")
public boolean createTable(String tableName, List<String> columnFamily) {
try {
if (StringUtils.isBlank(tableName) || columnFamily == null
|| columnFamily.size() < 0) {
log.error("===Parameters tableName|columnFamily should not be null,Please check!===");
}
HBaseAdmin admin = new HBaseAdmin(conf);
if (admin.tableExists(tableName)) {
return true;
} else {
HTableDescriptor tableDescriptor = new HTableDescriptor(
TableName.valueOf(tableName));
for (String cf : columnFamily) {
tableDescriptor.addFamily(new HColumnDescriptor(cf));
}
admin.createTable(tableDescriptor);
log.info("===Create Table " + tableName
+ " Success!columnFamily:" + columnFamily.toString()
+ "===");
}
} catch (MasterNotRunningException e) {
// TODO Auto-generated catch block
log.error(e);
return false;
} catch (ZooKeeperConnectionException e) {
// TODO Auto-generated catch block
log.error(e);
return false;
} catch (IOException e) {
// TODO Auto-generated catch block
log.error(e);
return false;
}
return true;
} /**
* 查询单条记录
*
* @param tableName
* 表名
* @param rowKey
* rowKey值
* @return 返回单条记录
*/
public List<Map<String, String>> selectOneByRowKey(String tableName,
String rowKey) {
if (StringUtils.isBlank(rowKey) || StringUtils.isBlank(tableName)) {
log.error("===Parameters tableName|rowKey should not be blank,Please check!===");
return null;
}
List<Map<String, String>> rowList = new ArrayList<Map<String, String>>();
try {
Get get = new Get(Bytes.toBytes(rowKey));
HTableInterface hTable = getHTable(tableName);
if (hTable != null) {
Result result = hTable.get(get);
Map<String, String> cellMap = getRowByResult(result);
rowList.add(cellMap);
}
hTable.close();
} catch (IOException e) {
// TODO Auto-generated catch block
log.error(e);
}
return rowList;
} /**
* 分页查询表数据
*
* @param tableName
* 表名
* @param ddate
* 数据日期
* @param pageSize
* 页大小
* @param lastrowKey
* 起始rowkey值
* @return 返回查询数据结果集
*/
public List<Map<String, String>> selectAllByPage(String tableName,
String ddate, int pageSize, String lastrowKey) {
if (StringUtils.isBlank(tableName) || StringUtils.isBlank(ddate)
|| StringUtils.isBlank(pageSize + "")
|| StringUtils.isBlank(lastrowKey)) {
log.error("===Parameters tableName|ddate|pageSize|rowKey should not be blank,Please check!===");
return null;
}
HTable hTable = (HTable) getHTable(tableName);
Scan scan = new Scan();
FilterList filterList = new FilterList(
FilterList.Operator.MUST_PASS_ALL);
Filter rowFilter1 = new RowFilter(CompareFilter.CompareOp.EQUAL,
new SubstringComparator(ddate));
Filter pageFilter = new PageFilter(pageSize);
filterList.addFilter(rowFilter1);
filterList.addFilter(pageFilter);
if (!CpConstants.ROWKEY_FIRST.equals(lastrowKey)) {
Filter rowFilter2 = new RowFilter(CompareFilter.CompareOp.GREATER,
new BinaryComparator(Bytes.toBytes(lastrowKey)));
filterList.addFilter(rowFilter2);
}
scan.setFilter(filterList);
List<Map<String, String>> lists = new ArrayList<Map<String, String>>();
try {
ResultScanner rs = hTable.getScanner(scan);
for (Result result : rs) {
lists.add(getRowByResult(result));
}
hTable.close();
} catch (IOException e) {
// TODO Auto-generated catch block
log.error(e);
}
return lists;
} /**
* 根据状态分页查询表数据
*
* @param tableName
* 表名
* @param ddate
* 数据日期
* @param pageSize
* 页大小
* @param lastrowKey
* 起始rowkey值
* @param status
* 发送状态
* @return 返回查询数据结果集
*/
public List<Map<String, String>> selectAllByPageStatus(String tableName,
String ddate, int pageSize, String lastrowKey, String status) {
if (StringUtils.isBlank(tableName) || StringUtils.isBlank(ddate)
|| StringUtils.isBlank(pageSize + "")
|| StringUtils.isBlank(lastrowKey)) {
log.error("===Parameters tableName|ddate|pageSize|rowKey should not be blank,Please check!===");
return null;
}
HTable hTable = (HTable) getHTable(tableName);
Scan scan = new Scan();
FilterList filterList = new FilterList(
FilterList.Operator.MUST_PASS_ALL);
filterList
.addFilter(new SingleColumnValueFilter(Bytes.toBytes("info"),
Bytes.toBytes("status"), CompareOp.EQUAL, Bytes
.toBytes(status)));
Filter rowFilter1 = new RowFilter(CompareFilter.CompareOp.EQUAL,
new SubstringComparator(ddate));
Filter pageFilter = new PageFilter(pageSize);
filterList.addFilter(rowFilter1);
filterList.addFilter(pageFilter);
if (!CpConstants.ROWKEY_FIRST.equals(lastrowKey)) {
Filter rowFilter2 = new RowFilter(CompareFilter.CompareOp.GREATER,
new BinaryComparator(Bytes.toBytes(lastrowKey)));
filterList.addFilter(rowFilter2);
}
scan.setFilter(filterList);
List<Map<String, String>> lists = new ArrayList<Map<String, String>>();
try {
ResultScanner rs = hTable.getScanner(scan);
for (Result result : rs) {
lists.add(getRowByResult(result));
}
hTable.close();
} catch (IOException e) {
// TODO Auto-generated catch block
log.error(e);
}
return lists;
} /**
* 获取页数
*
* @param tableName
* 表名
* @param ddate
* 数据日期
* @param pageSize
* 分页大小
* @return 返回页数
*/
public int getPages(String tableName, String ddate, int pageSize) {
if (StringUtils.isBlank(tableName) || StringUtils.isBlank(ddate)
|| StringUtils.isBlank(pageSize + "")) {
log.error("===Parameters tableName|ddate|pageSize should not be blank,Please check!===");
return 0;
}
enableAggregation(tableName);
int total = 0;
try {
HTable hTable = (HTable) getHTable(tableName);
Scan scan = new Scan();
Filter rowFilter = new RowFilter(CompareFilter.CompareOp.EQUAL,
new SubstringComparator(ddate));
scan.setFilter(rowFilter);
AggregationClient aggregation = new AggregationClient(conf);
Long count = aggregation.rowCount(hTable,
new LongColumnInterpreter(), scan);
total = count.intValue();
hTable.close();
} catch (Throwable e) {
// TODO Auto-generated catch block
log.error(e);
}
return (total % pageSize == 0) ? total / pageSize
: (total / pageSize) + 1;
} /**
* 根据发送状态获取页数
*
* @param tableName
* 表名
* @param ddate
* 数据日期
* @param pageSize
* 分页大小
* @param status
* 发送状态
* @return 返回页数
*/
public int getPagesByStatus(String tableName, String ddate, int pageSize,
String status) {
if (StringUtils.isBlank(tableName) || StringUtils.isBlank(ddate)
|| StringUtils.isBlank(pageSize + "")
|| StringUtils.isBlank(status)) {
log.error("===Parameters tableName|ddate|pageSize|status should not be blank,Please check!===");
return 0;
}
enableAggregation(tableName);
int total = 0;
try {
HTable hTable = (HTable) getHTable(tableName);
Scan scan = new Scan();
FilterList filterList = new FilterList(
FilterList.Operator.MUST_PASS_ALL);
Filter rowFilter = new RowFilter(CompareFilter.CompareOp.EQUAL,
new SubstringComparator(ddate));
filterList.addFilter(rowFilter);
filterList.addFilter(new SingleColumnValueFilter(Bytes
.toBytes("info"), Bytes.toBytes("status"), CompareOp.EQUAL,
Bytes.toBytes(status)));
scan.setFilter(filterList);
AggregationClient aggregation = new AggregationClient(conf);
Long count = aggregation.rowCount(hTable,
new LongColumnInterpreter(), scan);
total = count.intValue();
hTable.close();
} catch (Throwable e) {
// TODO Auto-generated catch block
log.error(e);
}
return (total % pageSize == 0) ? total / pageSize
: (total / pageSize) + 1;
} /**
* 获取同一个rowkey下的记录集合
*
* @param result
* 结果集
* @return
*/
private Map<String, String> getRowByResult(Result result) {
if (result == null) {
log.error("===Parameter |result| should not be null,Please check!===");
return null;
}
Map<String, String> cellMap = new HashMap<String, String>();
for (Cell cell : result.listCells()) {
String rowkey = Bytes.toString(cell.getRowArray(),
cell.getRowOffset(), cell.getRowLength());
String cf = Bytes.toString(cell.getFamilyArray(),
cell.getFamilyOffset(), cell.getFamilyLength());
String qf = Bytes.toString(cell.getQualifierArray(),
cell.getQualifierOffset(), cell.getQualifierLength());
String value = Bytes.toString(cell.getValueArray(),
cell.getValueOffset(), cell.getValueLength());
cellMap.put(CpConstants.HBASE_TABLE_PROP_ROWKEY, rowkey);
cellMap.put(CpConstants.HBASE_TABLE_PROP_COLUMNFAMILY, cf);
cellMap.put(qf, value);
}
return cellMap;
} /**
* 获取HTableInterface
*
* @param tableName
* 表名
* @return 返回HTableInterface实例
*/
private HTableInterface getHTable(String tableName) {
if (StringUtils.isBlank(tableName)) {
log.error("===Parameter |tableName| should not be blank,Please check!===");
return null;
}
HTableInterface hTable = null;
try {
HConnection conn = HConnectionManager.createConnection(conf);
hTable = conn.getTable(Bytes.toBytes(tableName));
} catch (IOException e) {
// TODO Auto-generated catch block
log.error(e);
return null;
}
return hTable;
} /**
* 批量插入或更新
*
* @param tableName
* 表名
* @param paraList
* 组装成json或xml后的参数
* @return 成功-true 失败-false
*/
public boolean batchPut(String tableName, List<Map<String, String>> paraList) {
try {
List<Put> puts = new ArrayList<Put>();
for (Map<String, String> map : paraList) {
Put put = getPutByMap(map);
puts.add(put);
}
HTable hTable = (HTable) getHTable(tableName);
hTable.put(puts);
hTable.close();
} catch (RetriesExhaustedWithDetailsException e) {
// TODO Auto-generated catch block
log.error(e);
return false;
} catch (InterruptedIOException e) {
// TODO Auto-generated catch block
log.error(e);
return false;
} catch (IOException e) {
// TODO Auto-generated catch block
log.error(e);
return false;
}
return true;
} /**
* 根据map返回put
*
* @param paraMap
* 参数map
* @return 返回put
*/
private Put getPutByMap(Map<String, String> paraMap) {
if (paraMap == null) {
log.error("===Parameter |paraMap| should not be null,Please check!===");
return null;
}
Set<Entry<String, String>> set = paraMap.entrySet();
Iterator<Entry<String, String>> it = set.iterator();
byte[] rowkey = Bytes.toBytes(paraMap
.get(CpConstants.HBASE_TABLE_PROP_ROWKEY));
byte[] columnfamily = Bytes.toBytes(paraMap
.get(CpConstants.HBASE_TABLE_PROP_COLUMNFAMILY));
Put put = new Put(rowkey);
while (it.hasNext()) {
Entry<String, String> entry = it.next();
String key = entry.getKey();
if (!CpConstants.HBASE_TABLE_PROP_ROWKEY.equals(key)
&& !CpConstants.HBASE_TABLE_PROP_COLUMNFAMILY.equals(key)) {
String value = entry.getValue();
put.add(columnfamily, Bytes.toBytes(key), Bytes.toBytes(value));
}
}
return put;
} /**
* 使表具有聚合功能
*
* @param tableName
* 表名
*/
@SuppressWarnings("resource")
private void enableAggregation(String tableName) {
String coprocessorName = "org.apache.hadoop.hbase.coprocessor.AggregateImplementation";
try {
HBaseAdmin admin = new HBaseAdmin(conf);
HTableDescriptor htd = admin.getTableDescriptor(Bytes
.toBytes(tableName));
List<String> coprocessors = htd.getCoprocessors();
if (coprocessors != null && coprocessors.size() > 0) {
return;
} else {
admin.disableTable(tableName);
htd.addCoprocessor(coprocessorName);
admin.modifyTable(tableName, htd);
admin.enableTable(tableName);
}
} catch (TableNotFoundException e) {
// TODO Auto-generated catch block
log.error(e);
} catch (MasterNotRunningException e) {
// TODO Auto-generated catch block
log.error(e);
} catch (ZooKeeperConnectionException e) {
// TODO Auto-generated catch block
log.error(e);
} catch (IOException e) {
// TODO Auto-generated catch block
log.error(e);
}
}
}
Hbase 分页设计的更多相关文章
- hbase分页查询
为了广大技术爱好者学习netty,在这里帮新浪微博@nettying宣传下他出版的新书 <netty权威指南>@nettying兄在华为NIO实践多年,这本书是他的技术和经验的一个结晶.N ...
- 分布式数据库HBase表设计
比较常用的数据库是关系型数据库,但很多场景下nosql数据库会更加擅长,从sql到nosql实施的第一步就是设计表结构,这是两种不同的思维方式,这里说下HBase表设计. 需求:需要一张stock表用 ...
- HBase Rowkey 设计指南
为什么Rowkey这么重要 RowKey 到底是什么 我们常说看一张 HBase 表设计的好不好,就看它的 RowKey 设计的好不好.可见 RowKey 在 HBase 中的地位.那么 RowKey ...
- 【Hbase学习之四】Hbase表设计案例
环境 虚拟机:VMware 10 Linux版本:CentOS-6.5-x86_64 客户端:Xshell4 FTP:Xftp4 jdk8 hadoop-2.6.5 hbase-0.98.12.1-h ...
- hbase分页应用场景及分页思路与代码实现
转自:http://www.aboutyun.com/forum.php?mod=viewthread&tid=7030&extra=page=1 可以带着下面问题来阅读1.hbase ...
- 大数据学习(17)—— HBase表设计
为啥要把表设计拿出来独立成章?因为我觉得像我这样搞了很多年Java后端开发的技术人员,在学习HBase的时候,会受到关系型数据库3NF.BCNF的影响.事实上,数据库范式在HBase里完全没用,必须转 ...
- Hbase Rowkey设计
转自:http://www.bcmeng.com/hbase-rowkey/ 建立Schema Hbase 模式建立或更新可以通过 Hbase shell 工具或者使用Hbase Java API 中 ...
- Solr与HBase架构设计
摘要:本篇是本人在做一个大数据项目时,对于系统架构的一点总结,如何在保证存储量的情况下,又能保证数据的检索速度. 前提: Solr.SolrCloud提供了一整套的数据检索方案,HBase提 ...
- Angular简易分页设计(一):基本功能实现
(首先声明本文来自博客园本人原创,转载请说明出处.欢迎关注:http://www.cnblogs.com/mazhaokeng/p/6752990.html) 之前网站的后台管理为了图快,把Jquer ...
随机推荐
- 树莓派4B基本配置
一.系统安装 官网下载好系统解压,使用SD Card Formatter格式化内存卡 # 查看内存卡状态,通过内存卡大小判断是哪个 df -lh # 卸载内存卡 diskutil unmount /d ...
- golang 使用 protobuf 的教程
1.下载protobuf的编译器protoc 地址: https://github.com/google/protobuf/releases window: 下载: protoc-3.3.0-w ...
- Linux中常用命令cat
cat可谓是随处可用,全名:concatenate and print files.用于连接和打印文件,按序读取文件内容,然后输出到标准输出.如果文件是单个破折号-或者缺乏文件,cat将读取标准输入. ...
- C#中真正的属性
引言 我们以前课堂上说的类当中的“属性”,其实官方叫法是“字段”或者“域”域(Field).正常使用,把它们当属性理解更加方便快捷,也没有什么问题. 如果要在微软的mvc中充分利用类带来的便利,就有必 ...
- mybatis关联映射一对多
实际项目中也存在很多的一对多的情况,下面看看这个简单的例子 table.sql CREATE TABLE tb_clazz( id INT PRIMARY KEY AUTO_INCREMENT, CO ...
- 巧记 In/hasOwnProperty/for…in/for…of/forEach区别
写在前面 上面提到的这些东西,反复的记忆,反复的忘记.现分享一种巧记方法,有需要,请参考,希望你也过目不忘. 仅提供巧记思路.仅提供巧记思路.仅提供巧记思路. 1. in 用于判断属性是否在对象上(包 ...
- react 爬坑记录
1.父子组件优化其一发生render条件:数据改变(state或者props改变),有时子组件会过多render.这时可在子组件里面的生命周期钩子里执行 shouldComponentUpdate(n ...
- Java中遇到Case cannot be resolved to a variable问题
Java中遇到Case cannot be resolved to a variable问题 记录一下这两天项目中遇到的一个小问题. 在项目中遇到一个问题,一直显示 Case cannot be ...
- Golang: 接收GET和POST参数
GET 和 POST 是我们最常用的两种请求方式,今天结合前端 axios 请求库来讲一讲,如何在 golang 服务中,正确接收这两种请求的参数信息. 一.搭建一个简单的服务 首先,我们来创建一个最 ...
- Redis中的LFU算法
在Redis中的LRU算法文中说到,LRU有一个缺陷,在如下情况下: ~~~~~A~~~~~A~~~~~A~~~~A~~~~~A~~~~~A~~| ~~B~~B~~B~~B~~B~~B~~B~~B~~ ...