启
我觉得互联网的大部分业务功能都集中在写数据, 查询, 聚合, 排序
站在用户的角度而言, 他们对一个系统的写数据的感知往往没有对查询, 聚合, 排序的感知强烈
很多业务的面向用户的显示都是查询, 聚合和排序得到的最终结果, 不管业务场景多么的复杂
承
对于业务程序员, 一般在公司的任务也就是把这些业务功能场景设计成数据库的不同表不同字段, 然后按照业务设计查询, 聚合, 排序等功能. 在这些业务场景的数据量没有达到一定数量级的时候, 程序员的工作的挑战性无非就是以上的工作不断的重复. 当然, 并没有鄙视以上工作的简单, 而事实上以上的工作并不简单, 随着产品的迭代, 业务不断的升级, 业务场景会变得越来越复杂. 程序员如果只是被产品的迭代被动牵着鼻子走的话, 接着就会慢慢导致复杂的业务称为系统功能扩展的瓶颈, 即使业务数据量没那么大的情况下.
当然, 程序员的任务不单单就完成业务功能这么简单, 还会考虑性能, 随着业务数据量的不断变多或者有高并发的场景, 性能无非是一个很重要的话题, 于是程序员们就开始各种打开百宝箱, 关系数据库, 非关系数据库, 缓存, 消息队列, 搜索引擎, 架构, 微服务, 同步异步, 分布式事务. 这些十八般武艺都是用来解决业务数据库或者高并发带来的性能问题的
程序员用来设计和实现业务的时间其实并不多, 最多不超过四成, 其他时间都是用来解决上面三个状况产生出来的问题(业务迭代, 大数据, 高并发). 对于一些公司, 可能甚至并没有大数据高并发的场景, 他们就单单在业务迭代上就忙的焦头烂额
既然查询, 聚合和排序是系统要实现的主要功能, 那么这些功能就要解决三大状况(业务迭代, 大数据, 高并发)带来的所有问题
开
$ 查询
要解决这些问题, 先分析一下这三大功能吧, 还是先说说查询
先给查询下一个简单的定义吧, 不好意思, 我定义不出来, 跳过后补
我还是先把查询的功能分解出来吧, 我刚才说了, 一般的公司都会用到关系数据库的表, 用来定义一个业务模型, 把一些内聚的属性字段组织成一个数据表, 为了说明问题, 我设计一个电商的模型关系(只是例子)
游客->用户->会员
游客: 在电商网站上溜达的人, 我们需要跟踪他的会话信息, 对他的行为进行统计, 因为他也可以下单, 只是支付的方式有限制而已
表设计: session信息, 访问ip, 上次访问时间, 当前访问时长, 行为统计: 当天访问主页几次, 当天访问招聘页几次(他是不是要找工作…)
用户: 肯登录的游客(包括各种第三方登录), 这样的游客我们可以对他的信息进行绑定了, 当然, 我们可以画出更精准的用户画像了
表设计: 用户名, 密码, 登录方式(微信登录, qq登录, 账号登录), 个人信息(比如性别, 生日, 年龄)联系方式, 收货地址
会员: 肯花的用户, 不管是花时间或花钱, 我们会黏住他, 用各种权益诱惑他, 比如每天登录签到(连续签到10天给1分钟), 我们要想尽一切办法让这些死忠不断花时间或花钱
表设计: 钱包余额, 积分, 会员等级, 会员权益
如果把上面的例子的三个模型用三个物理上的数据表来表示的话, 那么查询功能大概有这些分解
单表单条件单值查询
如: 查询年龄为20的用户Id(用户表, 年龄等于20)
单表单条件多值查询
如: 查询年龄为20或30的用户Id(用户表, 年龄等于20或30)
单表单条件单区间值查询
如: 查询年龄在20到30之间的用户Id(用户表, 年龄在20和30之间)
单表单条件多区间值查询
如: 查询年龄在20到30, 40-50之间的用户Id(用户表, 年龄在20和30, 40和50之间)
单表多条件单值查询
如: 查询年龄为20的男用户Id(用户表, 年龄等于20, 性别等于男)
单表多条件多值查询
如: 查询年龄为20或30的男用户Id(用户表, 年龄等于20或30, 性别等于男)
单表多条件单区间值查询
如: 查询年龄为20到30之间的男用户Id(用户表, 年龄在20和30之间, 性别等于男)
单表多条件多区间值查询
如: 查询年龄为20到30之间的男用户Id(用户表, 年龄在20和30之间, 性别等于男)
多表多条件单值查询
如: 查询年龄为20并且今日访问过招聘页的男用户Id(用户表, 年龄等于20, 性别等于男, 游客表, 访问招聘页次数大于0)
多表多条件多值查询
如: 查询年龄为20或30并且今日访问过招聘页的男用户Id(用户表, 年龄等于20或30, 性别等于男, 游客表, 访问招聘页次数大于0)
多表多条件单区间值查询
如: 查询年龄为20到30之间并且今日访问过招聘页的男用户Id(用户表, 年龄在20到30之间, 性别等于男, 游客表, 访问招聘页次数大于0)
多表多条件多区间值查询
如: 查询年龄为20到30或40到50之间并且今日访问过招聘页的男用户Id(用户表, 年龄在20到30或40到50之间, 性别等于男, 游客表, 访问招聘页次数大于0)
来给查询下一个定义, 在一个数据容器内, 根据给定的条件, 获取符合条件的一个指定属性的数据集, 条件必须是建立在已定义的属性上. 这里有几个说明
- 数据容器必须是有界限的, 也就是说容器内的所有的数据必须是有限可访问的(可以是一个数据库, 或者数据库群, 搜索引擎数据库)
- 条件建立在已定义的属性, 也就是说这里的属性是不需要经过二次计算的, 在系统中已经可以直接获取
- 数据集的获取前提是要指定获取的模型的数据属性, 比如上面的用户Id
$ 聚合
我的理解, 聚合是在查询的基础上, 对指定的相同类别的一个或多个记录数据的进行逻辑运算, 比如求和, 求最大值, 求最小值, 求平均值等等其他的逻辑预算, 当然, 最后查询的条件也可以建立在聚合后计算字段(通过逻辑运算得出来).
聚合包含两个操作, 先分组后运算, 分组就是把查询得到的记录集根据分组条件分成几组结果集, 比如对上面的例子
查询年龄为20到50的用户, 并且是积分最多的男性
这里的分组就是把符合条件的用户进行性别分组, 运算就是对分组后的每个组进行指定的逻辑运算, 比如上面的求积分的最大值
在上面的例子, 如果我把用户Id用来分类的话, 每个用户就是一个单独的类别, 这样其实也可以对每个用户单个记录进行逻辑运算, 我把这也归类为聚合里面
分组需要指定一个分类器, 分类器就是一个对查询得到的结果集的每个记录进行映射成一个键值, 然后对相同键值进行分门别类. 所以分类器的作用就是对结果集进行分组. 分组是可以无限嵌套的, 对分组后的每个结果集再次运用另外的分类器得到更小的分组后的结果集, 如此依次无限下去
分组的作用其实是为了接下来的运算, 运算会把分组后的同一个组下的多个记录合并成一个记录, 这些运算包括求和, 求最大值, 求最小值, 求平均值, 求拼接值, 等等其他一些自定义的运算, 当然, 可以同时指定多个运算
过滤器, 通过聚合的分组和运算操作后, 最后还可以对聚合后的结果集进行过滤, 指定过滤条件, 包括对聚合运算后的值作为过滤条件, 这和查询条件必须建立在已定义的属性上不同, 过滤器用来筛选最终需要的结果集
$ 排序
排序是对上面的查询后或者聚合后(如果有聚合操作)的结果集进行指定字段按升序或者降序的有序排列, 结果集的多个纪录肯定是需要排序的, 即使不指定显示排序, 也有一个默认的排序(物理排列顺序). 排序也可以是多级的, 先按第一个字段排序, 当第一个字段排序权重一样的时候, 可以按指定的第二个字段排序
$ 分页
当排序后的结果集非常大的时候一般都需要对结果集进行分页, 指定每次需要处理的记录数量. 分页一定是在排序之后的. 分页的原因首先是因为数据量过大, 超过了使用者(人对信息量每次处理的容量, 电脑屏幕限制)每次使用的容量, 其目的有是为了将一个比较大的结果集进行分批处理, 或者限量处理(并不是每个使用者都会对所有的分页进行处理或使用), 在一定程度上缓解了计算资源的使用压力
$ 总结
对上面的四个操作做一个简单的总结, 他们的依次执行顺序是
查询, 聚合, 排序, 分页, 最后对得到的结果集做别的处理(比如显示在屏幕上)
老大来了(待续…)