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


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

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

    1.内存存储

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

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

    在当前服务中存在一个静态的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());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

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

    3.redis存储

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

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

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    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);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

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

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

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

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

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

  • 相关阅读:
    docker build xxx --build-arg设置http_proxy环境变量
    golang中的字符串
    【小程序专栏】个人及企业资质分别该如何注册小程序
    基于C++的社交应用的数据存储与实现
    STM32 cubemx hal库huart串口接收不到第一帧数据或数据全为0的问题
    学习操作系统之分时系统+时间片
    Notepad++ 和正则表达式 只保留自己想要的内容
    【华为机试真题 JAVA】猴子吃桃-200
    【数据库原理与应用(第3版)】第一章:数据库系统概论(选择与填空)
    【时间之外】EA交易代码入门
  • 原文地址:https://blog.csdn.net/qq_35551875/article/details/125897507