1 | // 切换镜像地址 |
Java的动态代理
1 | // 定义接口 |
python的一些工具类
使用 pandas 读取 excel,返回 Json 格式
1 | import pandas as pd |
使用 pandas 保存字典到 excel
1 | import pandas as pd |
读取 csv 文件,返回 Json 格式
1 | def csv2Json(path): |
字典保存 csv 文件
1 | import csv |
字典列表根据某个字段排序
1 | def sort_dict_list(dict_list, key, reverse=False): |
字典列表根据某个字段分组
1 | from operator import itemgetter |
合并多个 excel 文件
1 | import xlrd |
一行代码读取文件每一行到列表
1 | kws = [line.strip() for line in open('keywords.txt', encoding='UTF-8').readlines()] |
时间转换工具
1 | # -*- coding:utf-8 -*- |
时间工具
1 | import datetime |
cookies 格式转换
1 | def get_cookies(): |
Java 文件输入输出
1 | // File类 |
面试系列(二)分布式搜索引擎
ES 的分布式架构原理
存储数据的基本单位是索引,每个索引拆分成多个 shard,每个 shard 存储部分数据
每一份 shard 都有其副本 replica,而且 replica 不在同一台机器上
在写入数据时,会将 primary shard 的数据同步到 replica shard,数据分布式存储
数据结构:index->type->mapping->docment->field
index:相当于一张表
type: 一个 index 有多个 type,每个 type 的字段差不多,可以有细微差别
mapping:表结构
docment:表中的一行数据
field:字段
es 集群有多个节点,会自动选举出 master 节点,作用有维护索引元数据,切换 primary shard 和 replica shard 等等。
- master 宕机会重新选举出新 master,新 master 上的属于宕机节点的 replica shard 会被提拔成 primary shard。宕机故障修复后,取消宕机机器的 master 身份,将宕机机器上的 primary shard 降级至 replica shard。
- 非 master 宕机,master 会将宕机节点上的 primary shard 转移到其他机器的 replica shard。故障修复后,宕机机器上的 primary shard 降级至 replica shard,并同步数据。
ES 写入查询数据的工作原理
- 写数据总体原理
- 客户端选择一个节点发送请求,这个节点会变成 coordinating node(协调节点)
- 协调节点对请求进行路由,将请求转发到对应的节点(有 primary shard)
- 实际收到请求的节点的上的 primary shard 处理请求,并将数据同步到其他 replica
- 协调节点发现数据已经在 primary shard 和所有 replica shard 上,返回响应给客户端
- 写数据到 shard 原理
- 先将数据写入内存 buffer,并将数据写入 translog 日志文件
- 如果内存 buffer 或者每隔一段时间(默认 1s),将 buffer 数据refesh到新的 segment file(磁盘文件)中,当 segment file 越来越多时,会定时执行merge操作,如果 buffer 没有数据则不会执行 refresh 操作。
- 写入 segment file 磁盘文件前会先写入 os cache(操作系统缓存)
- 每写入一条数据到 buffer,同时每隔 5s 写入日志到 translog,会导致 translog 文件不断变大,当 translog 大到一定程度,会进行commit操作
- commit 操作(默认每隔 20min)(ES api 中的flush操作)
- 将现有数据 refresh 到 os cache 中,清空 buffer
- 将 commit point 写入磁盘,里面标识 commit point 对应的所有 segment file
- 强行将 os cache 中所有数据 fsync 到磁盘文件中
- 将现有的 translog 清空,重新启用一个 translog
- es 丢数据问题
- 准实时,数据每隔 1s 才能搜索到
- 有 5s 的数据停留在 buffer,translog os cache,segment os cache 中,不在磁盘中,有机会导致 5s 的数据丢失。
- 删除数据原理
- 将数据写入.del 文件,标识数据被删除,客户端搜索出某条数据,一旦发现数据在.del 文件中已经标识成已删除,就不会出现在搜索结果
- 物理删除。每次执行 merge 操作时,会将多个 segment file 合并成一个,同时将标识成 delete 的数据物理删除掉,然后将新的 segment file 写入磁盘,同时写一个 commit point,标识所有新的 segment file,打开 segment file 供搜索使用,删除旧的 segment file。
- es 读数据过程
- GET 某条数据,写入某个 document,这个 document 分配一个全局唯一 id,同时根据 doc id 进行 hash 路由到对应的 primary shard,也可以手动指定 doc id。
- 可以通过 doc id 查询,根据 doc id 进行 hash,判断 doc id 在哪个 shard 上面,从其中查询。
- es 全文搜索
- 客户端发送请求到协调节点
- 协调节点将请求发送到所有 shard 对应的 primary shard 或者 replica shard
- query phase:每个 shard 将自己的搜索结果(doc id)返回给协调节点,有协调节点进行数据合并,排序,分页等操作,产出最终结果
- fetch phase:协调节点根据 doc id 去各节点拉取实际 document 数据,返回给客户端
ES 在数据量很大的情况下(数十亿级别)如何提高查询性能
filesystem cache
根据节点内控制 es 数据量。仅仅将少数检索字段放入 es,其他字段写入 hbase(适用于海量数据的在线存储,根据 id 查询)或者直接放 mysql
数据预热(手动搜索热数据)
冷热分离(字段水平拆分,冷热数据分开放到不同的索引)
document 结构,尽量不适用复杂的关联查询,在写入 es 前就进行关联操作,将关联好的数据写入 es
分页性能优化
不允许深度分页,规定最大页数
scroll 游标获取下一页数据,但是不能指定页数
ES 生产集群部署架构?每个索引的数据量大小?每个索引有多少分片?
- 5 台集机器,6 核 64g,总内存 320g
- 日增量数据大概 2000 万条,数据量 500mb,月增量 6 亿,15g,系统运行了几个月,es 集群有 100g 数据
- 线上有 5 个索引,每个索引的数据量大概有 200g,每个索引分配 8 个 shard,比默认 5 个 shard 多 3 个 shard
面试系列(一)消息队列
应用场景(使用场景,项目具体使用场景,MQ 类型)
- 解耦 (一个系统调用多个系统,或者多个系统相互调用,并且不需要同步调用,使用 MQ 做解耦)
- 异步 (一个系统调用完,有多个耗时动作,使用 MQ 异步调用加快运行效率)
- 削峰 (一段时间内有大量请求涌入系统,并且同时写入数据库,导致系统崩溃)
缺点
- 系统可用性减低 (MQ 一旦出问题,与之关联的系统都会出现问题)
- 系统复杂度变高 (需要考虑数据幂等,消息顺序性,数据积压在 MQ 中,如何快速消费等)
- 数据一致性问题 (一个请求有多个动作,其中一个动作失败,但是整个请求返回成功,导致一致性问题)
MQ 技术选型(Kafka,ActiveMQ,RabbitMQ,RocketMQ)
ActiveMQ | RabbitMQ | RocketMQ | Kafka | |
---|---|---|---|---|
单机吞吐量 | 万级 | 万级 | 十万级 | 十万级(吞吐量最高) |
topic 数量对吞吐量的影响 | topic 可以到几百,几千个的级别吞吐量会小幅降低 | topic 从几十个到几百个的时候,吞吐量会大幅度下降 | ||
时效性 | ms | 微秒级(延时最低) | ms 级 | ms 级 |
可用性 | 高,基于主从实现 | 高,基于主从实现 | 非常高,分布式架构 | 非常高,分布式架构,一个数据多个副本 |
消息可靠性 | 有较少概率丢失数据 | 经过参数优化可做到不丢失数据 | 经过参数优化可做到不丢失数据 | |
核心特点 | 功能极其完备 | 基于 erlang 开发,并发高,性能好,延时低 | 功能较为完备,分布式,拓展性好 | 功能较为简单,在大数据领域的实时计算以及日志采集被大规模使用 |
总结 | 成熟,功能强大。偶尔会出现丢失消息,社区不活跃 | 性能好,延时低,管理界面好用。但是吞吐量比较低,erlang 源码定制比较困难,比较难定制,集群动态拓展麻烦 | 接口简单易用,吞吐量高,使用 java 开发,容易定制,分布式架构,拓展性好 | 功能较少,但吞吐量高,易于拓展,适用于大数据 |
高可用
RabbitMQ(集群)
单机模式
普通集群模式
多个 RabbitMQ 实例,元数据(配置信息)所有实例中都存在,实际消息数据只保存在主节点中。
消费者要在从节点中消费数据,实际上是从节点向主节点拉取数据,再让消费者消费。
缺点:
集群内部会存在大量数据传输;
可用性几乎没有保障(如果主节点数据丢失,导致整个集群无法消费)
镜像集群模式
元数据和消息数据在所有节点都存在,生产者想一个节点生产数据,会同步到所有节点中。
缺点:
- 实际不是分布式,消息数据过于庞大,超过服务器容量,无法解决!
Kafka(分布式)
每一个 Kafka 在每台服务器上启动一个Broker进程。
在 Kafka 集群中创建一个Topic,指定其Partition的数量为 3,则可认为每个 Partition 承载该 Topic 的一部分数据,并且分布在 3 台服务器上,生产者向 Topic 生产数据会分布在 3 个 Partition 中,分布在不同的服务器上。
通过Replica 副本机制实现高可用。每个 Partition 都有各自的 Replica 副本,每个 Partition 可能有多个 Replica 副本,实际上 Partition 也是一个副本,分布在其他服务器上。
Partition 下所有副本会选举一个 Leader 和多个 Follower,其中只有 Leader 能对外提供读写(生产者只能往 Leader 里面写数据,写数据到 Leader 后,Leader 会将数据同步到 Follower)
高可用体现在当 Leader 宕机后,会从 Follower 中选举出新 Leader。
幂等性(重复消费/消息的重复发送)
- Kafka(为什么会出现消息重复消费)
- 生产者生产的消息都带有一个 offset,代表消息的顺序(进入 Kafka 的顺序)
- 消费者按照 offset 的顺序消费,定期会提交 offset(告诉 Kafka 该 offset 的数据已经消费了,基于 zk 实现)
- 因为 offset 是定期提交的,消费者重启会导致期间的 offset 不会被提交,Kafka 不会知道消息已经被消费,消费者重启完成后 Kafka 再次发送那条没有提交 offset 但是重复的消息。
- 保证幂等的几个方法
- Set
- Redis
- 消息中带着全局唯一 ID
- 基于数据库唯一键
数据丢失问题
RabbitMQ
- 生产者生产消息由于网络传输问题或者 RabbitMQ 内部出错造成数据丢失
- 消息已经保存至 RabbitMQ 内存中,但消费者消费之气 MQ 挂掉,造成数据丢失
- 消费者已经收到消息,但是没处理就挂掉,RabbitMQ 以为已经处理完,造成数据丢失
解决办法:
- 生产者丢失数据
基于 RabbitMQ 提供的事务功能(同步机制,性能不好)
1
2
3channel.txSelect // 开启事务
channel.txCommit // 提交事务
channel.txRollback // 回滚事务设置 channel 为 confirm 模式(异步机制,回调)
在 RabbitMQ 收到消息后,会回调生产者的一个接口,通知生产者收到消息。如果接受失败,也会回调接口通知生产者失败。
- RabbitMQ 丢失数据
- 持久化磁盘(queue 设置持久化;消息的 deliveryMode 设置成 2,设置消息持久化)
- 出现消息在内存,没持久到磁盘的情况,也会小概率造成小部分消息丢失。
- 消费者丢失数据(消费者打开 autoAck 机制)
- 关闭 autoAck,手动发送 ack
Kafka
- 消费者丢失数据
- 关闭 Kafka 自动提交 offset,手动提交 offset
- Kafka 丢失数据
- leader 没有将数据同步给 fellower 就挂掉,选出新 leader 后,数据就丢了
- 设置 replication.factor > 1 (每个 partition 至少有两个副本)
- 设置 min.insync.replicas > 1 (leader 至少要跟 1 个 fellower 一直联系到)
- 生产者设置 acks=all (每条数据必须写入所有 replica 才算成功)
- 生产者设置 retries=MAX (写入失败,无限重试)
- 生产者丢失数据
- 设置 acks=all,retries=MAX 就不会丢数据
消息顺序性
- 顺序出错的场景
- RabbitMQ (一个 queue,多个消费者)
- Kafka (一个 Topic 一个 Partition 一个消费者内部多线程消费消息)
- 解决办法
- RabbitMQ:每个消费者使用一个 queue
- Kafka:每个线程一条内存队列,需要顺序的数据设置同一个 id,每条数据 id 都进行 hashcode,需要顺序的数据进入相同的队列
消费端问题
- 消息队列延时以及过期失效
- 生产环境不能设置过期时间
- 手动重新导入 MQ,再消费
- 消息队列满了,怎么办
- 消费到的消息直接写到临时队列接着消费
- 修改消费者,直接丢掉,快速消费消息,问题解决后重新导入 MQ,再消费
- 几百万消息积压几个小时,怎么快速消费积压消息
- 尽快修复消费者的问题,确保恢复消费速度
- 新建一个 Topic,partition 是原来的 10 倍,临时建立原来 10 倍或 20 倍的 queue 数量
- 将原来的消费者修改为直接写入消息到新 Topic,再部署 10 倍数量的消费者消费新 Topic 里面的消息,消费完积压消息就能恢复原来的消费者正常消费消息。
如何设计一个消息队列
分布式(增加吞吐量和容量)
参考 Kafka,broker->topic->partition,通过调整个组件的数量,完成数据迁移,实现分布式。
持久化
参考 Kafka,顺序读写
可用性
参考 Kafka,多副本,leader->fellower 机制,leader 挂了重新选举
数据可靠性,怎么做到数据零丢失
关于Python时间的那些事
新年快乐,马上要猪年了
猪年第一更
记录一下 python 里面关于时间的一些事
1 | # -*- coding:utf-8 -*- |