随手记录第三话 --你见过哪些神乎其乎的存储方式?

作为一个后端开发,那必要的存储肯定是要的。
随着业务的不同,数据类型也是各种各样,接口需要也是各种各样,存储的方式也就慢慢的多了起来。

本文主要记录工作中不同的业务场景下,不同的存储方式。

1.内存存储

内存存储应该是最轻便也是最简单的存储了,一起来看看代码的实现。

static Map<String, Object> map = new HashMap<>();
map.put("name", "张三" + new Date().getTime());
map.put("age", 19);
map.put("crad", new Date().getTime());

在当前服务中存在一个静态的Map容器,对一些不是太重要的数据进行延迟刷库等操作。
优点:轻便,读取速度快
缺点:声明周期跟随服务,服务重启容器也重置

2.磁盘存储

数据最终还是要落入磁盘的,不然就要考虑丢失问题,一起来看看磁盘怎么存储的。

public static void main(String[] args) throws Exception {
    File file = new File("/test/123.txt");
    //写入流 写入数据
    FileOutputStream outputStream = new FileOutputStream(file);
    outputStream.write("123456".getBytes());
    outputStream.write("\n78901".getBytes());
    outputStream.write("\nasdfdf".getBytes());
    outputStream.flush();
    outputStream.close();

    StringBuilder builder = new StringBuilder();
    int len;
    byte[] bytes = new byte[1024];
    //读取文本中数据
    FileInputStream inputStream = new FileInputStream(file);
    while ((len = inputStream.read(bytes)) != -1) {
        builder.append(new String(bytes, 0, len));
    }
    inputStream.close();
    System.out.println(builder.toString());
}

这种存储方式在单机模式下还是挺实用的,至少数据比较稳妥,不会丢。
缺点:磁盘io和内存io在读写速度上有这天差地别,高频率下磁盘读写肯定有瓶颈

3.redis存储

前面两种都是基于本机的存储,第一种甚至做不到多服务间交互,随着服务的演变,分布式的存储肯定必不可少的
先来说说中间件redis存储

Jedis jedis = new Jedis("192.168.0.100", 6379, 10000000);
jedis .select(14);
jedis .set("test", "test_value");

redis作为一个特殊的多路复用网络模型,在读写上多线程读,单线程写,在性能上正常机器能达到秒Qps 10W加,基本上是项目中必备的了。
缺点:纯内存的存储,但无法做关系型数据搜索

4.数据库存储

数据库的种类繁多,也就不一一介绍了,只要贴下mysql的代码

@Autowried
UserMapper userMapper;

Map<String,Object> map = new HashMap();
map.put("user_id","ud112");
map.put("name","张三");
//insert into user_info(`user_id`,`name`) values (#{user_id},#{name})
userMapper.insert(map);

关系型的数据库能够快速的查询出想要的数据,建立合适的索引可以快速的扫描数据。

5.文件型数据库存储

首先看下MongoDb存储的代码

//实体类上添加@Document("student") 其他和JPA一样的操作
Student student = new Student();
student.setUid(new Date().getTime());
student.setUsername("张三");
student.setAge(19);
student.setCreateTime(new Date());

Student insert = mongoTemplate.insert(student);
log.info("插入数据成功:{}", insert);

mongoDb单个的key/value存储和redis类似,但mongoDb可以存储文件,这个是redis做不到的

6.elasticsearch搜索引擎存储

在微服务时间一直有一个痛点,在订单服务要对用户做条件搜索,在没有es之前这真是一个痛点,但是在有了es这样一个分布式的搜索引擎之后,这个问题也迎刃而解了。
一起来看看代码

//在索引test_index上存储一条数据
User user = new User();
user.setName("世界上最好的语言php.java");
user.setAge(19);
user.setAddr("老北京");
user.setInfo("老北京信息");
IndexResponse response = elasticsearchClient.index(a -> a
        .index("test_index")
        .id("0").document(user));
log.info("response:{}", response.seqNo());

//查询and多条件匹配数据 name包含语言 and age=17
SearchResponse<JSONObject> response1 = elasticsearchClient.search(s -> s
                .index("test_index")
                .query(q -> q.bool(t ->
                        t.must(m -> {
                            m.match(a -> a.field("name").query("语言"));
                            m.match(a -> a.field("age").query(117));
                            return m;
                        })
                )),
        JSONObject.class);
       Iterator<Hit<JSONObject>> iterator = response1.hits().hits().iterator();
while (iterator.hasNext()) {
    Hit<JSONObject> decodeBeanHit = iterator.next();
    JSONObject docJson = decodeBeanHit.source();
    log.info("response_all:{}", docJson.toJSONString());
}

elasticsearch的api使用起来比较复杂的,下一篇文章会专门记录一下

7.zookeeper存储

zookeeper的特色,节点唯一,临时节点和过期回调

public static void init(){
  try {
	 //连接到zk
      zooKeeper = new ZooKeeper(address, 60000,
              watchedEvent -> {
              });
      while (true) {
          synchronized (object) {
              for (int i = 0; i < nodeNum; i++) {
                  String path = defPath + i;
                  try {
                  	//创建一个临时节点 如果不成功则添加监听 过期后会回调到监听
                      String s = zooKeeper.create(path, "1".getBytes(),
                              ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
                      //取消监听
                      zooKeeper.exists(path, false);
                      log.info("创建节点成功:{}", s);
                      return;
                  } catch (KeeperException ex) {
                      log.error("节点已存在,添加监听并等待节点事件:{}", path);
                      addZkWatch(path, zooKeeper);
                  } catch (Exception e) {
                      log.error("执行异常:", e);
                      throw new RuntimeException("zk初始化失败");
                  }
              }
              object.wait();
          }
      }
  } catch (Exception e) {
      log.error("初始化zk异常!");
      throw new RuntimeException(e);
  }
}


private static void addZkWatch(String path, ZooKeeper zooKeeper) {
  log.info("添加监听:{}", path);
  try {
      zooKeeper.exists(path, watchedEvent -> {
          synchronized (object) {
              String eventPath = watchedEvent.getPath();
              Watcher.Event.EventType eventType = watchedEvent.getType();
              log.info("收到节点事件======{}", eventPath, eventType);
              if (Watcher.Event.EventType.NodeDeleted.equals(eventType)) {
                  log.info("发现节点删除,唤醒服务抢占");
                  object.notifyAll();
              } else {
                  //原生包由map存储事件只会调用一次,处理过事件之后需要重新监听
                  addZkWatch(path, zooKeeper);
              }
          }
      });
  } catch (Exception e) {
      throw new RuntimeException(e);
  }
}

上述代码则是在已有项目中用zookeeper做备用节点的案例,如果服务宕机则会唤醒备用节点。

存储方式各有差异,也各有千秋,这里主要是挑了比较常见的存储方式。
至于合适主要还是要跟业务相关联,才能最大的力度的使用。

如果还有其他的方式实现的,欢迎评论区留言哦
以上就是本章的全部内容了。

上一篇:随手记录第二话 – 高并发情况下秒杀、抢红包都有哪些实现方式?
下一篇:随手记录第四话 – elasticsearch基于docker安装以及SpringBoot集成使用

问渠那得清如许,为有源头活水来

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值