数据库知识小记

数据库知识小记 最近任务涉及到要在redis里面存储结构化数据,并且还要支持范围查询,同时方案设计时还需要考察各类数据库,因此调查了一下目前业界的数据库。 目前业界数据库种类繁多,根据使用方法不同可以分为: SQL: Mysql, PostgresQL, SQLServer, Oracle NoSQL: 不同传统的SQL数据库,又分成了: 文档型数据库:CouchDB, MongoDB, ElasticSearch等 图数据库:JanusGraph等 键值数据库: ETCD, Redis, memcached, Zookeeper, Consul 列族数据库:Cassandra, BigTable, HBase 时序型数据库:InfluxDB, OpenTSDB, Prometheus, m3db等等 NewSQL: Tidb 数据库太多,这里不一一枚举了,有比如kv下面有非常多的数据库,它们背后落盘的方式都不太相同,但是大致可以分为几种: HASH:键值类数据常用,优点是易维护,查询快,缺点是范围查询太慢,节点的增删成本高,涉及到 Reshard 。 B树:很多存储都用了B树的方式去构建数据,典型的比如Mysql(B+),Mongo(B-),优点是增删成本低,缺点是查询速度不如 HASH, O1 vs Ologn LSM树:在机器磁盘上表现非常出色的写性能,用于降低存储成本,但是读上面牺牲比较大,和B树类似 同时在索引上也分为两类: 聚集索引(主索引):索引上直接存储了数据,查到后直接返回数据 非聚集索引(辅助索引):索引上仅保存了主键索引号,查到后仍然需要到主键索引上获取数据,这个过程被称为 回表,如果查询的所有字段都属于辅助索引则不再需要回表,这个现象被称为 覆盖索引(cover index), 深度分页 所有存储都面临这个问题:如果一大批数据进行非常靠后的查询,比如千万级别的结果集,跳到最后几条,基本上现有存储的 skip 都是基于范围扫描实现的,比如 skip(100w).limit(1),扫描 100w01条数据后抛弃前 100w 行记录来实现,因此 skip 太大后会导致大量的无效扫描。 这个问题有几个解法 调整业务:绝大多数这种场景都是不合理的,当你的默认条件能够查询出上百万条数据时,更合理的操作是缩小数据范围,而不是真的让用户翻到最后一页,这里从交互上有几个处理方法 当超过预定的数据时(比如 1w),只返回 1w,并提示用户缩小范围 默认的搜索条件添加一些区分粒度大的条件,比如日期,可以有效控制默认页面的数据窗口 游标:游标就是在上次翻译的结果上做好标记,比如选取一个递增的字段,记录返回时的极值(通常选择主键ID),下次查询时携带上这个字段,然后用其作为查询条件来进行索引,而不是直接 skip。方案优点是易于实现,缺点是不支持随机翻页,只能查看下一页 or 上一页。 更换存储方案:通常这么大的统计需求一般都是建立 数仓 来实现了,不要直接基于 OLTP 的存储去实现。 隔离级别 先说说可能出现的几个问题:
查看更多 →

M3db

背景 我们的云平台需要采集所有集群正在运行的容器的性能数据与业务指标,最开始采用的每个集群一个独立Promethues的配置,如下图: 结构比较简单,这里就不累述了。需要解释下的是 prom-nanny 这个组件是个啥,其实就是对社区版本 prometheus 的封装,它主要实现以下功能: watch 用户配置的自定义告警规则,然后更新到配置文件中 更新配置文件后,调用 prometheus 接口热更新配置 以上则是以前老的监控体系下的拓扑图。很明显这里很多问题: 存储、采集、查询单点:这里 Prometheus 承担了三个责任,但是它仅有一个实例,一旦故障,我们将失去所有功能 无法水平扩容:由于只有一个实例,所以我们只能垂直扩容,而母机的配置是有上限的,你不可能无线扩下去,对于有的集群动则几千个节点,单一实例是不可能满足的。有的同学可能会提到 hashmod 、federation 和 remote_read,其实这三个功能可以解决采集和查询的水平扩容,但是解决不了存储的水平扩容,Prometheus 提出了 remote_write 来解决存储单点的问题 权限未分开:由于我们不但采集性能数据,还采集业务系统自行暴露的业务指标,因此存在一些敏感数据,但是 grafana 和 prometheus 都共用一个的情况下,权限都是共用的,这带来的一定的安全风险 方案对比 由于现在集群的指标都是通过 Prometheus 的 exporter 来暴露,所以方案已经限定以 Prometheus 为基础了。 基于这个前提,在作者进行方案选型时(2019年中),了解到的有以下几种解决方案: Prometheus + influxdb Thanos cortex M3 这里其中第一个方案由于此前隔壁组的同事以前做过预研,发现性能存在较大缺陷,所以不在考虑范围内,最终他们选择了 Thanos。 这里列个对比图: 简述 优点 缺点 Thanos 开源社区很有名的集群方案,它的核心点在于将查询最近时间的查询请求路由到各个 Prom 分片进行查询,同时通过 SideCar 将数据同步到对象存储中,作为历史存储。 已经有了很多案例资料可参考,查询快,功能成熟 历史查询慢,由于是是从对象存储转换过来,很显然这里存在复杂的转换过程,同时对象存储的数据库也不会没有针对时序型数据的优化;同一 Prom 分片承担采集与查询的责任,会互相影响 cortex WeaveCloud 公司搞的开源项目,通过 Prometheus 的 remote_write 写入自研的组件后将其转换为块存储,然后提供兼容 PromQL 的查询组件来提供外部使用 虽然使用的块存储,但是它还利用了额外的存储来简历索引,同时提供了优化查询的缓存组件,可以是说是煞费苦心了;功能应该是我了解的方案中最为丰富的一个 太复杂,它的优点其实也是它的缺点,为了能优化查询的速度,让整个拓扑图的复杂度增高了,每多一个组件在分布式系统里面其实都是一种负担。 m3 这是 Uber 在2014 年开始自研的一个方案,他们在开始之前,也尝试了很多开源组件作为底层存储,发现都无法满足他们的要求,于是他们从底层存储开始自研了这套方案,有兴趣的同学可以看下 这篇文章,m3 的理念非常简单,直接通过 remote_write 写到转换组件,然后存入自研的时序数据库,并且提供兼容 PromQL 和 Graphite 语法的查询组件 拓扑简单,落地方便;由于自研的时序数据专门针对时序型数据进行了优化,所以无论短期还是历史数据查询效率和压缩率都非常高;已经在 Uber 内部经过大量数据的验证 案例太少,由于 2018 年才刚刚对外开源,网上关于 m3 实践的资料非常少,偶尔有几篇文章也只是在浅显地跑了起来而已,规模不可考;还没有稳定,版本还是0.
查看更多 →

项目中是否应该大量使用ORM

本文重在分析ORM在程序设计中的收益与风险,回答 ——是否应该使用ORM—— 这个常见问题
查看更多 →

单元测试的必要性

很多人都在质疑单元测试的必要性,今天我们就来探讨下,单元测试对一个服务或者框架来说,到底有多重要
查看更多 →

APISIX 与 腾讯 Oteam 的故事

本文主要讲述了ApachaAPISIX解决程序中的什么问题,以及在腾讯内部是如何服务于各个业务项目为研发们肩负的。
查看更多 →

常见算法思想

查看更多 →

后台任务漫谈

查看更多 →

API认证授权

本文记录了常见的API认证(Authentication)和授权(Authorization)协议,包括 ACL, RBAC, ABAC,OAuth2.0
查看更多 →

Nginx在云上环境的性能最佳实践

Nginx在云上环境的性能最佳实践 目录 背景 Nginx的CPU相关设置 k8s的CPU策略 方案对比 结论 背景 最近有用户反馈 ApacheAPISIX 在云原生环境下存在几个问题: 无法获取容器准确核数 配置了多核的情况下吞吐量增长缓慢 这里先简单介绍项目相关的情况,APISIX 是一个基于 Openresty 的开源网关。而 Openresty 其实就是 Nginx + LuaJIT,那么我们要调查问题其实跟 Nginx 是脱不开关系的。 Nginx的CPU相关设置 首先看看第一个问题:无法获取容器准确核数,这个问题的起因是因为在 Nginx 配置中使用了 worker_processes auto; auto 意味 Nginx 会自动获取 CPU 核数,然后根据核数创建 worker。不幸的是,在容器当中它获取到的是 母机的核数 导致 Nginx 会在容器中创建数十个甚至上百个 worker,多个 worker 间的资源竞争和上下文切换都会降低它的性能。 为了核验 Nginx 是否真的获取的是母机核数,我翻了下 Nginx 相关的代码,截取核心片段如下: src/os/unix/ngx_posix_init.c #include <ngx_config.h>#include <ngx_core.h>#include <nginx.h> ngx_int_t ngx_ncpu; ngx_int_t ngx_max_sockets; ngx_uint_t ngx_inherited_nonblocking; ... #if (NGX_HAVE_SC_NPROCESSORS_ONLN) if (ngx_ncpu == 0) { ngx_ncpu = sysconf(_SC_NPROCESSORS_ONLN); 查看 sysconf 的文档发现底层调用的 get_nprocs_conf(3), 继续查看它的源码,核心片段如下:
查看更多 →

APISIX 插件

编写APISIX的插件的常见姿势以及简单介绍
查看更多 →