数据库知识小记 最近任务涉及到要在redis里面存储结构化数据,并且还要支持范围查询,同时方案设计时还需要考察各类数据库,因此调查了一下目前业界的数据库。 目前业界数据库种类繁多,根据使用方法不同可以分为: SQL: Mysql, PostgresQL, SQLServer, Oracle NoSQL: 不同传统的SQL数据库,又分成了: 文档型数据库:CouchDB, MongoDB, ElasticSearch等 图数据库:JanusGraph, HugeGraph等 键值数据库: 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 的存储去实现。 隔离级别 先说说可能出现的几个问题: