上一篇中我们了解了bitset索引的基本用法,本篇开始学习bitset索引更新及一些复杂查询。

1.bitset索引更新

  因为我们的数据是在系统启动时全部加载进内存,所以当数据库数据发生变化时要实时通知到内存,可以使用消息队列的方式实现:将新增或者修改的数据写入kafka,然后在索引服务中从kafka中读出数据更新索引.

在UserIndexStore类中增加更新索引方法:

/**
* 更新索引
* @param user
*/
public void updateIndex(User user) {
String name = user.getName();
Integer userIndex = this.nameIndexMap.get(name);
if (userIndex != null) {
clear(userIndex);//清除bitset对应位置的值
update(user, userIndex);
this.userMap.put(userIndex, user);
this.nameIndexMap.put(user.getName(), userIndex);
} else {
//新增时会有并发问题
synchronized (this) {
int index = this.userMap.size() + 1;
createIndex(user, index);
}
} } private void update(User user, Integer userIndex) {
getAddress().update(user.getAddress(), userIndex);
getAge().update(user.getAge(), userIndex);
getGender().update(user.getGender(), userIndex); } private void clear(Integer index) {
getAddress().clear(index);
getAge().clear(index);
getGender().clear(index);
}

在BitSetIndexModel类中增加clear()方法

    /**
* 对第i位置0
* @param i
*/
public void clear(Integer i) {
for (BitSet bs : bsList) {
if (bs != null && i < bs.length()) {
bs.clear(i);
}
}
}
2.bitset进阶查询

'>=','<=',between and

在BitSetIndexModel类中增加如下方法:

public List<String> getHigherList(String str) {
List<String> newlist = new ArrayList<String>();
newlist.add(str);
newlist.addAll(list);
// 排序
Collections.sort(newlist);
// 查找str在list中的位置
int fromIndex = Collections.binarySearch(newlist, str);
if (fromIndex >= 0) {
// 如果map中不包含当前值则 index后移一位
if (map.get(str) == null) {
fromIndex++;
}
return newlist.subList(fromIndex, newlist.size());
} else {
return new ArrayList<String>();
}
} public List<String> getLowerList(String str) {
List<String> newlist = new ArrayList<String>();
newlist.add(str);
newlist.addAll(list);
// 排序
Collections.sort(newlist);
// 查找str在list中的位置
int endIndex = Collections.binarySearch(newlist, str);
if (endIndex >= 0) {
return newlist.subList(0, endIndex + 1);
} else {
return new ArrayList<String>();
}
} @SuppressWarnings("unchecked")
public <T extends Comparable<? super T>> List<T> getRange(T min, T max, Comparator<? super T> c) {
List<T> newlist = new ArrayList<T>();
for (String s : list) {
newlist.add((T) (s));
}
Collections.sort(newlist);
// 查找str在list中的位置
int fromIndex = minBinarySearch(newlist, min, c);
int endIndex = maxBinarySearch(newlist, max, c);
if (fromIndex >= 0 && endIndex <= list.size() - 1) {
if (fromIndex == endIndex) {
return newlist.subList(fromIndex, endIndex + 1);
}
return newlist.subList(fromIndex, ++endIndex);
} else {
return new ArrayList<T>();
}
} /**
*
* @param list
* @param key
* @return
*/
private static <T> int maxBinarySearch(List<T> list, T key, Comparator<? super T> c) {
int low = 0;
int high = list.size() - 1;
int mid = 0;
while (low <= high) {
mid = (low + high) >>> 1;
T midVal = list.get(mid);
int cmp = c.compare(midVal, key);
if (cmp < 0) {
low = mid + 1;
} else if (cmp > 0) {
high = mid - 1;
} else {
return mid; // key found
}
}
if (mid == low) {
return high;
} else {
return mid;
}
} private static <T> int minBinarySearch(List<T> list, T key, Comparator<? super T> c) {
int low = 0;
int high = list.size() - 1;
int mid = 0;
while (low <= high) {
mid = (low + high) >>> 1;
T midVal = list.get(mid);
int cmp = c.compare(midVal, key);
if (cmp < 0) {
low = mid + 1;
} else if (cmp > 0) {
high = mid - 1;
} else {
return mid; // key found
}
}
if (high == mid) {
return low;
} else {
return mid;
}
}

在UserIndexStore中增加以下方法

/**
* 查询年龄大于等于指定值的user索引
* @param age
* @return
*/
public BitSet findUserByAgeHigher(String age) {
BitSetIndexModel indexModel = getAge();
List<String> strs = indexModel.getHigherList(age);
BitSet bitset = null;
for (String str : strs) {
bitset = indexModel.or(str, bitset);
}
return bitset;
} /**
* 查询age小于等于指定值的user索引
* @param age
* @return
*/
public BitSet findUserByAgeLower(String age) {
BitSetIndexModel indexModel = getAge();
List<String> strs = indexModel.getLowerList(age);
BitSet bitset = null;
for (String str : strs) {
bitset = indexModel.or(str, bitset);
}
return bitset;
} /**
* 查询age在某两个值区间内的user索引
* @param min
* @param max
* @return
*/
public BitSet findUserByAgeBetweenAnd(String min, String max) {
BitSetIndexModel indexModel = getAge();
List<String> strs = indexModel.getRange(min, max, new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) { return Integer.valueOf(o1 == null ? "0" : o1.toString()).compareTo(Integer.valueOf(o2 == null ? "0" : o2.toString()));
}
});
BitSet bitset = null;
for (String str : strs) {
bitset = indexModel.or(str, bitset);
}
return bitset;
}
测试,查询年龄在16-17之间的北京女孩。
public class BitSetTestRange {

	public static void main(String[] args) {
List<User> users = buildData();
UserIndexStore.getInstance().createIndex(users);
ExecutorService executorService = Executors.newFixedThreadPool(50);
int num = 2000;
long begin1 = System.currentTimeMillis();
for (int i = 0; i < num; i++) {
Runnable syncRunnable = new Runnable() {
@Override
public void run() {
BitSet bs = UserIndexStore.getInstance().query("北京", "girl", null);
BitSet ageBs = UserIndexStore.getInstance().findUserByAgeBetweenAnd("16", "17");
bs.and(ageBs);
for (Integer index : BitSetIndexModel.getRealIndexs(bs)) {
UserIndexStore.getInstance().findUser(index);
}
}
};
executorService.execute(syncRunnable);
}
executorService.shutdown();
while (true) {
try {
if (executorService.awaitTermination(1, TimeUnit.SECONDS)) {
System.err.println("单次查询时间为:" + (System.currentTimeMillis() - begin1) / num + "ms");
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} private static List<User> buildData() {
String[] addresss = { "北京", "上海", "深圳" };
String[] ages = { "16", "17", "18" };
List<User> users = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
User user = new User();
user.setName("name" + i);
int rand = ThreadLocalRandom.current().nextInt(3);
user.setAddress(addresss[ThreadLocalRandom.current().nextInt(3)]);
user.setGender((rand & 1) == 0 ? "girl" : "boy");
user.setAge(ages[ThreadLocalRandom.current().nextInt(3)]);
users.add(user);
}
return users;
} }
单次查询时间为:22ms

相比"="查询,区间查询速度慢了一些,但还在预期之内。


总结

  以上就实现了一个bitset索引,支持索引创建,更新,查询。并且因为没有传统的网络传输和磁盘io,所以速度非常快,基本上响应时间都在10ms以内。如果需要我可以在下一篇使用spring cloud搭建一个较完整的demo,供大家参考使用。

使用bitset实现毫秒级查询(二)的更多相关文章

  1. 使用bitset实现毫秒级查询

    前言 因为业务要求api的一次请求响应时间在10ms以内,所以传统的数据库查询操作直接被排除(网络io和磁盘io).通过调研,最终使用了bieset,目前已经正常运行了很久 *** bitset介绍 ...

  2. 双汇大数据方案选型:从棘手的InfluxDB+Redis到毫秒级查询的TDengine

    双汇发展多个分厂的能源管控大数据系统主要采用两种技术栈:InfluxDB/Redis和Kafka/Redis/HBase/Flink,对于中小型研发团队来讲,无论是系统搭建,还是实施运维都非常棘手.经 ...

  3. mysql搭建亿级cmd5数据库,毫秒级查询 完全过程

    前言: 最近也在玩数据库,感觉普通机子搞数据库,还是差了点,全文查找,慢的要查一分钟更久. 但是搞cmd5库很不错,亿级数据库,毫秒级. qq 944520563好吧,下面开始,首先你得需要一个mys ...

  4. OpenTSDB查询和写入毫秒级数据

    由于OpenTSDB没有支持Java的SDK进行调用,所以基于Java开发OpenTSDB的调用将要依靠HTTP请求的方式进行. 1.毫秒级数据写入 /api/put:通过POST方式插入JSON格式 ...

  5. ES 调优查询亿级数据毫秒级返回!怎么做到的?--文件系统缓存

    一道面试题的引入: 如果面试的时候碰到这样一个面试题:ElasticSearch(以下简称ES) 在数据量很大的情况下(数十亿级别)如何提高查询效率? 这个问题说白了,就是看你有没有实际用过 ES,因 ...

  6. Elasticsearch如何做到亿级数据查询毫秒级返回?

    阅读本文大概需要 6 分钟. 如果面试的时候碰到这样一个面试题:ES 在数据量很大的情况下(数十亿级别)如何提高查询效率? 这个问题说白了,就是看你有没有实际用过 ES,因为啥?其实 ES 性能并没有 ...

  7. sqlserver 父子级查询(理念适应所有数据库)

    实现技术: 存储过程   ,零时表(3) 一句话说完 :把父级查询下来的子级ID 保存成零时表,并且将符合子级ID数据添加到另一张零时表. 同时清空数据时需要使用到一张零时表作为容器: alter P ...

  8. SNF快速开发平台MVC-高级查询组件

    1.   高级查询 在我们做项目的时候经常想要按名称.编号进行查询数据,可在开发时会把最常用的查询条件写上,不常用的就不写了,也是因为把所有字段都写上太多了,布局不好看而且不实用.还有些查询条件几百年 ...

  9. php Swoole实现毫秒级定时任务

    项目开发中,如果有定时任务的业务要求,我们会使用linux的crontab来解决,但是它的最小粒度是分钟级别,如果要求粒度是秒级别的,甚至毫秒级别的,crontab就无法满足,值得庆幸的是swoole ...

随机推荐

  1. 201521123106《java程序设计》第二周学习总结

    1. 本周学习总结 学习了java的基础语法.在java中使用浮点型会不精确,改用double行就好.学习了string的类型,string的对象是不可变的,创建之后不能再修改,在string的拼接中 ...

  2. 201521123060《Java程序设计》第1周学习总结

    1. 本章学习总结 认识和了解了Java的发展进程: 了解了相关开发工具: 认识了JVM,JRE,JDK: 2. 书面作业 Q1.为什么java程序可以跨平台运行?执行java程序的步骤是什么?(请用 ...

  3. 201521123072《java程序设计》第十三周学习总结

    201521123072<java程序设计>第十三周学习总结 1. 本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 2. 书面作业 1. 网络基础 ...

  4. 201521123027<java程序设计>第14周作业总结

    1.本周作业总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多数据库相关内容. 2.书面作业 Q1. MySQL数据库基本操作 建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现自己 ...

  5. 201521123027 <java程序设计>第十二周作业总结

    1.本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2.书面作业 将Student对象(属性:int id, String name,int age,double ...

  6. js的原型

    在讲js的原型之前,必须先了解下Object和Function. Object和Function都作为JS的自带函数,Object继承自己,Funtion继承自己,Object和Function互相是 ...

  7. Nim函数调用的几种形

    Nim函数调用的几种形式 Nim 转载条件:如果你需要转载本文,你需要做到完整转载本文所有的内容,不得删改文内的作者名字与链接.否则拒绝转载. 关于nim的例行介绍: Nim 是一门静态编译型的系统级 ...

  8. ASP.Net开发WebAPI跨域访问(CORS)的精简流程

    1: Web.config里有一行: <remove name="OPTIONSVerbHandler" /> 这个要删除. 2: nuget安装Microsoft.A ...

  9. JVM菜鸟进阶高手之路二(JVM的重要性,Xmn是跟请求量有关。)

    转载请注明原创出处,谢谢! 今天看群聊jvm,通常会问ygc合适吗? 阿飞总结,可能需要2个维度,1.单位时间执行次数,2.执行时间 ps -p pid -o etime 查看下进程的运行时间, 17 ...

  10. 编号中的数学_KEY

    题目描述: 从美国州际高速公路建筑者那里,奶牛们引进了一种路径编号系统,来给牧场之间的道 路编号.他们已经把 N(1<=N<=25)个牧场,用 1 到 N 的整数编号.现在他们需要将牧场间 ...