技术

go 内存管理 golang 系统调用与阻塞处理 图解Goroutine 调度 重新认识cpu mosn有的没的 负载均衡泛谈 《Mysql实战45讲》笔记 单元测试的新解读 《Redis核心技术与实现》笔记 《Prometheus监控实战》笔记 Prometheus 告警学习 calico源码分析 对容器云平台的理解 Prometheus 源码分析 并发的成本 基础设施优化 hashicorp raft源码学习 docker 架构 mosn细节 与微服务框架整合 Java动态代理 编程范式 并发通信模型 《网络是怎样连接的》笔记 go channel codereview gc分析 jvm 线程实现 go打包机制 go interface及反射 如何学习Kubernetes 《编译原理之美》笔记——后端部分 《编译原理之美》笔记——前端部分 Pilot MCP协议分析 go gc 内存管理玩法汇总 软件机制 istio流量管理 Pilot源码分析 golang io 学习Spring mosn源码浅析 MOSN简介 《datacenter as a computer》笔记 学习JVM Tomcat源码分析 Linux可观测性 学习存储 学计算 Gotty源码分析 kubernetes operator kaggle泰坦尼克问题实践 kubernetes垂直扩缩容 神经网络模型优化 直觉上理解机器学习 knative入门 如何学习机器学习 神经网络系列笔记 TIDB源码分析 《阿里巴巴云原生实践15讲》笔记 Alibaba Java诊断工具Arthas TIDB存储——TIKV 《Apache Kafka源码分析》——简介 netty中的线程池 guava cache 源码分析 Springboot 启动过程分析 Spring 创建Bean的年代变迁 Linux内存管理 自定义CNI IPAM 副本一致性 spring redis 源码分析 kafka实践 spring kafka 源码分析 Linux进程调度 让kafka支持优先级队列 Codis源码分析 Redis源码分析 C语言学习 《趣谈Linux操作系统》笔记 docker和k8s安全机制 jvm crash分析 Prometheus 学习 容器日志采集 Kubernetes 控制器模型 Kubernetes监控 容器狂占cpu怎么办? Kubernetes资源调度——scheduler 时序性数据库介绍及对比 influxdb入门 maven的基本概念 《Apache Kafka源码分析》——server Kubernetes objects 源码分析体会 《数据结构与算法之美》——算法新解 Kubernetes源码分析——controller mananger Kubernetes源码分析——apiserver Kubernetes源码分析——kubelet Kubernetes介绍 ansible学习 Kubernetes源码分析——从kubectl开始 jib源码分析之Step实现 jib源码分析之细节 线程排队 跨主机容器通信 jib源码分析及应用 为容器选择一个合适的entrypoint kubernetes yaml配置 《持续交付36讲》笔记 mybatis学习 程序猿应该知道的 无锁数据结构和算法 CNI——容器网络是如何打通的 为什么很多业务程序猿觉得数据结构和算法没用? 串一串一致性协议 当我在说PaaS时,我在说什么 《数据结构与算法之美》——数据结构笔记 PouchContainer技术分享体会 harbor学习 用groovy 来动态化你的代码 精简代码的利器——lombok 学习 《深入剖析kubernetes》笔记 编程语言的动态性 rxjava3——背压 rxjava2——线程切换 spring cloud 初识 《深入拆解java 虚拟机》笔记 《how tomcat works》笔记 hystrix 学习 rxjava1——概念 Redis 学习 TIDB 学习 分布式计算系统的那些套路 Storm 学习 AQS1——论文学习 Unsafe Spark Stream 学习 linux vfs轮廓 《自己动手写docker》笔记 java8 实践 中本聪比特币白皮书 细读 区块链泛谈 比特币 大杂烩 总纲——如何学习分布式系统 hbase 泛谈 forkjoin 泛谈 看不见摸不着的cdn是啥 《jdk8 in action》笔记 程序猿视角看网络 bgp初识 calico学习 AQS2——粗略的代码分析 我们能用反射做什么 web 跨域问题 《clean code》笔记 《Elasticsearch权威指南》笔记 mockito简介及源码分析 2017软件开发小结—— 从做功能到做系统 《Apache Kafka源码分析》——clients dns隐藏的一个坑 《mysql技术内幕》笔记2 《mysql技术内幕》笔记1 log4j学习 为什么netty比较难懂? 回溯法 apollo client源码分析及看待面向对象设计 学习并发 docker运行java项目的常见问题 Scala的一些梗 OpenTSDB 入门 spring事务小结 事务一致性 javascript应用在哪里 《netty in action》读书笔记 netty对http2协议的解析 ssl证书是什么东西 http那些事 苹果APNs推送框架pushy apple 推送那些事儿 编写java框架的几大利器 java内存模型 java exception Linux IO学习 netty内存管理 测试环境docker化实践 netty在框架中的使用套路 Nginx简单使用 《Linux内核设计的艺术》小结 Go并发机制及语言层工具 Linux网络源代码学习——数据包的发送与接收 《docker源码分析》小结 docker中涉及到的一些linux知识 Linux网络源代码学习——整体介绍 zookeeper三重奏 数据库的一些知识 Spark 泛谈 链式处理的那些套路 netty回顾 Thrift基本原理与实践(二) Thrift基本原理与实践(一) 回调 异步执行抽象——Executor与Future Docker0.1.0源码分析 java gc Jedis源码分析 Redis概述 机器学习泛谈 Linux网络命令操作 JTA与TCC 换个角度看待设计模式 Scala初识 向Hadoop学习NIO的使用 以新的角度看数据结构 并发控制相关的硬件与内核支持 systemd 简介 quartz 源码分析 基于docker搭建测试环境(二) spring aop 实现原理简述 自己动手写spring(八) 支持AOP 自己动手写spring(七) 类结构设计调整 分析log日志 自己动手写spring(六) 支持FactoryBean 自己动手写spring(九) 总结 自己动手写spring(五) bean的生命周期管理 自己动手写spring(四) 整合xml与注解方式 自己动手写spring(三) 支持注解方式 自己动手写spring(二) 创建一个bean工厂 自己动手写spring(一) 使用digester varnish 简单使用 关于docker image的那点事儿 基于docker搭建测试环境 分布式配置系统 JVM内存与执行 git spring rmi和thrift maven/ant/gradle使用 再看tcp 缓存系统 java nio的多线程扩展 《Concurrency Models》笔记 回头看Spring IOC IntelliJ IDEA使用 Java泛型 vagrant 使用 Go常用的一些库 Python初学 Goroutine 调度模型 虚拟网络 《程序员的自我修养》小结 VPN(Virtual Private Network) Kubernetes存储 访问Kubernetes上的Service Kubernetes副本管理 Kubernetes pod 组件 Go学习 JVM类加载 硬币和扑克牌问题 LRU实现 virtualbox 使用 ThreadLocal小结 docker快速入门

架构

《许式伟的架构课》笔记 Kubernetes webhook 发布平台系统设计 k8s水平扩缩容 Scheduler如何给Node打分 Scheduler扩展 controller 组件介绍 openkruise cloneset学习 kubernetes crd 及kubebuilder学习 pv与pvc实现 csi学习 client-go学习 kubelet 组件分析 调度实践 Pod是如何被创建出来的? 《软件设计之美》笔记 mecha 架构学习 Kubernetes events学习及应用 CRI 《推荐系统36式》笔记 资源调度泛谈 系统设计原则 grpc学习 元编程 以应用为中心 istio学习 下一代微服务Service Mesh 《实现领域驱动设计》笔记 serverless 泛谈 《架构整洁之道》笔记 处理复杂性 那些年追过的并发 服务器端编程 网络通信协议 《聊聊架构》 书评的笔记 如何学习架构 《反应式设计模式》笔记 项目的演化特点 反应式架构摸索 函数式编程的设计模式 服务化 ddd反模式——CRUD的败笔 研发效能平台 重新看面向对象设计 业务系统设计的一些体会 函数式编程 《左耳听风》笔记 业务程序猿眼中的微服务管理 DDD实践——CQRS 项目隔离——案例研究 《编程的本质》笔记 系统故障排查汇总及教训 平台支持类系统的几个点 代码腾挪的艺术 abtest 系统设计汇总 《从0开始学架构》笔记 初级权限系统设计 领域驱动理念入门 现有上传协议分析 移动网络下的文件上传要注意的几个问题 推送系统的几个基本问题 用户登陆 做配置中心要想好的几个基本问题 不同层面的异步 分层那些事儿 性能问题分析 当我在说模板引擎的时候,我在说什么 用户认证问题 资源的分配与回收——池 消息/任务队列

标签


为什么很多业务程序猿觉得数据结构和算法没用?

2018年10月03日

简介

本文主要从业务程序猿的角度来阐述问题。

2019.2.26 补充:为什么做搜索的公司技术都很牛?为什么要学数据结构与算法?为什么业务程序猿感觉数据结构与算法没用?剖析搜索引擎背后的经典数据结构和算法 光一个最最简单的搜索引擎涉及的数据结构和算法就有:图、散列表、Trie 树、布隆过滤器、单模式字符串匹配算法、AC 自动机、广度优先遍历、归并排序等。

首先,我们先回答一个孪生问题,再从“业务发展阶段” 及 “X/Y问题”视角来论证一下。

业务程序猿为什么要学习数据结构与算法

首先,笔者针对这个问题问了好几个大牛, 汇总如下

  1. 这不是大学基础专业课吗?基础的东西,也是it行业都必须要掌握的,不要问为什么要学,这是软技能,必须掌握
  2. 具体的数据结构是次要的,对目标问题的可利用的属性、数学模型的特殊属性、以及运行系统的物理属性保持高度敏感才是关键。数据结构课程不厌其烦讲解那么多例子就是让你建立这种“机敏”的世界观
  3. 数据结构基本就干两件事:对特定问题的有效存储,以方便更有效的检索。
  4. 不同的公司、不同的人做rpc框架,架构设计思路都差不多,最后实现的功能也差不多。但有的人做出来的框架 bug很多、性能一般、扩展性也不好,只是内部使用,而有的人可以被apache收录。高手之间的竞争其实就在细节,细节在技术上往往就是数据结构和算法。
  5. 数据结构不是最用不上的,数据结构是最普遍的,就像你可能认为数学是最用不上的时候,其实它无处不在
  6. 技术会有几个终极目标,比如最好的性能(包括最少时间和最少空间占用),还有最快交付结果。第一个和数据结构算法关系密切。虽然多数人并不需要自己写算法,只需要crud,但走到高级技术时,就要自己直面难题,不了解底下原理的话,就不容易做出合适的选择以及出深刻问题时如何排查。
  7. 数据结构,算法,作为编程基础,很有用,但未必有针对性的有用。很多日常工作可能根本不会直接用到。比如,高等数学有用吗?肯定有用,对你实际工作有很大帮助吗?未必,在实际逻辑开发中可能作用还不如一个简单的设计模式有用。我觉得你要看针对性,比如我是前一两年开始研究深度学习的时候,才去复习线性代数的一些算法比如梯度下降。如果关注架构,领域建模等层面,不太需要对数据结构和算法随时保持新鲜的记忆,有一点点“肌肉记忆”即可,如果你要做偏底层的开发,比如Southgate这种,才那可能会直接用到算法。
  8. 我觉得数据结构和算法属于提升层次的一个东西,具体点,对数据结构和算法敏感通常代码审美能力比较好,知道什么代码是好的,什么代码是低效的,数据结构最大的作用就是简化代码的复杂度和提高代码的维护性,典型的例子就是比如你用个前缀树啥的做搜索你可以很清楚时间复杂度和空间复杂度是多少,如果没这种数据结构的概念就容易写出低效的代码了。再补充一点,开源方案非常多,我们选择一些方案,进行技术选型的时候肯定会去稍微看看代码,美的代码会促进你使用,而评价美与丑就是看里面一些数据结构和小的算法的使用

笔者自己在交流中的一些体会

  1. 很多时候,是不学导致用不到,学到自然会找办法用到。就好比学英语,英语不好的程序猿不看英文文档,自然也体会不到英文的用处。
  2. 跟业务领域有关系,比如你在一家做地图的公司上班,对“图”相关的算法是一定要了解的。

业务发展阶段

有时候单说问题本身,是说不出所以然的,要place it in context,本文将context 定位为 公司的发展阶段。

一个互联网公司的发展通常有以下阶段:

  1. 业务为重,因为要生存
  2. 业务与技术并重,生存是第一的,但量级增大导致技术拖了后腿
  3. 技术驱动业务,最明显的体现在两个地方

    • 大公司做一个app的成本极其低廉
    • 最近流行的 为用户、主播、商家“赋能”

具体的说

  1. 互联网公司早期业务的本质是信息化,信息化体现在技术上就是crud,也就有了很多crud boy
  2. 早期用户、数据量不大
  3. “业务为重”体现在技术上 就是尽快的交付,无暇顾及数据结构与算法
  4. 后续,用户、数据量增大本身 对技术构成挑战
  5. 竞品的出现,以及用户需求的提高,不能止步于信息化,需要个性化、智能化

大部分公司 处在前两个阶段,所以业务程序猿 面对的事情更多是信息化和快速交付,因此对数据结构 与算法的 必要性感受不深。

一个很直观的例子, 大家谁能说的清楚自己的项目扛得住多大的QPS?有没有试过单机最大扛多少QPS?有没有体验过 最大QPS 下代码会出现哪些问题?很少。为何很多业务程序猿很少考虑这个问题呢?因为出了问题哪有时间想这些,代码逻辑没错就行,性能不够就加机器啊。

X/Y 问题

X/Y 问题说的是:你让我干Y,但其实为什么需要Y呢,是因为X,而实现X的方法 不是只有Y。说的是 做事情、看问题 要寻找本源。针对本文的问题可以衍射出两个问题

  1. 当我在说“数据结构与算法”的时候,我在说什么
  2. 数据结构与算法是Y,那么X是什么?

    对数据结构的认识问题

大部分人在说 数据结构 和算法的时候,其实说的是大学学的《数据结构》那本书,再具体一点就是清华大学出版社的那本。那本书 定义了 我们看到 数据结构 这四个字时的条件反射,即线性表、树、图、排序算法等。

所以,我们说学习数据结构与算法有什么用?对大部分人来说,可以转换为:学习“线性表、树、图、排序算法” 有什么用? 就这个具体问题而言,除了代码里必不可少的数组/List 之外,其它的确实用的少。再者,笔者确实没碰到业务场景说 一定要将ArrayList 替换为LinkedList 。

对应前文中大牛提到的观点:数据结构基本就干两件事:对特定问题的有效存储,以方便更有效的检索。 所以数据结构不只是线性表、树和图,业务数据的结构化都可以归类到数据结构问题,进而考量数据有哪些检索场景?是否高效?

真的是为了数据结构本身么?

我们学习数据结构 与算法 是为了“更优的数据结构和算法” 本身么?不是,性能不高 加机器就好了呀。

我们学习数据结构与算法, 不是为了死记硬背几个知识点,是为了不断提高自己的意识

  1. 从无到有写代码的时候,培养对空间、时间浪费的罪恶感
  2. 从有到更好。出了问题,你的应对手段多一个思维方向,而不是只有“加机器”/“求助” 两条路

结合笔者的工作经历提一个问题:netty最牛的地方在哪里?用了nio?用了主从线程模型?内存零拷贝?数据处理pipeline?使用arena 接管内存分配和释放? 都对也都不对,是netty 在不断迭代的过程中,不断地对自己提要求,不断地挖掘问题点以支持更高的性能。

netty 的版本一直在迭代,业务当前很稳定,自然没有必要升netty的版本。类似的,如果你没有这样的意识,学数据结构与算法 也毫无必要。

如何学习数据结构与算法——有度的学习

数据结构 在技术中的 整体地位

做偏中间件开发的人,对算法和偏底层的比如网络的运用是比较多的,一般数据结构和算法能力都比较好,但他们对“工程”架构的东西关注不足。

因此凡事都可以一体两面的去看,技术能力可以分为

  1. 基础能力,包括但不限于语言、计算机组成原理、操作系统、数据结构与算法等
  2. 工程能力,如何应用基础能力,以及需求分析、项目设计、测试、维护和文档等
  3. 技术管理,人员项目协调、进度把控、技术氛围等

数据结构、计算机组成原理、操作系统等 经过文章、博客、课本的普及,成为一种“显学”。而“工程/架构”、”技术管理“等 因为模糊 提的少, 被归类为软技能。软技能在做他人/自我评估时,便容易被漏掉,一些工程能力/管理能力还不错的同学 也容易因为数据结构 不好而觉得不自信。

结合上文公司的发展阶段也可以看到,业务从无到有的过程 对工程能力和技术管理比较看重,业务从有到更好阶段 则更为看重基础能力。

有理有利有节

这是毛主席抗日战争日期讲如何应对与顽固派摩擦的,那我们如何应对时不时蹦出来的“学习数据结构与算法”的念头与观点。

记忆有以下几种

  1. 有随时“新鲜”的记忆
  2. 不常用但是了解原理的“常识“记忆
  3. “肌肉”记忆,跟直觉一样

工作内容尽量要覆盖在新鲜记忆上。结合公司的发展阶段 以及自己的兴趣 爱好 和 定位 来把控自己的学习节奏,要不要学?学到什么程度? 很多时候,学不到位会误事,太超前了则容易受挫,进而打击积极性。此外,要随着项目、阶段的变化调整自己的策略,有些事要提前布局,有些事被动跟进就可以了。

个人微信订阅号