Tuesday, March 22, 2022

SegmentFault 最新的文章

SegmentFault 最新的文章


Redis 缓存击穿(失效)、缓存穿透、缓存雪崩怎么解决?

Posted: 21 Mar 2022 08:08 PM PDT

原始数据存储在 DB 中(如 MySQL、Hbase 等),但 DB 的读写性能低、延迟高。

比如 MySQL 在 4 核 8G 上的 TPS = 5000,QPS = 10000 左右,读写平均耗时 10~100 ms。

用 Redis 作为缓存系统正好可以弥补 DB 的不足,「码哥」在自己的 MacBook Pro 2019 上执行 Redis 性能测试如下:

$ redis-benchmark -t set,get -n 100000 -q SET: 107758.62 requests per second, p50=0.239 msec GET: 108813.92 requests per second, p50=0.239 msec

TPS 和 QPS 达到 10 万,于是乎我们就引入缓存架构,在数据库中存储原始数据,同时在缓存总存储一份。

当请求进来的时候,先从缓存中去数据,如果有则直接返回缓存中的数据。

如果缓存中没数据,就去数据库中读取数据并写到缓存中,再返回结果。

这样就天衣无缝了么?缓存的设计不当,将会导致严重后果,本文将介绍缓存使用中常见的三个问题和解决方案:

  • 缓存击穿(失效);
  • 缓存穿透;
  • 缓存雪崩。

缓存击穿(失效)

高并发流量,访问的这个数据是热点数据,请求的数据在 DB 中存在,但是 Redis 存的那一份已经过期,后端需要从 DB 从加载数据并写到 Redis。

关键字:单一热点数据、高并发、数据失效

但是由于高并发,可能会把 DB 压垮,导致服务不可用。如下图所示:

缓存击穿

解决方案

过期时间 + 随机值

对于热点数据,我们不设置过期时间,这样就可以把请求都放在缓存中处理,充分把 Redis 高吞吐量性能利用起来。

或者过期时间再加一个随机值。

设计缓存的过期时间时,使用公式:过期时间=baes 时间+随机时间。

即相同业务数据写缓存时,在基础过期时间之上,再加一个随机的过期时间,让数据在未来一段时间内慢慢过期,避免瞬时全部过期,对 DB 造成过大压力

预热

预先把热门数据提前存入 Redis 中,并设热门数据的过期时间超大值。

使用锁

当发现缓存失效的时候,不是立即从数据库加载数据。

而是先获取分布式锁,获取锁成功才执行数据库查询和写数据到缓存的操作,获取锁失败,则说明当前有线程在执行数据库查询操作,当前线程睡眠一段时间在重试。

这样只让一个请求去数据库读取数据。

伪代码如下:

public Object getData(String id) {     String desc = redis.get(id);         // 缓存为空,过期了         if (desc == null) {             // 互斥锁,只有一个请求可以成功             if (redis(lockName)) {                 try                      // 从数据库取出数据                     desc = getFromDB(id);                     // 写到 Redis                     redis.set(id, desc, 60 * 60 * 24);                 } catch (Exception ex) {                     LogHelper.error(ex);                 } finally {                     // 确保最后删除,释放锁                     redis.del(lockName);                     return desc;                 }             } else {                 // 否则睡眠200ms,接着获取锁                 Thread.sleep(200);                 return getData(id);             }         } }

缓存穿透

缓存穿透:意味着有特殊请求在查询一个不存在的数据,即不数据存在 Redis 也不存在于数据库。

导致每次请求都会穿透到数据库,缓存成了摆设,对数据库产生很大压力从而影响正常服务。

如图所示:

缓存穿透

解决方案

  • 缓存空值:当请求的数据不存在 Redis 也不存在数据库的时候,设置一个缺省值(比如:None)。当后续再次进行查询则直接返回空值或者缺省值。
  • 布隆过滤器:在数据写入数据库的同时将这个 ID 同步到到布隆过滤器中,当请求的 id 不存在布隆过滤器中则说明该请求查询的数据一定没有在数据库中保存,就不要去数据库查询了。

BloomFilter 要缓存全量的 key,这就要求全量的 key 数量不大,10 亿 条数据以内最佳,因为 10 亿 条数据大概要占用 1.2GB 的内存。

说下布隆过滤器的原理吧

BloomFilter 的算法是,首先分配一块内存空间做 bit 数组,数组的 bit 位初始值全部设为 0。

加入元素时,采用 k 个相互独立的 Hash 函数计算,然后将元素 Hash 映射的 K 个位置全部设置为 1。

检测 key 是否存在,仍然用这 k 个 Hash 函数计算出 k 个位置,如果位置全部为 1,则表明 key 存在,否则不存在。

如下图所示:

布隆过滤器

哈希函数会出现碰撞,所以布隆过滤器会存在误判。

这里的误判率是指,BloomFilter 判断某个 key 存在,但它实际不存在的概率,因为它存的是 key 的 Hash 值,而非 key 的值。

所以有概率存在这样的 key,它们内容不同,但多次 Hash 后的 Hash 值都相同。

对于 BloomFilter 判断不存在的 key ,则是 100% 不存在的,反证法,如果这个 key 存在,那它每次 Hash 后对应的 Hash 值位置肯定是 1,而不会是 0。布隆过滤器判断存在不一定真的存在。

缓存雪崩

缓存雪崩指的是大量的请求无法在 Redis 缓存系统中处理,请求全部打到数据库,导致数据库压力激增,甚至宕机。

出现该原因主要有两种:

  • 大量热点数据同时过期,导致大量请求需要查询数据库并写到缓存;
  • Redis 故障宕机,缓存系统异常。

缓存大量数据同时过期

数据保存在缓存系统并设置了过期时间,但是由于在同时一刻,大量数据同时过期。

系统就把请求全部打到数据库获取数据,并发量大的话就会导致数据库压力激增。

缓存雪崩是发生在大量数据同时失效的场景,而缓存击穿(失效)是在某个热点数据失效的场景,这是他们最大的区别。

如下图:

缓存雪崩-大量缓存同时失效

解决方案

过期时间添加随机值

要避免给大量的数据设置一样的过期时间,过期时间 = baes 时间+ 随机时间(较小的随机数,比如随机增加 1~5 分钟)。

这样一来,就不会导致同一时刻热点数据全部失效,同时过期时间差别也不会太大,既保证了相近时间失效,又能满足业务需求。

接口限流

当访问的不是核心数据的时候,在查询的方法上加上接口限流保护。比如设置 10000 req/s。

如果访问的是核心数据接口,缓存不存在允许从数据库中查询并设置到缓存中。

这样的话,只有部分请求会发送到数据库,减少了压力。

限流,就是指,我们在业务系统的请求入口前端控制每秒进入系统的请求数,避免过多的请求被发送到数据库。

如下图所示:

缓存雪崩-限流

Redis 故障宕机

一个 Redis 实例能支撑 10 万的 QPS,而一个数据库实例只有 1000 QPS。

一旦 Redis 宕机,会导致大量请求打到数据库,从而发生缓存雪崩。

解决方案

对于缓存系统故障导致的缓存雪崩的解决方案有两种:

  • 服务熔断和接口限流;
  • 构建高可用缓存集群系统。

服务熔断和限流

在业务系统中,针对高并发的使用服务熔断来有损提供服务从而保证系统的可用性。

服务熔断就是当从缓存获取数据发现异常,则直接返回错误数据给前端,防止所有流量打到数据库导致宕机。

服务熔断和限流属于在发生了缓存雪崩,如何降低雪崩对数据库造成的影响的方案。

构建高可用的缓存集群

所以,缓存系统一定要构建一套 Redis 高可用集群,比如 《Redis 哨兵集群》或者 《Redis Cluster 集群》,如果 Redis 的主节点故障宕机了,从节点还可以切换成为主节点,继续提供缓存服务,避免了由于缓存实例宕机而导致的缓存雪崩问题。

总结

  • 缓存穿透指的是数据库本就没有这个数据,请求直奔数据库,缓存系统形同虚设。
  • 缓存击穿(失效)指的是数据库有数据,缓存本应该也有数据,但是缓存过期了,Redis 这层流量防护屏障被击穿了,请求直奔数据库。
  • 缓存雪崩指的是大量的热点数据无法在 Redis 缓存中处理(大面积热点数据缓存失效、Redis 宕机),流量全部打到数据库,导致数据库极大压力。

参考资料

https://segmentfault.com/a/11...

https://cloud.tencent.com/dev...

https://learn.lianglianglee.com/

https://time.geekbang.org/

高效求职,直通技术leader,思否招聘内推季正式开启!

Posted: 16 Mar 2022 12:05 AM PDT

金三银四招聘旺季悄然而至,企业渴望高效招聘,快速匹配优质开发者,广大程序员们也在 "伺机而动" 为更好的职业发展谋求新机。

如何帮助开发者更高效地获取企业信息?

避开"内卷重灾区",还有哪些值得关注的"宝藏公司"?

作为科技企业和开发者沟通的桥梁,为解决企业与开发者无法快速"了解"彼此的困扰,SegmentFault 思否正式开启本年度最大型线上活动:「宝藏公司内推季」。

本次活动分为两期,第一期精心筛选出五家宝藏级公司,共 上百个程序员 HC,薪资高达 50 万/年,福利更是多到数不清: "远程办公"、"弹性工作"、"公款旅游"、"带薪撸猫"、"五险一金全额缴"、"补充商业险+子女补充医疗"......

你看,它们虽不是所谓的"大厂、BAT",但确有比肩大厂的机遇和气魄。如此适合开发者"撒欢儿"的宝藏公司,你心动了吗?~

本次活动将以线上直播的方式,于 3 月 26 日 下午 2 点进行,感兴趣的小伙伴们一定要先进群。群内会分享岗位情报、开播提醒、简历投递引导等信息,帮助你更全面的了解每一家企业,更快速的匹配到心仪岗位 ~

人在职场,总要看新机会,暂时没有求职计划的同学也欢迎进群围观,看宝藏公司如何招揽优秀人才。

进群了解更多信息

宝藏公司揭晓

StreamNative

StreamNative 是一家开源基础软件公司,由 Apache 软件基金会顶级项目 Apache Pulsar 创始团队组建而成,围绕 Pulsar 打造下一代云原生批流融合数据平台

作为 Apache Pulsar 商业化公司,StreamNative 专注于开源生态和社区构建,致力于前沿技术领域的创新,创始团队成员曾就职于 Yahoo、Twitter、Splunk、EMC 等知名大公司。2021 年 10 月,StreamNative 宣布完成 2300 万美元 A 轮融资

StreamNative 此次开放的职位包括 Platform Engineer、Cloud Product Engineer,薪资范围 25k - 50k 不等。

除此之外,在 StreamNative 你还将获得:

  • 激动人心的项目,快速成长的公司,开放协作的环境,有竞争力的薪酬和福利,灵活弹性的工作时间,和一群地球上最好的队友。
  • 开源文化,倡导异步沟通,高度自驱,遵循 OKR 结果导向,无996不内卷,文化透明公开。
  • 我们是远程办公团队,我们的员工来自不同时区和城市,无论你在地球上哪个角落,只要你可以连接互联网,就可以加入 StreamNative 。

这家宝藏公司你"心动"了吗?点击了解更多信息

ONES

ONES 成立于2015年,是国内领先的企业级研发管理解决方案提供商

2021年,ONES 连续完成3轮总计近1亿美元融资,是国内研发管理领域累计融资金额最多、历史融资速度最快的公司。

凭借专业的产品及服务,ONES 已成功帮助小米、浪潮软件、贵州茅台、上汽、招商基金等多家500强在内的企业实现高效研发。

ONES 此次开放的职位包括 Go 后端工程师、Web前端工程师,薪资范围 20k - 40k 不等。

三句话带你了解 ONES:

  • ONES 领跑国内研发管理赛道,现在就加入我们,你将与 ONES 一同快速成长,以软件之力量服务全球企业
  • 我们相信团队的力量,也尊重每一份个性,在这里你可以求知探索也可以成为别人的老师,信任、沟通与支持能让我们跑得更快;
  • 我们鼓励开放与创新,工程师不仅仅可以写代码,还可以参与产品设计、lead 项目、弹吉他、打篮球、撸猫……

这家宝藏公司你"心动"了吗?点击了解更多信息

涛思数据

北京涛思数据科技有限公司(TAOS Data)瞄准日益增长的物联网数据市场,专注时序空间大数据的存储、查询、分析和计算,不依赖任何开源或第三方软件,开发了拥有自主知识产权、100% 自主可控的高性能、分布式、支持 SQL 的时序数据库 TDengine

涛思的核心产品 TDengine 在 GitHub 上已累计获得 17.9k Star,目前在国内时序数据库领域位居 Top 1,国际 CNCF 数据库领域位居 Top 5

在融资方面,公司已经获得投资方经纬中国、红杉资本、GGV 纪源资本、明势资本、蛮子基金、永辉瑞金等多家机构的近 7000 万美元的投资,深受资本市场青睐。

涛思数据此次开放的职位包括流式计算资深研发工程师、数据库内核资深研发工程师,年薪 50W+。

为什么要来涛思数据?

  • 价值观 & 公司文化:涛思数据团队直率、真诚、专注、注重细节、自我驱动而且目标导向。
  • 发展前景 & 行业地位:曾多次在 GitHub 全球趋势排行榜上排名第一,而且全球运行的 TDengine 实例数超过 106k,用户遍布全球。
  • 福利待遇 & 办公环境:超越一线大厂的薪酬、五险一金全额缴+...

这家宝藏公司你"心动"了吗?点击链接了解更多信息

支流科技

API7.ai 是一家提供 API 处理和分析的开源基础软件公司,基于 Apache APISIX 提供 API 网关、Kubernetes Ingress Controller 和服务网格产品。

致力为全球企业管理并可视化 API 和微服务等关键业务流量,通过大数据和人工智能(AI)加速企业业务决策,驱动数字化转型。

Apache APISIX 是API7.ai 2019年捐赠给 Apache 软件基金会的顶级项目,全球已经有数百家企业用户在使用 Apache APISIX 处理核心的业务流量,其中包括 NASA、瑞士电信、欧盟数字工厂、微博、腾讯、中国移动、奈雪的茶、WPS、爱奇艺、B站等国内外知名企业。

支流科技此次开放的职位包括SRE工程师、服务端开发工程师,薪资范围 15k - 45k 不等。

除此之外,在支流科技你还将获得超多福利:

  • 基金类:健身基金、体检基金、学习基金、考学基金
  • 节礼类:法定节日礼、入职周年礼、生日礼
  • 假期类:每年可享起步 6 天的带薪年假,以及每月 1 天的带薪假期

这家宝藏公司你"心动"了吗?点击链接了解更多信息

维格表


vika维格公司创立于 2019 年,是一家以技术为核心驱动的科技公司,一个高速发展,并且极具创造力的团队。我们怀揣着"让十亿人获得科技能力"的使命,聚焦于零代码应用搭建平台工具研发和基于可视化数据库的生态建设

vika维格在创立初期先后获得来自 IDG 、高瓴、五源、天图、靖亚等一线投资机构累计 4 轮的上亿元融资,目前已经服务海内外9000多家企业和数以万记的组织。

作为全新的可视化关系型数据库,vika维格表始终以产品驱动增长,上线 14 个月即为全球范围内 10000+ 企业提供数字化服务,涵盖电商、教育、科技、500强、国企、政府单位等。

维格表此次开放的职位包括高级前端工程师、高级后端工程师,薪资范围 15k - 40k 不等。

除此之外,在维格表你还将获得超多福利:

  • 元宇宙办公——无办公室、国内远程办公先行者
  • 极具市场竞争力的薪酬:16 薪、+X 年终奖 、股票期权
  • 独创的旅班制、弹性工作不打卡、课程报销...

这家宝藏公司你"心动"了吗?点击链接了解更多信息

Three.js 火焰效果实现艾尔登法环动态logo 🔥

Posted: 21 Mar 2022 06:18 PM PDT

声明:本文涉及图文和模型素材仅用于个人学习、研究和欣赏,请勿二次修改、非法传播、转载、出版、商用、及进行其他获利行为。

背景

《艾尔登法环》是最近比较火的一款游戏,观察可以发现它的 Logo 是由几个圆弧和线段构成。本文使用 React + Three.js 技术栈,实现具有火焰效果艾尔登法环 Logo,本文中涉及到的知识点包括:Fire.js 基本使用方法及 Three.js 的其他基础知识。

效果

实现效果如 👆 banner 图所示,页面主体由 Logo 图形构成,Logo 具有由远及近的加载效果,加载完毕后具有上下缓动动画效果。

在线预览

已适配:

  • 💻 PC
  • 📱 移动端

实现

Logo 的火焰效果主要是通过 Fire.js 实现的, 开始实现之前先来了解一下它的基本用法。

💡 Fire.js

Threejs 提供了一个可以实现火焰和烟雾效果的扩展包,通过引用并设置参数可以实现非常逼真的火焰和厌恶效果。【不过该扩展包已经从新版中移除】

火焰设置可选属性

  • color1:内焰颜色
  • color2:外焰颜色
  • color3:烟雾颜色
  • colorBias:颜色偏差
  • burnRate:燃烧率
  • diffuse:扩散
  • viscosity:粘度
  • expansion:膨胀
  • swirl:旋转
  • drag:拖拽
  • airSpeed:空气速度
  • windXX 轴风向
  • windYY 轴风向
  • speed:火焰速度
  • massConservation:质量守恒

常用方法

  • 添加资源:addSource(u, v, radius, density, windX, windY)
  • 清除资源:clearSources()
  • 设置贴图:setSourceMap(texture)

基本用法

通过简单几步:创建载体、使用Fire构造函数初始化、添加火焰、添加到场景等简单几步,就可实现火焰效果。可以创建多个火源,多种火焰效果也可以叠加到同一个载体上。

const geometry = new THREE.PlaneBufferGeometry(10, 10); const fire = new THREE.Fire(geometry,{   textureWidth: 10,   textureHeight: 10,   debug:false }); fire.addSource(0.5, 0.1, 0.1, 1.0, 0.0, 1.0); scene.add(fire);

实现效果

🔗 在线亲手尝试调整火焰各种参数效果:threejs/examples/webgl_fire.html

资源引入

引入开发所需的的模块资源,注意 Three.jsFire.js 是从当前目录引入的旧版本新版本已删除 Fire.jsTWEEN 用于实现简单的镜头补间动画、ringTexture 是需要显示火焰效果轮廓的贴图。

import React from 'react'; import * as THREE from './libs/three.module.js'; import { Fire } from './libs/Fire.js'; import { TWEEN } from "three/examples/jsm/libs/tween.module.min.js"; import ringTexture from './images/ring.png';

页面 DOM 结构非常简单,只包含一个渲染 WEBGL 的容器 #container

<div className='ring_page' id="container"></div>

场景初始化

初始化渲染场景、相机和光源。(如若需要详细了解这部分知识可翻阅我往期的文章或阅读官网文档,本文不再赘述)

const container = document.getElementById('container'); const renderer = new THREE.WebGLRenderer({ antialias: true,  alpha: true }); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) renderer.setSize(window.innerWidth, window.innerHeight); container.appendChild(renderer.domElement); renderer.setClearAlpha(0); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, 0, 100); camera.lookAt(new THREE.Vector3(0, 0, 0)); const ambientLight = new THREE.AmbientLight(0xffffff, 1); scene.add(ambientLight);

💡 设置渲染背景透明度

  • alphacanvas 是否开启透明度,默认为 false
  • renderer.setClearAlpha(alpha : Float):设置 alpha 透明度值,合法参数是一个 0.01.0 之间的浮点数。

以上代码中,通过设置 new THREE.WebGLRenderer({ antialias: true, alpha: true })renderer.setClearAlpha(0) 可以将 canvas 背景设置为透明,这样就可以通过 CSS 设置背景样式。本例中的背景图片就是通过 CSS 设置的,而不是 Sence.background

🌵 当开启 alpha: true 时,透明度默认为 0,可以不用写 renderer.setClearAlpha(0)

添加Logo主体

创建一个 PlaneBufferGeometry 平面作为火焰 Logo 载体,Logo 形状通过调用 setSourceMap 使用贴图生成,然后添加 Fire.js 的各种参数,调整平面的位置,最后将它添加到场景中即可。

const ring = new Fire(new THREE.PlaneBufferGeometry(20, 25), {   textureWidth: 800,   textureHeight: 1000,   debug: false, }); ring.setSourceMap(new THREE.TextureLoader().load(ringTexture)); ring.color1 = new THREE.Color(0xffffff); ring.color2 = new THREE.Color(0xf59e00); ring.color3 = new THREE.Color(0x08120a); ring.colorBias = .6; ring.burnRate = 10; ring.diffuse = 1; ring.viscosity = .5; ring.expansion = -1.6; ring.swirl = 10; ring.drag = 0.4; ring.airSpeed = 18; ring.windX = 0.1; ring.windY = 0.2; ring.speed = 100; ring.massConservation = false; ring.position.y = 4; ring.position.z = -6; scene.add(ring)

🌵 Logo 形状也可直接使用圆环等几何体拼接生成,本文为了简单省时并且更加逼真,直接使用了自己在 Photoshop 中绘制的贴图。注意贴图主体部分实际应用中要使用白色,为了便于展示我改成了黑色。

页面缩放适配

window.addEventListener('resize', () => {   camera.aspect = window.innerWidth / window.innerHeight;   camera.updateProjectionMatrix();   renderer.setSize(window.innerWidth, window.innerHeight); }, false);

镜头补间动画

页面刚开始加载完成时由远及近的镜头补间动画。

const controls = new OrbitControls(camera, renderer.domElement); Animations.animateCamera(camera, controls, { x: 0, y: 0, z: 22 }, { x: 0, y: 0, z: 0 }, 2400, () => {   controls.enabled = false; });

页面重绘动画

图案上线往复运动的缓动动画及渲染更新。

let step = 0; const animate = () => {   requestAnimationFrame(animate);   renderer.render(scene, camera);   stats && stats.update();   TWEEN && TWEEN.update();   step += .03;   ring && (ring.position.y = Math.abs(2.2 + Math.sin(step))); }

到这里,一个低配版的艾尔登法环 Logo 所有效果都全部实现了 😂,希望随着自己图形学方面知识的积累,后续可以通过 shader 实现更加炫酷的效果 🔥。 完整代码可通过下方链接查看。

🔗 完整代码:https://github.com/dragonir/3...

总结

本文知识点主要包含的的新知识:

  • Fire.js 基本使用
  • 设置渲染背景透明度
想了解场景初始化、光照、阴影、基础几何体、网格、材质及其他Three.js的相关知识,可阅读我往期文章。转载请注明原文地址和作者。如果觉得文章对你有帮助,不要忘了一键三连哦 👍

附录

2022招聘季|如何写一份好的前端面试简历?

Posted: 13 Mar 2022 05:34 PM PDT

项目地址: https://github.com/Wscats/CV
体验地址: http://wscats.github.io/CV/om...

简历的本质

在写简历之前,我们必须清楚的了解一件事情,那就是简历是什么?
它不是人生履历,不是项目清单,也不是技能大放送。
简历的存在只有一个目的 —— 帮你约到面试。只要能达到这个目的,简历可以是一段视频,一个开源项目,一张照片,甚至是一行字,比如:

I wrote javascript

当然,绝大部分简历的形式,就是我们所熟知的,是一篇文章。即使你通过其他方式获得了面试,当你入职的时候,还是要有这么一份纸质简历的,所以不要想着偷懒。

其实简历不只是表现自己,更是传递以下三个信息,增强通过率。

  • 是什么(你能做什么?,擅长什么?)
  • 比别人好在哪些地方(相比其他同行,你的优势?)
  • 如果雇佣你,招聘方会得到什么好处 (能不能为企业带来效益?)

正如你抉择跳槽,思考要不要留在这个公司的时候,你也可能考虑下面三点

  • 待遇(给的钱够不够?福利好不好)
  • 环境(同事nice不?老板好不好?事少离家近?工作开心与否?是否帅哥美女多?嗯哼)
  • 个人(有晋升机会不?能否再进步?)

当然企业和你的相互选择其实正如上面几点中的考量和博弈,当然面试是平等的,是相互选择的结果,所以有你对企业的考量,也有企业对你的考量

回归到写简历,和很多人在大学写议论文写作文是不同的,过分的论证会显得自夸,反而容易引起反感,所以要点到为止。这里的技巧是,提供论据,把论点留给阅读简历的人自己去得出。放论据要具体,最基本的是要数字化,好的论据要让人印象深刻。

举个例子,下边内容是虚构的:

2015年(时间可以具体到年月,或者经历的项目周期),我共同(一般都是团队开发了,单干就别写了,除非你个人能力非常突出)参与了XX新闻网站后端发布系统的开发(这部分是大家都会写的)。作为团队里面的核心程序员,我不但完成了网站界面、功能逻辑的开发工作,更提出了高效的组件化和模块化开发方案,通过模块化开发让团队成员较少编写代码时候的冲突,通过组件化让团队成员分工合作,各善其职,有效的提升了系统的的开发效率。(这部分是很多人忘掉的,记得写出你在这个项目中具体负责的部分,以及你贡献出来的价值。)在该系统上线后,Web前端性能从10QPS提升到200QPS,服务器由10台减少到3台( 通过量化的数字来增强可信度 )。2016年我升任WAPCMS项目负责人,带领一个3人小组支持着每天超过2亿的PV( 这就是你自身的优势。你能带给旧企业的价值,也就是你能带给新企业的价值。 )。

有同学问,如果我在项目里边没有那么显赫的成绩可以说怎么办?讲不出成绩时,就讲你的成长。因为学习能力也是每家公司都看中的东西。你可以写你在这个项目里边遇到了一个什么样的问题,别人怎么解决的,你怎么解决的,你的方案好在什么地方,最终这个方案的效果如何。

具体、量化、有说服力,是技术简历特别需要注重的地方。

(以上内容在写完简历后,对每一段进行评估,完成后再删除)


联系方式

(HR会打印你的简历,用于在面试的时候联系,所以联系方式放到最上边会比较方便)

  • 头像(如果你觉得自己长得可以,你可以放哈)
  • 手机:139如果是外地手机,可注明。如经常关机,要写上最优联系时间
  • Email:wscats@gmail.com虽然我觉得QQ邮箱无所谓,不过有些技术人员比较反感,建议用163,gmail也可以
  • QQ/微信号:7*提供一个通过网络可以联系到你的方式

个人信息

  • 易佳梦/女/1990
  • 本科/XX大学计算机系
  • 工作年限/经验:3年
  • 技术博客:https://wscats.github.io/blog ( 使用GitHub Page映射页面会显得的逼格较高 )
  • Github:https://github.com/Wscats ( 原创repo+多star的Github帐号会极大的提升你的个人品牌 )
  • 期望职位:Web前端程序员/工程师
  • 期望薪资:税前月薪15k~20k,特别喜欢的公司可例外
  • 期望城市:广州

工作经历

工作经历按逆序排列,最新的在最前边
因为HR更多的是了解你在上一家公司或者近几年你的工作情况,我建议实际工作小于半年的你也可以不写当然(IT行业跳槽如此平凡~),因为短的在职经历会让HR觉得你不够稳定,存在录用后的风险,还有工作经历最好有层次感,比如你刚开始第一份前端工作你用的技术比较简单,你可以写你自己负责切图,用jQuery写逻辑,用sass写样式,然后再下一份工作你可能技术加强了,你就可以写你用vue,angular来开发了一个单页面应用,还负责写了部分后端接口,这样可以让面试官觉得你有自我学习,不断进步的习惯。

123公司 ( 2016年9月 ~ 2017年9月 )

456项目

我在此项目负责了哪些工作,分别在哪些地方做得出色/和别人不一样/成长快,这个项目中,我最困难的问题是什么,我采取了什么措施,最后结果如何。这个项目中,我最自豪的技术细节是什么,为什么,实施前和实施后的数据对比如何,同事和领导对此的反应如何。

789项目

我在此项目负责了哪些工作,分别在哪些地方做得出色/和别人不一样/成长快,这个项目中,我最困难的问题是什么,我采取了什么措施,最后结果如何。这个项目中,我最自豪的技术细节是什么,为什么,实施前和实施后的数据对比如何,同事和领导对此的反应如何。

其他项目

(每个公司写2~3个核心项目就好了,如果你有非常大量的项目,那么按分类进行合并,每一类选一个典型写出来。其他的一笔带过即可。)


根据上面的模板你可以这样来写

广州XX有限公司(2016.09 ~ 2017.03)技术部-前端工程师 这个项目目的主要以成人学历为主线开发,有大专、本科、资格证等线上销售的教育培训产品。 我在此项目负责了前期界面研究,做出原型设计,原型切图,配合后台开发人员设计界面与开发功能,并负责框架开发,封装公共组件,以及根据需要引进第三方插件。 我在项目中遇到的最大困难是实现产品前端界面效果与设计图纸在某些界面上难以绝对统一 我采用Vue+jQuery开发方案,在细节上我运用了jwplayer.js、jquery.KinSlideshow来实现幻灯片轮播效果,增强用户体验更好等。

自我评价

最好突出自己的优势,比如学习能力,解决问题能力,带队能力,沟通能力和技术能力等等
想简单点可以这样写:

1.喜欢学习新技术,有良好的文档编写和代码书写规范,耐心打磨用户体验和用户界面 2.有良好的沟通能力和团队合作能力,性格随和,认真负责,自学能力强

详细的你也可以这样来

1. 对前端技术具有浓厚的兴趣,喜欢逛CSDN,博客园等技术论坛; 2. 具备良好的审美能力,有良好的代码编程习惯;(说好的审美,可以和设计师好好玩耍) 3. 学习适应能力强,愿意不断学习新知识丰富自己;(学习能力大企业最看重) 4. 抗压能力强,有信心在不同的工作强度下进行开发工作;(抗压技能点加上,去常加班的公司有独特优势,额) 5. 性格随和,具有良好的团队合作精神。(性格最好能适应团队)

开源项目和作品

选取你自己Github上的某个开源项目即可,当然开源项目最好写清楚文档,比如安装方法,核心代码的分析(这一段用于放置工作以外的、关键来证明你的能力的材料,也可以理解为其他工程师或者开源社区对你的认可度)

开源项目

(对于程序员来讲,没有什么比Show me the code能有说服力了)

  • Piano:🎹用键盘8个键演奏一首蒲公英的约定送给自己或月亮代表我的心送给她(项目的简要说明,Star和Fork数多的可以注明)
  • Articles:🔖分享我的学习片段和与你的回忆(项目的简要说明,Star和Fork数多的可以注明)

技术文章

(挑选你写作或翻译的技术文章,好的文章可以从侧面证实你的表达和沟通能力,也帮助招聘方更了解你)

技能清单

(我一般主张将技能清单写入到工作经历里边去。不过很难完整,所以有这么一段也不错)
以下均为我熟练使用的技能

  • 后端开发:PHP/Node
  • 后端框架:ThinkPHP/CodeIgniter
  • 前端框架:ReactJS/VueJS/AngularJS/Omi/EmberJS/Cocos2dJS/Ionic
  • 前端样式库:WeUI/Bootstrap/AntDesign/ElementUI/AmazeUI
  • 前端工具库:jQuery/Zepto/Lodash/Axios
  • 前端调试:Postman/Charles/Vue(React) Dev Tools
  • 前端插件:Swiper/Iscroll
  • 前端模块工具:Webpack/Gulp/RequireJS/Fis/Grunt
  • 预编译语言:Typescript/Sass/Less/Pug/Jade
  • Native开发:Weex/React Native/Cordova/Uni App
  • 小程序相关:Mpvue/Taro/Kbone/Wepy
  • 数据库相关:MySQL/MongoDB/PgSQL/PDO/SQLite
  • 版本管理、文档和自动化部署工具:SVN/Git
  • 单元测试:PHPUnit/SimpleTest/Qunit/Mocha
  • 云和开放平台:SAE/BAE/AWS/微博开放平台/微信应用开发

例如你可以这样写,当然你得根据自己的个人情况进行修改

1、熟练使用合理的结构和样式编写兼容主流浏览器的页面; 2、能适当运用CSS3使页面在现代浏览器上效果更佳; 3、熟练运用rem单位和flex布局实现不同浏览器宽度下的整页缩放; 4、熟练使用photoshop软件进行简单的图片处理,能根据图片特征保存成最优的格式; 5、熟练使用原生javaScript以及常用javaScript库,如jQuery、zepto; 6、能运用模块化、面向对象的方式编程; 7、了解http协议,能够根据http请求报文、响应报文和状态码排查问题; 8、熟练运用ajax实现异步数据传递,熟悉JSON等数据交换格式;了解php和数据库; 9、了解如何使用node.js搭建本地server模拟接口返回数据测试ajax接口; 10、能使用Bootstrap、WeUI、Ionic等前端样式库和框架进行开发; 11、熟练运用AngularJS,VueJS进行模块开发,了解双向数据绑定原理,熟练编写控制器、服务、组件、过滤器等 12、熟练使用Gulp对代码进行压缩合并;熟悉使用Git和SVN进行代码管理和版本控制; 

当然再举个例子(精通慎用!熟悉可以多用,不够自信就用了解吧,额哼)

1、熟练HTML、DIV+CSS的页面布局,能根据设计图完成页面制作 2、熟练响应式布局、弹性盒布局,熟悉基于HTML5的WebApp开发以及移动端适配 3、熟练主流浏览器的兼容性以及相应的适配技术,完成页面的完整兼容 4、熟练掌握HTML5、CSS3新增功能,了解Canvas动画制作 5、熟悉掌握原生JavaScript,熟悉jQuery,了解Zepto 6、熟练Bootstrap、AngularJS等前段框架,了解MUI、Vue框架 7、熟练使用AJAX进行数据交互,协助后端开发工程师完成项目 8、熟练JSON数据的使用以及处理方式 9、熟练LocalStorage、SessionStorage、Cookie等本地存储 10、熟悉使用Swiper、Font-awesome、jQueryUI以及jQuery插件等前端插件 11、熟练使用Cordova插件将WebApp打包成安卓App,实现混合App开发 12、熟练使用Visual Studio Code、SublimeText、HBulider、Git、Gulp、Scss、Photoshop 等开发工具

这里尽量不要重复叙述相同的技能点,保持好每一条都是独特的,每一条描述出现的英文技能点关键词尽量保持大小统一,比如Bootstrap、WeUI、Ionic,这样写没问题,但是不要个别大写个别小写bootstrap、WeUI、ionic,遇到强迫症的HR就尴尬了,不过有些还是例外的比如jQuery,还是第二个字母大写吧(...额,不解释),还有这里不要出现一些"感性"的描述,比如熟悉使用PHP,能跟后端工程师愉快的合作(什么鬼),这些句子就不要出现了

参考技能关键字

本技能关键字列表是在简历和招聘信息中统计出来的高频词。如果您的简历要投递给有机器(简历筛选系统)和非机器(不懂技术的HR)筛选简历环节的地方,请一定从下边高频关键词中选择5~10个适合你自己的。

SkillSkillSkillSkillSkillSkill
webjavascripthtml5css3H5js
ajaxw3cdivcsshtmlES6
vueangularreactbackbonejQueryzepto
jsonxmlxhtmlbomdomsvg
uiphotoshopweuibootstrapionicflex
atomvisual studio codehbuildersublime textfirebugdreamweaver
aspjspjavarubypythonphp
psmobilemvcmvvmmvpwww
httphttpsftpsslwebsocketseo
sasslessAMDCMDrequirejsseajs
githubstackoverflowsegmentFaultcodingcsdn
linuxmacwindowsiosandroid
gulpgruntwebpackgitsvn
flashjssdkphonegapcordovahybrid
firefoxchromesafaricharles
expressnodejskoa
semanticfoundationyui
actionscriptcoffeescripttypescript
mysqlwebapp
面向对象闭包继承组件化模块化数据结构
正则表达式兼容性安全性重构交互性能
微信小程序微信公众号阿里云知乎

简历注意项

  • 检查标点符号(不要中英文符号混着用)
  • 检查字母大小写(首字母该大写就统一,不要大小写混着来)
  • 检查专业词汇拼写(不要拼错单词,少用简写)
  • 检查错别字(用词惬当,语句通顺连贯)
  • 检查排版(格式工整整齐,排版统一,文字甚于图表,少用斜体、大写、下划线)
  • 检查真实性(不要子虚乌有也不要浮夸,因为撒了一个谎就需要更多的谎言来弥补,如果由于自身的不足没自信,那提高自身比撒谎走得更远)
  • 检查打印纸质油墨(如果可以的话)

在投简历之前必须检查大小写字母是否统一,中英文标点符号要统一,专业词汇是否正确,是否有错别字,细节决定成败,态度决定一切,投简历之前不检查清楚会让面试管觉得你不尊重面试,这会轻易被淘汰!

由于现在比较少手写纸质版简历,但如果需要自己用笔来写的内容(比如签名,笔试题)不要潦草涂乱,字迹要工整,因为字是代表你的形象,也是面试官心情的起点。

面试表现

千言万语总结为三条

  • 态度比技术重要
  • 做人比做事重要
  • 公平谦虚和乐观

面试是平等的,不是上下级的汇报,也不只是简单的一问一答(对方的设问,你也可以有疑问)
如果对方不是主动提问者,那自己要活跃一点,化被动为主动,一般面试官在前三道问题配合简历足够建立对你的印象(包括,技术,人品,沟通和态度等)
技术不行表现态度,遇到问题不懂别简单说不知道(面试官更倾向于你彰对这个问题的见解而不是准确答案),遇到不懂问题可以主动先说完自己的理解,剩余部分你可以尝试跟他留个联系方式(QQ,Facebook,Wechat,Email等)等你面试完回去再回复Ta一个答案(能彰显学习能力,态度与决心),有时候这不失是一个很好的机会,做不成同事也可以做朋友,还有给面试官纸质简历的时候,最好双手放过去,铺垫些礼貌的开场白。

面试题参考

这里收录了142多张现场纸质面试题集合,来自于不同面试者应聘不同公司的真实记录

面试前学习片段和笔记


其他参考

ArticleArticle
前端程序员经常忽视的一个JavaScript面试题怎样才是一份好的技术简历?
初级前端面试需要带什么作品?——知乎)如何面试前端工程师?——知乎
写给前端面试者如何写好一份前端简历
写给前端面试者(大漠,淘宝)谈谈面试与面试题、谈谈面试与面试题 II (winter,淘宝)
互联网公司技术岗实习/求职经验(实习内推+简历+面试+offer篇)(张秋怡,阿里巴巴)史上最全 前端开发面试问题及答案整理
前端开发面试题(马云云,ZTE)收集的前端面试题和答案(邱德清,阿里妈妈)
web前端面试相关的知识点(王文杰,乐视云)一道常被人轻视的前端JS面试题(沧海)
常见前端面试题及答案(默语,腾讯)前端工作面试问题(一)(Ruipeng Zhang,哈工大)

特别鸣谢

最后

最近比较忙,后面有时间继续整理,感谢您花时间阅读,期待你能找到一份不错的前端工作,加油!谢谢。如果本文能带您一丝帮助或者启发,请不要吝啬你的赞和Star,你的肯定是我前进的最大动力💞

本文参与了 SegmentFault 思否征文「如何"反杀"面试官?」,欢迎正在阅读的你也加入。

2022招聘季 | 怎么准备面试?面试官一般会问些什么问题? | web 前端

Posted: 20 Mar 2022 05:47 AM PDT

前两天北京下雪了,很冷。2022 招聘季,好像也比往年一些。

2021 年 K12 的大量裁员,腾讯、快手、字节、滴滴、蘑菇街也在裁员。

  • 想要换工作的同学应该准备些什么呢?
  • 面试流程是什么样子的?
  • 如何应对面试呢?怎么样可以提高成功率?

如果你关心上面的问题,并且你的目标位置不超出我的能力范围(高级工程师、阿里P6、字节2-1)。我会以我经验给你一些建议

面试流程

  1. 复习、准备
  2. 投递简历
  3. 一面
  4. 二面
  5. 三面
  6. HR 面
  7. 定级、谈薪
  8. 定入职时间、收 offer

怎么准备面试?

前期准备

个人介绍

每一场面试第一个环节都是「请做一下自我介绍」,所以这个问题一定要提前准备好。

如何准备?个人介绍重点是什么?

我们先从面试官角度来看一下面试官想知道什么?

  1. 姓名

    • 如果你的姓名中有生僻字、多音字叫错还是比较尴尬的
    • 确认你是否和面试官手中简历匹配。我就遇到过异常情况,候选人是面试 java 开发岗位
  2. 工作情况、项目概况、技术栈。虽然一般面试官会提前看过简历,但是有时候简历并不能很好的展示所有内容,所以需要一个简短的介绍来告诉面试官

    • 比如说上家公司做音视频的、上家公司做 ERP、上家公司做 IM 的、在线协作、协同办公、项目管理、云服务商。如果说你从 YY 直播跳虎牙直播,是比 YY 直播跳金山办公要匹配的多,公司匹配的话成功率更高,业务场景也更了解。比如说腾讯云、阿里云。
    • 比如说做移动端活动、做 toB 的服务平台、做电商。你之前做移动端面移动端岗位成功率更高,比如说抖音电商跳快手电商,就要比今日头条跳快手电商好。
    • 比如说使用 JQuery、Vue、React、React、threejs。你简历中写了 React、Vue,但是你更擅长 Vue,面试官无法从简历中看到两个技术在你手中的掌握程度
  3. 擅长什么?优势是什么?

    • 比如说你善于做移动端项目,善于使用 Vue,善于和其他部门沟通
    • 比如说你做过一年 Node 开发,可以独立开发,可以独立进行技术研发任务
    • 比如说你善于对项目进行优化
    • 甚至说你有过项目经理助理工作经验都是你的优势。
    • 社区影响力,github 有开源项目,思否、掘金、CSDN 有内容产出
个人介绍案例

接下来我们就可以针对面试官写一份合适的自我介绍

  1. 你好,我叫xxx,我在上一家公司做的项目是和 XXX 相关。
    一直从事于 web 方面的工作,掌握的前端技术有 xxx。
    在上一家公司中,我收获不单单是技术方面,主要还有业务能力,自学能力和同事的沟通能力,这都是我学到的,而且我认为很重要的。
    我的性格方面不算外向,也不算内向,跟朋友,同事相处,比较外向,在工作中,代码开发时,我是比较内向的,我喜欢全心全意的投入工作中。我也喜欢交朋友,偶尔跟朋友聚聚,聊聊。对于工作我总是抱着认真负责,有责任心,吃苦耐劳的态度工作。谢谢,以上是我的自我介绍。
  2. 面试官下午好,我叫XX,今天来应聘贵公司的前端工程师岗位。我从事前端开发两年多,有X年的XXX开发经验,在上家公司主要从事H5页面,后台管理系统,混合App等项目开发。平常喜欢逛一些技术社区丰富自己的技术,像思否,掘金之类,记录自己的工作总结和学习心得。
    我的性格比较温和,在工作中代码开发时我喜欢全心全意的投入,对于工作我总抱着认真负责的态度。面试官,以上是我的介绍,谢谢。
  3. 面试官你好;我叫XX,16年本科毕业于XXXX;毕业后在合肥卫宁,一家医疗软件公司工作,技术上前后端、数据库包括项目部署都做,属于全栈开发,18年来到上海入职京东一直到现在,做的是线下商场的后台及接口开发;老的项目还是 mvc,新项目都做了前后端分离,前端用到 H5、小程序 JQueryEasyUIVUEAngular,后端用到 .Net Framwork 4.5,4.6NetCoreJava,数据库用到 SQLServiceMysqlMongoDBRedis,中间件用到 RabbitMQ 消息队列,在公司是核心开发的角色,负责老项目的维护及二次开发,和其他开发相比,我的优势在于沟通交流能力比较强,还有我的责任心和学习能力也是比较强的,如果有幸加入XXXX,我相信我能快速融入团队,快速掌握公司相关的技术;谢谢领导。
  4. 面试官你好。我叫 XXXX。
    主要技术栈是 Vue 全家桶、jQuery、原生、NodeCSS 能力也不错,常见布局、伪类、过渡动画都会。
    上家公司是做音视频泛娱乐社交这块的,竞品如:全民K歌、唱吧、YY、快手、抖音之类的。
    我主要做移动端活动这块,年度庆典、小视频活动模版、直播间广告位之类的, PC 端也做一些,比如 PC的 IM 系统。因为公司的用户群体特性,对于兼容低版本设备有挺多经验。
    平时也经常在思否社区做问答,写文章笔记之类的,对于 BUG 排查有敏锐度,也善于沟通理解问题。

知识储备

知识储备是我们面试成功的必需品。主要分为两部分:护城河、扩展。这里主要还是靠平时积累,面试时针对高频且不会的加强记忆。

接下来我们还是针对前端面试来举例。

前端护城河

什么是护城河?这都不会干屁前端! 没办法就是这么卷,所以这里只能去卷一些八股文

但是我推荐还是由易到难高频到低频

  1. HTML、CSS、JS 基础知识

    • flex: 0 1 auto 是什么意思?
    • css 有哪些伪元素选择器
    • HTML5 语义化标签有哪些?
    • 闭包编程题
    • 手写 Promise 实现
    • 手写 requestCache 实现
    • 同步异步、微任务宏任务
    • 深拷贝
  2. Vue、React 常见基本框架

    • 生命周期钩子函数
    • 如何获取原始 DOM
    • 如何获取虚拟 DOM
    • 原理是什么?如何实现?
    • 对 diff 的理解
    • 数据双向绑定如何实现?兼容性?
    • 状态管理、组件通信
    • 路由跳转
  3. ElementUI、antd 基本框架搭配 UI 库

    • 组件化开发
    • 源码理解程度
    • 能力熟悉程度
  4. 浏览器基本知识

    • 浏览器缓存原理
    • 本地存储
    • 基本能力(上传、下载)
扩展

这里一般就不局限于前端,也不要求全部掌握,但是一般都会有一些擅长点

  1. 打包构建、前端研发工具(webpack、babel、gulp、vite、eslint)

    • webpack 升级。
    • eslint、stylelint。
    • git hook。(husky)
  2. 项目优化

    • 打包速度优化
    • 加载速度优化
    • 产物体积优化
  3. 跨端、跨平台、跨技术、跨语言

    • Node
    • Python
    • nginx
  4. git、svn。

    • 如何回滚代码
  5. 网络

    • https、http、http2,三次握手、四次挥手,如何保证安全
    • websocket
    • response 响应状态码
    • 强缓存、弱缓存

算法练习

算法练习没什么好说的,简单必须会,中等也要做一些,困难看个人能力,掌握常见数据结构。一般就是推荐力扣、牛客。

学习路径的话是先了解数据结构,然后针对性做题,做不出来看题解。

  1. 链表、数组、字符串

    • 查找是否有环?入环点在哪里?
    • 快慢指针、双指针
    • 先序遍历、中序遍历、后序遍历
    • 平衡二叉树
  2. 栈、队列
  3. 动态规划

项目复盘

项目一般是为了看看你做过什么,有什么出色的地方,不是为了听你说一堆没用的。

这里属于一个没有标准答案的题,也是一个可以提前准备的题。

我面试的时候准备了三个内容,并且对具体实现方法都做了细致的应对方案。

  1. PC IM 消息列表优化,其中有长列表优化、排序算法Bug及优化思考、多消息类型支持、消息手法机制、发送框实现等等

    • chrome 的 sort 使用了什么算法。chrome 低版本用的是什么算法。原理是什么。
    • 自己使用了什么算法?二分查找+插入排序。从 xxms 优化到 xxms。
  2. M 直播间广告位设计,其中有排序规则、动态载入规则、动态上线下线规则等等
  3. 项目加载速度优化

常见问题准备

一般来说会有一些高频问题,我们可以提前准备一下。当然具体还是要看你的技术栈是什么,你可以针对你的技术栈做特殊复习。

  1. 职业规划
  2. 为什么选择前端
  3. 个人介绍、项目介绍
  4. promise
  5. 浏览器强缓存、协商缓存
  6. 闭包
  7. vue 数据双向绑定原理
  8. 菲波那切数列
  9. 微任务、宏任务

面试前准备

如果说我们简历、知识储备、常见问题都准备就绪,那么接下来我们就可以开始以战养战

投递岗位

不建议海投,推荐你选择二线、一线、大厂,把自己心仪的公司放在第二三周

  1. 培养题感。(我面试的时候一道简单的题没做出来,超级尴尬,那道题是一道我十年前就会的题,但是第一次面试的时候怎么都想不起来。懵了)
  2. 培养自信、调整心态、告别紧张。如果你在面试的时候手里已经有了保底的 offer,那么你整个人的精神都会不一样。而且要价的时候也更大胆。(我面试的时候拒绝了金山,因为薪资低)
  3. 由易到难、由低到高,慢慢提升。其实有时候小公司也挺爽的(前端10人±),太少的就不推荐了。

可以选择适合自己的岗位投递,并不是比如说快手所有人都在做App,也会有做面向机构的服务平台,也会有面向电商的服务平台,还有对内的基建中台等等。选择一个适合自己的岗位比乱投强太多了

面试真题(针对性搜索)

这个就属于玩赖了,比如说你面阿里,可以针对性去找一些阿里一面面经,最好把岗位部门带上(会有意外惊喜哟

根据我面过的几家公司,网上是有真题的。

如果你常混社区的话,你甚至可以问问面过的前辈

面试后准备

面试完不代表着结束,收到 offer 才是结束。

面试复盘

所以这口气我们不能松,可以在面试后把所有题都记录下来,然后看看自己那个题掌握不够全面。

  1. 记录所有的面试题。网上搜索资料排查是否有回答不清晰,缺斤短两情况
  2. 录屏。做更完整的复盘,包括语气节奏、口头语、面部表情、反应速度等等。

    • 从我自己来说,我即兴是不如我有准备的。同样一份资料,在我无准备讲出来会多很多口头语,甚至我在讲的时候都会注意到这个问题,但是我无法解决,也克制不住,留给我调整的时间太少了。所以我只能做很多准备。
  3. 如果有不会的题,千万要查一下,然后记住。因为下一面有可能会考近似题。面试官也会通过这种方式来看你的自学能力。

    • 比如说你面第一个公司的时候,问了你权限的问题。那么你就应该重点关注一下,比如说按钮权限,异步权限,如何和服务端交互。这个问题在二面甚至说其他公司都会再次遇到

真题整理

上面说了复盘的重要性,也说了需要整理真题,这里主要是需要你注意分类,而且需要看到题后的内容

有时候面试题只是考点的一个应用场景,所以我们需要看到背后的知识,不只是把面试官问你的问题搞会。

一般来说面试题会符合部门场景,做 PC 的部门不可能问你移动端适配原理。

  • css

    • 「字节商业化」权重计算。

      • .a .b{color: #f00;} .b[data-role="1"]{color: #0f0}
    • 「阿里」选择器
    • 「快手搜索」「腾讯开放平台」css 单位
  • 网络

    • 缓存
    • https、http、http2

面试中有哪些最最最常见的坑

简历

到了我个人擅长的位置了。哈哈哈,我看过成百上千的简历,基本没有看错的。

  1. 一眼假的简历(工作时间、经历造价),一般针对实习生、应届生、培训班简历比较常见。

    • 熟练使用 Vue + react,其实一般来说为了维护成本学习成本,正常公司都会统一技术栈
    • 熟练使用 Node xxxx,目前来看全栈工程师并不是很多,都会有可能只是半桶水
    • 项目经历不属于同一家公司。比如说一个项目是医疗,一个项目是石油,项目跨度很大。这种一般我们考虑是否存在经验积累,一个持续迭代的精细项目才是我们想看到的。
    • 项目经历一看就比较水、或者开源项目、样子货。比如说网易云、饿了么,Vue1 的时候就开始仿饿了么,Vue3 还仿。

      图片来源于沸点:https://juejin.cn/pin/7072864...
      image.png
  2. 错别字简历、排版异常简历、雷同简历、异常简历

    • 部分简历会存在一些错别字,尤其是 Reat 之类的错别字。千万不要有错别字,会显得不细心。而且简历应该每个字都是用心写的
    • 学历异常、外派外包。我不歧视外包外派,但是从团队内任务分配、稳定性等多方面来说,的确不是很理想,需要多注意。
    • 雷同简历出现在同一学校、毕业季,上下午两个人简历一模一样。简历不是论文机器查重,不管是ERP的登陆权限系统,还是后台管理系统的登录权限系统,都是相同的简历,不是改个名称就叫不同简历。
  3. 异常的常见功能,对于有工作经验的人来讲反而不是常见功能。不是说常见功能,一定就是开发常开发功能

    • 比如说登陆注册、权限、菜单,登陆一般来说都是现成的功能,不会轮到你再去开发。
  4. 不是每一家公司都认为你的"玩具"很有意思。

    • 比如你说你写了 IM 相关的功能,如果你面试的也是 IM 相关的部门,那么很大可能性会揪着这个功能深挖。
    • 如果你写了性能优化,那么会给你一个近似的场景考你,看你有没有去做优化的动机。

面试准备(面试题、算法题权重问题)

建议你先准备基础知识题,然后常见题,最后准备算法题。一定要记住由易到难

如果不是高级、专家岗位,算法都可以暂时放弃。

  • 我面试一面简单算法没答出来,但是并不影响我面试结果。顺利通过面试拿 offer。
  • 有个朋友,基础题还没搞会,疯狂练习算法。到现在还分不清判断是否有环查找入环点两个题的区别。(快有十年工作经验了,还混在中级工程师)他以为算法好了就能换到大公司,但是算法题一般都在比较后面考察。

面试中

  1. 问薪资。一般来说前几面只是同级同事,不应该知道你的薪资。也无权决定
  2. "攻击"面试官。面试是否通过,就是当场面试官的一句话,很少公司有考核审查手段。所以不建议攻击面试官,放平心态(如果你不在乎这个机会,并且觉得和这样面试官共事比较恶心,那么怼就怼了。无所谓哈哈哈哈哈)。

    • 我就遇到过一个人,面到一半跑了,和 HR 反馈我不专业。但是对我并没有什么影响。
  3. "贿赂"面试官。虽然说当前这场面试,面试官可以帮助你,但是后面还会有其他考核(天黑路滑人心复杂呀)。所以还是靠自己真正的实力比较好。
  4. 拖时间。一场面试 20分钟至 60分钟,会存在定会议室(HR 不帮定会议室,且有时间限制),工期挤压(面试属于突发时间,且不计算在正常工期中)等问题。所以不建议在单个问题上拖时间,这样会导致无法全面考察,直接判负。
  5. 不敢问面试官。面试官都是比较和善的,一些面试官也比较善于引导你回答问题。即使没有引导出来,给你个方向也是好事,面试官经验比较丰富,如果是单独的技术咨询来看明显是赚了。

面试官一般会问些什么问题?

一面(基础知识)

一面一般是同级同事、同级领导(斜线领导),属于主力开发,业务经验也比较足。一面侧重基础知识

  1. 项目考察(2个)

    1. 介绍一下你最近做的项目
    2. 介绍一下你在这个项目里做了什么?有什么出色的嘛?
    3. 你是怎么进行项目优化的?你是怎么设计这块权限的?你是怎么实现这里的?你的设计思路是什么?
    4. 你在项目中负责什么?有什么特别满意的地方的?
    5. 你们团队构成是什么样子?开发流程是什么样子?有什么优化空间嘛?
    6. 印象中最深的一件事
  2. 基础之类(6个±)

    1. js、css、html 基础题。(2个)

      • css 样式优先级、权重计算、选择器、Flex 布局等等
      • js 场景类型、数组方法、闭包等等
      • html 语义化标签、其他能力?
    2. js 高级开发(2个)

      • Promise、await、async、异步同步、微任务宏任务
      • 继承、class、ES6+
      • TS
      • webpack、gulp、babel
      • 框架知识 Vue、React
      • requestCache、自动重试、异常上报
    3. 扩展知识(1个)

      • http2、http1.1、https
      • nginx
      • node
      • echarts、threejs、ui 等等
  3. 算法(1个)

    • 一般会出初中级难度的题,链表、树、栈队列比较常见,图好像少一些
    • 考察时间复杂度、空间复杂度
    • 会让你讲一下想法
    • 会让你思考有没有优化空间,如果有会逐步优化
  4. 简历异常点(学历、工作变动频繁)
  5. 普通聊天

    1. 为什么离职呀?
    2. 为什么选择我们公司呀?
    3. 为什么学前端呀?
    4. 住哪里呀?
    5. 平时有什么爱好
    6. 如何学习前端?
    7. 逛社区嘛?
    8. 对我们公司什么看法?
    9. 觉得今天面试表现怎么样?

二面(项目)

二面一般是不同组同事,一般来说是跨组交叉面试,当然会有可能不是前端。二面一般更侧重项目

  1. 项目考察(同一面,但有可能切入点不同)
  2. 高级应用(同一面,但是一般会和实际业务关联)
  3. 算法(同一面)
  4. 普通聊天(同一面)

这里可以看到,如果一面过了,二面基本是稳的。

因为一二面本身级别差不多,有可能面试官不是同技术(java)、升职不是技术(产品)

三面

三面一般是直系领导(50人至100人团队负责人),可能是前端,也可以是其他端,只能说肯定是个技术人员。

  1. 项目考察(同一面,切入点不同,会更加深入甚至扩展转换)
  2. 算法(同一面,但是有可能不用写,只考思路)
  3. 扩展知识

    1. 项目如何协调
    2. 分支如何管理
    3. 任务进度如何管理
    4. 上线流程是什么
    5. 发版流程是什么
  4. 部门介绍
  5. 普通聊天(同一面)

一般来说高级工程师就只有三面,三面的这个人就是决定你是否能入职的那个人,三面面试官一般也是一面面试官的直系领导。

HR 面试

  1. 普通聊天(同一面)

    1. 职业规划
    2. 为什么选择我们
    3. 你在原来公司负责什么
    4. 为什么离职
  2. 薪资期望

    1. 你当前的薪资
    2. 你期望的薪资

问面试官哪些问题?

一面

一面面试官一般就是你的同事,也是主力开发,是真正的打工人。所以这在一面环节你可以多问一些你关心的内容

  1. 面试官是谁?是否为你的同事。
  2. 工作情况
  3. 基建情况
  4. 团队氛围、团队规模
  5. 技术方向
  6. 自己表现怎么样
  7. 自己没答好的题,看看能不能给个方向。

二面

二面面试官因为存在交叉面试的情况,所以可用信息不太多。

  1. 面试官是谁?是否是你的同事,还是交叉?
  2. 自己表现怎么样
  3. 问面试官所在的团队氛围
  4. 自己没答好的题,看看能不能给个方向。

三面

三面面试官是你的直属领导,能管得事也比较多,你可以问一些其他的东西。

  1. 晋升机制、培训机制是否完善
  2. 团队氛围
  3. 所做业务
  4. 团队规划
  5. 有不懂的点也可以问,白嫖的交流机会啊。
本文参与了 SegmentFault 思否征文「如何"反杀"面试官?」,欢迎正在阅读的你也加入。

5年经验(专升本)入职B站分享

Posted: 18 Mar 2022 10:14 PM PDT

跳槽周期跨度

什么时候动了想法

大概在去年11月份的时候,我负责的业务线一直做不出成绩。而且整个公司的前端技术设施,都是我一手搭建出来的,再待在公司,没什么可成长的空间,就想跳槽了,期间考虑了几个可能性:

  1. 出国工作:这个想法来源,主要是看了某位网友肉身出国到新加坡的虾皮到新加坡打工两个月的经历分享,觉得挺有意思,也想尝试下,后面投了一些跨国企业,没啥反馈,估计是卡学历,所以就放弃了,后面再想想弥补外语的可能性
  2. 进入大厂:很多大厂都卡学历,即使是内推,也会被卡面试流程,我被小红书、京东、腾讯、美团,拼多多、虾皮、携程、喜马拉雅卡学历和卡面试进程,所以,能选择的大厂很少,最终入职了 B站

面了哪些公司

  1. 热身阶段(11月下旬 - 12月中旬)

    • 彩云科技,offer
    • 天壤智能,offer
    • 掌门一对一,offer
    • 帷幄匠心,offer
    • 驰骛科技,offer
    • 坚果云,二面挂
    • 乐言科技,二面挂
  2. 正式阶段(12月下旬 - 2月下旬)

    • 微盟(海外电商),一面挂
    • 字节(广告商业化),一面挂
    • 字节(线索中台),二面挂
    • 字节(抖音生活服务),二面挂
    • 网易(严选),四面完,最终评审挂
    • 七牛云(低代码平台),三面完,后续无反馈
    • B站,offer
    • 中通快递,offer

复习备战花了多久,面试到入职,花了多久

11月 - 2月 一直在陆陆续续准备,持续了 3 个月,主要是准备复习、复盘、刷算法
从开始面试,到入职,差不多 3 个月时间

做了哪些准备

主要准备了以下几个方面:

  1. 简历,仔细梳理了最近的工作经历,项目上做的事情,用到哪些技术
  2. 笔试,主要是算法和编程题
  3. 技术知识点查漏补缺

复习了哪些内容,刷了哪些题

  1. 八股文
  2. 编程题,手写各种方法
  3. 算法题
  4. React/Mobx/Vite && Webpack
  5. 自己写的博客

八股文

这块没啥好说的,把各大社区各位大神经常分享的一些文章看了下,再就是针对计算机基础知识和网络基础知识,做了一些总结,大概看了下面这些文章:

  1. 浏览器工作原理与实践
  2. 图解V8
  3. 从多线程到Event Loop
  4. 前端年后面试真题,会80%直接进大厂副本

编程

刷了下基本会手写的面试题以及再把面试过程中遇到的手写题总结下,基本上都还好

算法

算法这块不是我的长项,基本没接触,主要是把 LeetCode 热题 HOT 100,Easy 难度简单刷了下,针对大厂频出的算法题,看不懂的,就背答案,比如经典的反转二叉树、任意两个数的和等于 Target、反转单向链表等

后面根据一些面经和文档,比如:前端年后面试真题,会80%直接进大厂副本,做了一些针对性地练习和巩固

框架

React 部分复习了 setState原理、 diff 原理,深入了 hooks 和 fiber 原理,当然这里的深入不是去读源码,因为时间来不及,而是参考了比较多的文章:

  1. React技术揭秘
  2. React 进阶实践指南
  3. Vite
  4. 前端年后面试真题,会80%直接进大厂副本

Mobx 由于我日常使用比较多的,也被面试官深入问过,就自己搭建过前端脚手架和看了下 React 进阶实践指南里面有一篇讲了 Mobx。主要了解框架的主体逻辑、响应式原理、依赖收集的实现
此外也总结了一下 Redux 和 Mobx 的特点,因为面试官大概率会问:Redux 和 Mobx 的区别?

博客

由于本人经常会写播客,这点在投递简历的时候,是非常大的一个加分项,在多个技术社区有同名账号,经常发表一些文章,会受到面试官的青睐,我最近的面试,都有被面试官提到,你的博客写的不错,而且面试官也会从你的博客中来提问你,我就被问到以下问题:

  • Webpack 如何实现热更新的?
  • React 和 Vue 的选型?
  • Redux 和 Mobx 的区别?

这些问题,我都在博客上记录了,所以都是信手拈来

重点补了哪些能力和短板

  1. 技术部分:算法、编程题、对框架的细节思考(不仅仅是停留在使用层面)
  2. 业务部分:将自己对业务的理解和思考进行了总结
  3. 管理部分:归纳和提炼

面试中暴露了哪些问题,怎么应对的

思考和表达的连续性

前期热身的时候,准备内容的结构化不足,遇到有些没有准备到的问题,临场思考和组织,或者一遍表述一遍思考,导致表达的结构性和连续性较差,表现上就是重点不够、啰嗦、回答不上

解决:针对几大类问题梳理了整体的思维导图,表达的时候,跟进面试官的侧重点,挑选关键点进行结构
化表达

过往经历的思考不足

当面试官聊到过往短板问题或者低谷经历时,一开始的回答有点浮于表面,并且有一些规避的回答,受到了面试官的质疑

解决:认真、客观地重新梳理了对应的问题,从主观、客观层面进行重新组织语言进行回答

印象深刻的事情

每个公司面下来的感受

  • 字节:我自己的意向是字节跳动,连续面了三个部门,后面字节挂了两个部门,又被其他部门捞起来,,面到后面,越来越没底气,一方面自己的技术深度不够,另一方面,感觉面试官都很年轻,不会往你的优势方面去进行引导(面试经验不足),有的部门,面的感觉还可以,都挂了,很烦
  • 网易严选:网易严选的流程最长,我面了一个多月,当字节跳动挂了之后,网易严选是我最意向的部门,技术栈和业务方向匹配,该部门接下来一年要做的一些事情,也跟我的职业发展比较匹配,但是,四面完之后,就给我挂了,很遗憾,估计是被其他候选人给 PK 下去了
  • 七牛云:一面问八股文,手写代码,比如实现一个 call/apply 方法等,二面很常规,问问简历上的事,三面由部门负责人来面试(产品经理),感觉还可以,还让我加了微信,但后面流程一直没有推动,我问了 HR 多次,HR 给我的反馈是对我的印象挺好,希望保持联系,但后面流程就推动不下去了,再联系 HR ,就没回复
  • B站:全程聊的都还可以,比较顺利
  • 坚果云:印象最深的就是二面了,要开始二面前, HR 先联系我,说是一个开放性的题目,让我二选一,我选择了其中一个,让我做整体的思考,我做了大量的后端知识、服务端知识,但面试官只问前端方面的知识,沟通的不是很顺利,整体面下来,感觉双方的理解都是有问题

哪些经历让自己很难熬

字节和七牛云的 offer 一个都没拿到

字节面的感觉还可以,第二天就挂了,很难受,心态有点蹦
七牛云三面完之后,我等了一周左右,没给我反馈,我一直在推进,包括联系三面的面试官,询问结果,就是不给反馈,挂了也没任何反馈

最终抉择

当时手头上只有掌门一对一的 offer ,掌门一对一,HR 在催我入职, B站和网易严选还没有面完,无法拒绝,很纠结,当时做的选择就是,先入职掌门一对一,哪会儿中通联系上我,我就再面了一个中通,拿到了中通的 offer,然后再等B站和严选的流程了,自己对严选是非常意向的,但严选终面完还是被挂,就很头疼,在面严选的过程,B站正式 offer 发了,只能选择B站

如何克服了过程中的困难

  1. 定好策略,稳住心态
    跳槽的过程中不是来一家公司就面一家公司的,而是要根据自己的情况循序渐进。像我这样后面 offer 还比较少的时候,虽然心里也慌,但还是准备了一些兜底的策略,这点非常重要,而且本人是裸辞的情况下,面了很长的时间,心态一定要稳住,特别是互联网寒冬时期
  2. 与人交流,开拓思路
    我会和学长、靠谱同事、靠谱猎头进行交流,一方面可以从他们的角度看出自己的不足,另一方面也可以从他们那里得到帮助和思路

其他的心得体会和经验

面试的 4 个环节:简历准备 - 投递策略 - 面试和复盘 - offer 谈判和选择

  1. 简历准备
    简历准备不是要跳槽的时候才准备的,而是日常工作中要经常做总结,跳槽前对这些总结进行梳理归纳提炼为简历
  2. 投递策略
    职业生涯中每个阶段的跳槽,可能会有不同的策略,常规好用的策略是先找几家公司热身,然后再开始投递自己的目标公司,在投递过程中,时间节点也需要有一定的关注
  3. 面试和复盘

    • 面试可以通过一些公司热身试错,同时,在面试中要快速捕捉到面试官想挖掘的点,用简炼地话表达出来回应给面试官。如果面试官没有挖到你长项的部分,还需要考虑引导面试官,让自己展现出自己的长项
    • 在表达方面,如果直接表达不够结构化和清晰,可以先使用笔记或者思维导图梳理,然后自己预演熟练
    • 复盘很重要,要尽可能让自己犯过的错不再犯第二次,并且,复盘的内容是可以长期积累的
  4. offer 谈判和选择

    • 首先,要大概了解自己的市场价(对于大厂要了解自己的定级,对于中小厂要按市场水平),虽然这个比较难,但还是可能的
    • 如果有了解自己的市场价,那么在期望薪资这块,就可以报得有底气一些,也比较容易得到较高的涨幅。
    • 多拿 offer,通过技巧去让厂家自己在 offer 之间互相竞争,是相对容易的。虽然每个公司有自己的薪资体系,但招聘本质上是一个市场行为,厂家也要遵循客观规律

总结

跳槽是一项心力、体力、脑力都必须在线的活动

资料参考和分享

最后

欢迎大家加入B站,我在B站等你,随时可跟我交流面试经验

也欢迎大家关注我的博客

高频面试题整理

CSS

  1. css 重绘和重排如何理解
  2. 下面宽高各是多少
    <style>         .box {             width: 100px;             height: 100px;             padding: 10px;             margin: 10px;             background-color: #f00;             box-sizing: content-box;         }     </style>     <body>         <div class="box">12312312</div>     </body>
  1. span 标签在浏览器中偏移量是多少
    <style>         .test {             margin: 20px;         }     </style>     <body>         <div>             <span class="test"></span>         </div>     </body>
  1. 为什么 marign 0 auto 无法垂直居中
  2. 控制 z-index 的规则有哪些
  3. 元素替换概念有了解吗?
  4. 移动端 1px 像素如何解决?
  5. 一个元素隐藏有几种方式?
  6. 讲讲 BFC
  7. css 选择器
  8. display 有哪些属性
  9. 三列布局如何实现
  10. css 栅格布局
  11. flex: 0 1 auto 的含义
  12. css 如何实现一个正方形盒子(随父元素)自适应

JS

  1. js 有哪些基本数据类型
  2. 讲讲闭包是什么?
  3. var、let、const 的区别
  4. es5 如何实现继承

    • 原型继承方式(这里要注意下,很有可能让你手写)
    • 寄生组合继承
    • 引申到 es6 的 Class 语法糖
  5. es6 有哪些新的特性
  6. map 和 waekMap 的区别
  7. js 中 this 指向
  8. 讲讲 setTimeout 和 setInterval 的差异
  9. 用过函数节流和防抖吗?
  10. localStorage, sessionStorage, Cookie 之间的区别
  11. 下面输出结果是什么?
    var count = 100;     var obj = {         count: 200,         getCount: function() {             console.log(this.count);         }     }      const c = obj.getCount;     obj.getCount();     c();
  1. 下面输出结果是什么?
    var obj1 = { a: 100 };     var obj2 = Object.assign({}, obj1);     var obj3 = obj2;     obj3.a = 200;     console.log(obj1);     console.log(obj2);
  1. 下面打印结果是多少
    var p1 = new Promise((resolve) => {         resolve(1);     });     var p2 = new Promise((resolve) => {         setTimeout(() => {             resolve(2);         }, 0);     });     var p3 = new Promise((resolve) => {         resolve(3);     });     Promise.all([p1, p2, p3]).then((res) => {         console.log(res);     });
  1. 下面打印结果是多少
    async function promise2() {         function p1() {             return new Promise((resolve) => {                 resolve(1);             });         }          function p2() {             return new Promise((resolve, reject) => {                 reject(2);             });         }          function p3() {             return new Promise((resolve, reject) => {                 resolve(3);             });         }          try {             var p11 = await p1();             var p22 = await p2();             var p33 = await p3();             console.log(p11);             console.log(p22);             console.log(p33);         } catch(e) {};     }
  1. 请描述下 new 的执行原理
  2. 为什么 Object.prototype.toString.call 可以判断出变量,而不是通过 Object.toString.call ?
  3. 讲讲 gc 原理
  4. js 如何解决数值精度问题
  5. 讲讲事件循环

浏览器

  1. 浏览器缓存原理
  2. 单页应用如何提高加载速度?

React

  1. 讲讲 fiber 架构
  2. 讲讲对 diff 的理解
  3. 讲讲对 hooks 的理解
  4. 讲讲 class 生命周期
  5. setState 原理
  6. hooks 为何有一些规则使用条件
  7. 聊聊 react 事件机制原理
  8. 讲讲 useCallBack 和 useMemo 的区别
  9. 讲讲 useRef 和 ref 的区别
  10. react 组件传递状态有哪几种方式

状态管理

  1. mobx 原理
  2. redux 原理
  3. mobx 和 redux 的区别
  4. 函数式组件特点,和 hoc 区别
  5. 讲讲 redux 中间件

微信小程序

  1. taro 源码如何实现的?
  2. taro 2.x 和 taro 3 最大区别是什么?
  3. 微信小程序原理是什么?

打包工具

  1. 聊聊 vite 和 webpack 的区别
  2. 模块化有了解吗,讲讲 import 和 require 的区别?
  3. webpack 的原理
  4. 谈谈摇树的概念
  5. webpack 热更新机制原理
  6. vite 原理是什么?

设计模式

  1. 订阅发布和观察者模式和什么之间的区别,实现一个订阅发布者模式

编程题

  1. 如何实现一个深 copy
  2. 实现一个 new
  3. 柯里话函数的实现
  4. 实现一个函数 calc
function calc() {  }
  • 对于任意参数,实现累乘功能
  • 对于两次同样的参数,结果缓存,比如 (1, 2, 3)、(3, 1, 2)
  • 对缓存优化
  1. 实现一个 apply
  2. 针对深 Copy,变种的题目
  3. 实现一个 ajax
function ajax(options) { }
  • 在实现的 ajax 基础上实现调用所有请求,等到所有请求结果成功之后,再返回结果
  • 在实现的 ajax 基础上实现请求顺序调用,第一个调用成功之后,再调用第二个,以此类推
  1. 实现一个 repeat 函数,根据传入的参数,间隔时间,打印次数,来输出 log
 repeat(func, inteval, times){ … }  const r = repeat(repeatPrint, 10, 10);
  1. 用最精炼的代码实现数组非零非负最小值的索引 index
// 例如:[10,21,0,-7,35,7,9,23,18] 输出索引 5, 数值 7 最小 function getIndex(arr) {     let index = null;     ...     return index; }
  1. 该代码输出结果是什么?
const list = [1, 2, 5] const square = num => {     return new Promise((resolve, reject) => {         setTimeout(() => {             resolve(num * num)         }, 1000)     }) }  function test() {     list.forEach(async x => {         const res = await square(x)         console.log(res)     }) } test()
  1. 手写一唯数组转换树节点
var array = [     {pid: 4, id: 6617, name: "a",subNode:[]},     {pid: 5, id: 666, name: "a",subNode:[]},     {pid: 4, id: 6616, name: "a",subNode:[]},     {pid: 6616, id: 66161, name: "a",subNode:[]},     {pid: -1, id: 0, name: "a",subNode:[]},     {pid: 0, id: 4, name: "a",subNode:[]},     {pid: 0, id: 5, name: "a",subNode:[]},     {pid: 4, id: 10, name: "a",subNode:[]},     {pid: 10, id: 451, name: "a",subNode:[]},     {pid: 0, id: 98, name: "a",subNode:[]},     {pid: 98, id: 23, name: "a",subNode:[]},     {pid: 98, id: 523, name: "a",subNode:[]} ];  var toTree = function(tarArray) { }  toTree(array);

算法题

  1. 实现一个反转二叉树
  2. 一个迷宫,最短路径生成
  3. 实现单向链表反转
  4. 实际场景算法题
  5. twoSum 得到两个数的之后等于 target

其他

  1. 聊聊业务上的事
  2. 聊聊最复杂的业务如何处理的
  3. 如何做性能优化
  4. 聊聊前端脚手架
  5. 带团队中有哪些难点
  6. 聊聊迭代流程
  7. Git 如何覆盖某次 commit
  8. 长列表滚动,你怎么优化的
  9. 印象中最深的一件事

本文参与了 SegmentFault 思否征文「如何"反杀"面试官?」,欢迎正在阅读的你也加入。

读Flink源码谈设计:流批一体的实现与现状

Posted: 16 Mar 2022 05:38 PM PDT

本文首发于泊浮目的语雀:https://www.yuque.com/17sing

版本日期备注
1.02022.3.16文章首发

0.背景:Dataflow之前

在Dataflow相关的论文发表前,大家都往往认为需要两套API来实现流计算和批计算,典型的实现便是Lambda架构。

由于早期的流处理框架并不支持Exactly Once,导致流处理的数据并不精准。在这个基础上,一旦数据出现问题,则要导致大量的数据重放——这是因为事件往往是有时序要求的。因此,Lambda往往会通过流处理框架获取不是特别精准的结果,同时也会定时运行批处理程序,来获取更精准的结果——当更精准的结果出来时,我们就不需要前者了。

但这也带来的新的问题,所有的视图都需要流、批处理层各做一次,代码也要写两套,这带来了数据口径不同。可以说是在计算机资源以及人力资源上至少加了两倍的开销。

Kappa提出了将所有数据落到Kafka上,将存储模型与计算模型统一,但牺牲了时间——当数据量大时,回溯计算的压力巨大。

直到The dataflow model: a practical approach to balancing correctness, latency, and cost in massive-scale, unbounded, out-of-order data processing发表后,流与批有望在编程模型上统一, 上述相关的问题得以缓解

1. Flink的实现

Flink比起其他的流处理框架,更优在两点:

  1. 遵循Dataflow模型,在编程模型上统一流批一体
  2. 改进Chandy-Lamport算法,以更低的代价保证精准一次的实现

1.1 编程模型统一的背后

编程模型的统一具体体现在Flink SQL以及DataStream上。我们可以用相同的SQL or 几乎相同的代码跑流与批的任务。尤其是SQL,比起DataStream在声明式上更甚。因此用户在使用它们时,仅仅需要描述自己想要什么,而不是自己要做什么。具体做什么的事,Flink框架会帮你搞定。

在Flink框架上,目前主要解决了以下问题:

  • IO模型:批处理会更加关注吞吐,因此是pull模型;而流处理更加关注实时性,因此是push模型。基于这个条件,Source算子需要同时支持两种模型来适应不同的计算模式。详细见 FLIP-27: Refactor Source Interface
  • 调度策略:批处理的算子并不需要同时在线,前一批的算子完成后再调度后一批算子即可——由于计算资源往往比存储资源昂贵,这是一个很不错的优化方案。当然在资源充足的情况下,追求性能也可以不考虑这种策略;但流处理的作业需要作业启动时就全部被调度。因此,StreamGraph需要同时支持这两种模式——即LazyScheduling和EagerScheduling。
  • 批流的衔接:假如我们要分析近30天的数据,大多数情况下都是29天的离线数据加上最近一天的实时数据,如何保证衔接时数据不多也不少,其实是个麻烦的事情,在不少工程实践中会用一些比较hacks的方法。好在Flink1.4中引入了 Hybrid Source来简化这件事—— FLIP-150: Introduce Hybrid Source

1.2 Checkpoint不是银弹

Checkpoint是Flink框架中重要的容错机制,它的一个前提要求是数据源可重复读。在数仓场景下,虽然绝大多数情况下数据都不会发生变化——但也会有冷数据处理机制以及一些merge发生。这将对数据可重读造成一定的挑战。另外,在笔者负责的产品QMatrix中,对数据库做全量迁移时也会遇到类似的挑战:T1时刻读到的全量数据为集合1,而T2时刻读到的全量数据则为集合2。而MVVC也只能维持在一个session中。

上面描述的是在数据源要考虑的容错条件。在数据已经全部流入任务时,容错机制也需要重新考虑——尽量避免重复读取数据源以及上游任务的重算。因此社区引入了可插拔的Shuffle Service来提供Shuffle数据的持久用以支持细粒度的容错恢复——FLIP-31: Pluggable Shuffle Service

2. 剩下的问题:数据来源不统一

上述流批衔接的前提是数据源被分为了流数据源和批数据源。那么口径便是不统一的,这会带来一些对接成本。

目前流行的方案会采用数据湖(如IceBerg、Hudi、DeltaLake)来做流批数据的统一,并且由于大多数据湖都支持Time Travel,离线数据的可重复读问题也顺带解决。

另外,Pravega这种以流批一体存储为设计目标的软件可能也是解决方案之一。

3. 小结

在本文中,笔者和大家一起了解了流批一体的来源,以及Flink社区在流批一体中做出的努力。此外,我们也看到了有些问题并不是Flink这个框架可以解决的,需要整个大数据生态来一起演进,走向流批一体。

在文章的最后,感谢余空同学的交流与指导,我们一起写出了这篇文章。

编程中常用的加密算法

Posted: 18 Mar 2022 01:17 AM PDT

前言

编程中常见的加密算法有以下几种,它们在不同场景中分别有应用。除信息摘要算法外,其它加密方式都会需要密钥。

  • 信息摘要算法
  • 对称加密算法
  • 非对称加密算法

密钥

密钥(key,又常称金钥)是指某个用来完成加密解密完整性验证等密码学应用的秘密信息。

密钥分类

  • 加解密中的密钥:对称加密中共享相同的密钥,非对称加密中分公钥私钥,公钥加密私钥解密。
  • 消息认证码和数字签名中的密钥:在消息认证码中,消息发送方和接收方使用共享密钥进行认证。在数字签名中,签名使用私钥,而验证使用公钥。
  • 会话密钥和主密钥:每次通信只使用一次的密钥称为会话密钥(session key)。相对于会话密钥,重复使用的密钥称为主密钥(master key)。

密钥和密码

密码一般是由用户生成,具有可读性,可以记忆和存储,常用于软件管理,而密钥是供实现加密算法的软件使用,不需要具备可读性(不过在编程中为了方便阅读都进行Base64)。我们也可以通过密码来生成密钥。

密钥管理

  • 生成密钥:可以用随机数生成密钥,也可以用口令生成密钥。
  • 配送密钥:可采用事先共享密钥、使用密钥分配中心、使用公钥密码、使用Diffie-Hellman密钥交换。
  • 更新密钥
  • 保存密钥
  • 作废密钥

密钥生成

jdk 中 jce (Java Cryptography Extension) 包含了加密相关的所有API

生成对称加密算法的密钥

The KeyGenerator Class

    public static SecretKey generateKey(int keySize) {         KeyGenerator keyGenerator;         try {             keyGenerator = KeyGenerator.getInstance("AES");             keyGenerator.init(keySize);             return keyGenerator.generateKey();         } catch (NoSuchAlgorithmException e) {             // ignore             return null;         }     }

生成对称非对称加密算法的密钥

    /**      * 生成非对称密钥对      *      * @param keySize 密钥大小      * @param random  指定随机来源,默认使用 JCAUtil.getSecureRandom()      * @return 非对称密钥对      * @throws NoSuchAlgorithmException NoSuchAlgorithm      */     public static PPKeys genKeysRSA(int keySize, SecureRandom random) throws NoSuchAlgorithmException {         KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");         if (null != random) {             generator.initialize(keySize, random);         } else {             generator.initialize(keySize);         }         KeyPair pair = generator.generateKeyPair();         PPKeys keys = new PPKeys();         PublicKey publicKey = pair.getPublic();         PrivateKey privateKey = pair.getPrivate();         keys.setPublicKey(Base64.getEncoder().encodeToString(publicKey.getEncoded()));       keys.setPrivateKey(Base64.getEncoder().encodeToString(privateKey.getEncoded()));         return keys;     }

密钥协商Diffie-Hellman

密钥协商是一种协议,两方或多方在通过该协议建立相同的共享密钥,然后通讯内容进行对称加密传输,而不需要交换密钥。

大致过程:每一方生成一个公私钥对并将公钥分发给其它方,当都获得其他方的公钥副本后就可以离线计算共享密钥。

Java中提供了 KeyAgreement 可以实现密钥协商。

KeyAgreement 类

  • Alice 和 Bob 分别用他们的私钥初始化自己的密钥协商对象 KeyAgreement ,调用init() 方法;
  • 然后将通信的每一方的公钥 传入执行 doPhase(Key key, boolean lastPhase)
  • 各方生成共享密钥 generateSecret()
    public static void diffieHellman() throws Exception {         AlgorithmParameterGenerator dhParams = AlgorithmParameterGenerator.getInstance("DH");         dhParams.init(1024);         KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DH");         keyGen.initialize(dhParams.generateParameters().getParameterSpec(DHParameterSpec.class), new SecureRandom());          KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");         KeyPair alicePair = keyGen.generateKeyPair();         KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");         KeyPair bobPair = keyGen.generateKeyPair();          aliceKeyAgree.init(alicePair.getPrivate());         bobKeyAgree.init(bobPair.getPrivate());          aliceKeyAgree.doPhase(bobPair.getPublic(), true);         bobKeyAgree.doPhase(alicePair.getPublic(), true);         boolean agree = Base64.getEncoder().encodeToString(aliceKeyAgree.generateSecret()).equals(           Base64.getEncoder().encodeToString(bobKeyAgree.generateSecret())         );         System.out.println(agree);     }

信息摘要算法

信息摘要算法又叫加密散列算法,加密过程不需要密钥,常见的加密散列算法有MD系列SHA系列

一个理想的加密散列函数应该具备以下特性:

  • 任何信息传入后,输出的总是长度固定;
  • 消息摘要看起来是"随机的",这样根据原始信息就很难推测出值;
  • 好的散列函数碰撞概率应该极低,也就是不同信息传入后得到相同值的概率;

MD系列

MD5信息摘要算法(MD5 Message-Digest Algorithm),一种被广泛使用的加密散列函数,输出出一个128位(16字节)的散列值(hash value),MD5最初设计为加密散列函数,而目前发现它存在大量漏洞,所以不建议直接用作加密,不过在非加密场景下如:数据完整性校验,文件完整性校验它仍然有广泛的应用。

    public static String md5(String content) {         try {             MessageDigest digest = MessageDigest.getInstance("MD5");             byte[] bytes = digest.digest(content.getBytes(StandardCharsets.UTF_8));             return Hex.encodeHexString(bytes);         } catch (final NoSuchAlgorithmException e) {             throw new IllegalArgumentException(e);         }     }

SHA系列

安全散列算法(Secure Hash Algorithm,缩写为SHA)是一个加密散列函数家族,是FIPS(美国联邦信息处理标准)所认证的安全散列算法。能计算出一个数字消息所对应到的,长度固定的字符串(又称消息摘要)的算法。且若输入的消息不同,它们对应到不同字符串的机率很高。

它们分别包含 SHA-0、SHA-1、SHA-2、SHA-3,其中 SHA-0、SHA-1 输出长度是160位,SHA-2 包含 SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256,我们平时常用 SHA-256

    public static String sha256(String content) {         try {             MessageDigest digest = MessageDigest.getInstance("SHA-256);             byte[] bytes = digest.digest(content.getBytes(StandardCharsets.UTF_8));             return Hex.encodeHexString(bytes);         } catch (final NoSuchAlgorithmException e) {             throw new IllegalArgumentException(e);         }     }

对称加密算法

对称加密算法,双方持有相同密钥进行加解密,常见的对称加密算法:DES 3DES AES128 AES192 AES256。理解对称加密需要先明白下面几个概念:

  • 分组密码模式:将明文切割进行加密,再将密文拼接到一起。比如AES中会将明文数据切割为大小16字节的数据块,最后一块不够16字节时,使用Padding模式进行补充。
  • 填充(Padding):它有三种模式PKCS5、PKCS7和NOPADDING,PKCS5用缺少的字节数来填充,比如缺少5个字节就填充5个数字5,PKCS7缺少的字节数用0来填充。如果数据刚好是16的整数倍,PKCS5和PKCS7会再补充一个16字节数据来区分填充和有效数据,NOPADDING模式不需要填充。
  • 初始化向量:初始向量IV的作用是使加密更加安全可靠,在分组密码模式下IV大小对应数据块长度。
  • 加密模式:四种加密模式分别是:ECB(电子密码本模式)、CBC(密码分组链接模式)、CFB、OFB。ECB模式是仅仅使用明文和密钥来加密数据,所以该模式下不需要Padding,安全性也较弱,CBC模式数据分块并且使用传入IV依次进行异或操作,安全性也相对较高,所以目前一般都选择CBC模式。
  • 加密密钥:不同加密算法密钥长度不同,比如:DES 默认长度56位,3DES默认长度168位,也支持128位,AES默认128位,也支持192位,256位。我们一般根据密码生成密钥,密码长度需要满足算法密钥长度。

DES

DES 是对称加密算法领域中的典型算法,因为密钥默认长度为56 bit,所以密码长度需要大于 8 byteDESKeySpec 取前 8 byte 进行密钥制作。

 public static String encryptDES(byte[] content, String password) {         try {             SecureRandom random = new SecureRandom();             DESKeySpec desKeySpec = new DESKeySpec(password.getBytes());             SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");             SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);             Cipher cipher = Cipher.getInstance("DES");             cipher.init(Cipher.ENCRYPT_MODE, secretKey, random);             return Base64.getEncoder().encodeToString(cipher.doFinal(content));         } catch (Exception e) {             throw new RuntimeException(e);         }     }      public static String decryptDES(String content, String password) throws Exception {         SecureRandom random = new SecureRandom();         DESKeySpec desKeySpec = new DESKeySpec(password.getBytes());         SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");         SecretKey secretKey = keyFactory.generateSecret(desKeySpec);         Cipher cipher = Cipher.getInstance("DES");         cipher.init(Cipher.DECRYPT_MODE, secretKey, random);         return new String(cipher.doFinal(Base64.getDecoder().decode(content)));     }

3DES

3DES(即Triple DES)。是DES算法的加强,它使用3条56位的密钥对数据进行三次加密。它以DES为基本模块,通过组合分组方法设计出分组加密算法。比起最初的DES,3DES更为安全。密钥默认长度 168 bit, 密码需要大于24 byte,IV 是 8 byte 的随机数字和字母数组。

     public static String encrypt3DESECB(String content, String key, String iv) {         try {             IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));             DESedeKeySpec dks = new DESedeKeySpec(key.getBytes(StandardCharsets.UTF_8));             SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");             SecretKey secretkey = keyFactory.generateSecret(dks);             Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");             cipher.init(Cipher.ENCRYPT_MODE, secretkey, ivSpec);             return Base64.getEncoder().encodeToString(cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)));         } catch (Exception e) {             throw new RuntimeException(e);         }     }      public static String decrypt3DESECB(String content, String key, String iv) {         try {             IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));             DESedeKeySpec dks = new DESedeKeySpec(key.getBytes(StandardCharsets.UTF_8));             SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");             SecretKey secretkey = keyFactory.generateSecret(dks);             Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");             cipher.init(Cipher.DECRYPT_MODE, secretkey, ivSpec);             return new String(cipher.doFinal(Base64.getDecoder().decode(content)), StandardCharsets.UTF_8);         } catch (Exception e) {             throw new RuntimeException(e);         }     } 

AES

AES 高级数据加密标准,能够有效抵御已知的针对DES算法的所有攻击,默认密钥长度为128 bit,还可以供选择 192 bit256 bitAES-128 AES-192 AES-256

默认 AES-128 ,使用 PBEKeySpec 生成固定大小的密钥。

public static String encryptAES128(String plainText, String password, String salt) throws Exception {         SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");         byte[] saltBytes = salt.getBytes(StandardCharsets.UTF_8);         // AES-128 密钥长度为128bit         PBEKeySpec spec = new PBEKeySpec(           password.toCharArray(),           saltBytes,           1000,           128         );         SecretKey secretKey = factory.generateSecret(spec);         SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");         Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");         AlgorithmParameters params = cipher.getParameters();         IvParameterSpec iv = params.getParameterSpec(IvParameterSpec.class);          cipher.init(Cipher.ENCRYPT_MODE, secret, iv);         byte[] encryptedTextBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));          String encodedText = Base64.getEncoder().encodeToString(encryptedTextBytes);         String encodedIV = Base64.getEncoder().encodeToString(iv.getIV());         String encodedSalt = Base64.getEncoder().encodeToString(saltBytes);         return encodedSalt + "." + encodedIV + "." + encodedText;     }       public static String decryptAES128(String encryptedText, String password) throws Exception {         String[] fields = encryptedText.split("\\.");         byte[] saltBytes = Base64.getDecoder().decode(fields[0]);         byte[] ivBytes = Base64.getDecoder().decode(fields[1]);         byte[] encryptedTextBytes = Base64.getDecoder().decode(fields[2]);          SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");         PBEKeySpec spec = new PBEKeySpec(           password.toCharArray(),           saltBytes,           1000,           128         );          SecretKey secretKey = factory.generateSecret(spec);         SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");         Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");         cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(ivBytes));         byte[] decryptedTextBytes;         try {             decryptedTextBytes = cipher.doFinal(encryptedTextBytes);             return new String(decryptedTextBytes);         } catch (IllegalBlockSizeException | BadPaddingException e) {             throw new RuntimeException(e);         }     }

使用 AES-256 时可能会出现下面异常:

 java.security.InvalidKeyException: Illegal key size

JDK 1.8.0_161 及以上版本默认已经启用无限强度加密:

    static {         java.security.Security.setProperty("crypto.policy", "unlimited");     }

JDK 1.8.0_161以前版本需要手动安装 jce 策略文件下载地址

非对称加密算法

非对称加密使用一对密钥,公钥用作加密,私钥则用作解密。关于密钥大小,截至2020年,公开已知的最大RSA密钥是破解的是829位的RSA-250,建议至少使用 2048 位密钥。

非对称加密

    public static String encrypt(byte[] publicKey, String plainText) {         X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);         KeyFactory kf;         try {             kf = KeyFactory.getInstance("RSA");             PublicKey publicKeySecret = kf.generatePublic(keySpec);             Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");             cipher.init(Cipher.ENCRYPT_MODE, publicKeySecret);             byte[] encryptedBytes = cipher.doFinal(plainText.getBytes());             return new String(Base64.getEncoder().encode(encryptedBytes));         } catch (Exception e) {             log.error("Rsa encrypt error ", e);             throw new RuntimeException(e);         }     }      public static String decrypt(byte[] privateKey, String encryptedText) {         PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);         KeyFactory kf;         try {             kf = KeyFactory.getInstance("RSA");             PrivateKey privateKeySecret = kf.generatePrivate(keySpec);             Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");             cipher.init(Cipher.DECRYPT_MODE, privateKeySecret);             return new String(cipher.doFinal(Base64.getDecoder().decode(encryptedText)), StandardCharsets.UTF_8);         } catch (Exception e) {             log.error("Rsa decrypt error ", e);             throw new RuntimeException(e);         }     }

老板让我 7 天研究一个 Plausible 统计

Posted: 18 Mar 2022 02:25 AM PDT

开始文章前先扯个淡

最近在思考统计功能的时候,一开始是寻思着用程序配合mysql来进行存储,但是如果用mysql来存一些比较细节话的数据然后再做统计,数据量会非常庞大,然后又思考用mysql存储数据,然后用定时脚本或队列来按照具体要求更新到redis中,这样查询就很快了。

后来想起我们本身用的plausible来记录数据,然后寻思着用plausibleevent来记录自定义的goals,发现的确可行,模拟了500万数据之后,表现良好,感觉自己棒棒哒。谁知道数据量起来之后,2000万的数据就能让服务炸了。期间,也遇到了一些其他问题,也给官方提了一些建议,逼不得已不得不学了elixir,当然这是后话了。这里提一嘴这个plausible主要是给后来者提个醒,如果也想用plausible来做自定义的一些统计功能,最好是放弃掉,改为直接用ClickHouse来处理。

最后没有办法,我观察了plausible的数据存储结构,发现根本原因就是event的存储类型导致的,性能误解,从而萌生了去研究一下ClickHouse,看看自己实现能不能得到很好的性能,于是便有了这篇文章,如果对大家有用,欢迎探讨,如果理解有错的话,欢迎各位给我斧正。

行式数据库 vs 列式数据库结构对比

以下先以官方文档的表格来抛砖引玉,记录一下这段时间对于clickhouse的学习成果。

行式数据库

RowWatchIDJavaEnableTitleGoodEventEventTime
#0893543506621Investor Relations12016-05-18 05:19:20
#1903295099580Contact us12016-05-18 08:10:20
#2899537060541Mission12016-05-18 07:38:00
#N

列式数据库

Row:#0#1#2#N
WatchID:893543506629032950995889953706054
JavaEnable:101
Title:Investor RelationsContact usMission
GoodEvent:111
EventTime:2016-05-18 05:19:202016-05-18 08:10:202016-05-18 07:38:00

从表格上看,一下子比较难以理解,行式数据库和我们电子表格看到的表现一样,但是存储是一行一行的存储,列式数据库是以一列一列的存储。以下是以文件存储结构来做一个示例:

也就是说,对于行数据库,一行数据是形成一个完整的文件(实际底层具体存储不是这样子,这里是方便说明整个思想),这个文件包含了这一整行的每个数据字段,取到这一行就能拿到完整的数据。

而对于列式存储来说,一列数据为一个文件,如以上所展示的,WatchId 的数据全部存在WatchId文件里面。

然后我做了个脑图再次对这个结构做个对比。
数据表对比.png

以下用json来描述两种数据结构的区别:

  1. 行式数据库存储结构

    [  {      "id": 1,      "title": "t1",      "content": "c1"  },  {      "id": 2,      "title": "t2",      "content": "c2"  },  {      "id": 2,      "title": "t2",      "content": "c2"  },  {      "id": 3,      "title": "t3",      "content": "c3"  } ]
  2. 列式数据库结构

    {  "id": [      1,      2,      3,      4  ],  "title": [      "t1",      "t2",      "t3",      "t4"  ],  "content": [      "c1",      "c2",      "c3",      "c4"  ] }

列式数据库主要应用场景

列式数据库主要运用场景为OLAP(联机分析),也就是说主要应用于数据分析这一块,通常不用来处理具体的业务数据存储。行式数据库主要用于处理业务数据的存储,业务数据的特点是对单条数据经常会产生更新,会对数据一致性有非常严格的要求。对于OLAP来说,数据很少会有变动,所以通常来说,列式数据库主要目的是为了更快的写入以及更快的数据查询和统计。

以下我列出一些OLAP场景的关键特征:

  • 绝大多数是读请求
  • 数据以相当大的批次(> 1000行)更新,而不是单行更新;或者根本没有更新。
  • 已添加到数据库的数据不能修改。
  • 对于读取,从数据库中提取相当多的行,但只提取列的一小部分。
  • 宽表,即每个表包含着大量的列
  • 查询相对较少(通常每台服务器每秒查询数百次或更少)
  • 对于简单查询,允许延迟大约50毫秒
  • 列中的数据相对较小:数字和短字符串(例如,每个URL 60个字节)
  • 处理单个查询时需要高吞吐量(每台服务器每秒可达数十亿行)
  • 事务不是必须的
  • 对数据一致性要求低
  • 每个查询有一个大表。除了他以外,其他的都很小。
  • 查询结果明显小于源数据。换句话说,数据经过过滤或聚合,因此结果适合于单个服务器的RAM中

MergeTree(合并树) 数据表引擎

主要特点

MergeTreeClickHouse最基础的数据存储引擎,绝大部分时候都是用这个数据引擎。

MergeTree 系列的引擎被设计用于插入极大量的数据到一张表当中。数据可以以数据片段的形式一个接着一个的快速写入,数据片段在后台按照一定的规则进行合并。相比在插入时不断修改(重写)已存储的数据,这种策略会高效很多。

主要特点:

  • 存储的数据按主键排序。

    这使得您能够创建一个小型的稀疏索引来加快数据检索。

  • 如果指定了分区键的话,可以使用分区。

    在相同数据集和相同结果集的情况下 ClickHouse 中某些带分区的操作会比普通操作更快。查询中指定了分区键时 ClickHouse 会自动截取分区数据。这也有效增加了查询性能。

    通常来说,都是以时间来进行分区,从表象来看的话,就是一个分区是一个文件夹,这样可以避免一个文件存储大量的数据。

  • 支持数据副本。

    ReplicatedMergeTree 系列的表提供了数据副本功能。

  • 支持数据采样。

    需要的话,您可以给表设置一个采样方法。

结构说明

table_name.png

这段时间为了研究plausible的统计性能问题,主要也是围绕业务展开的,对其他几个数据引擎没有做深入的了解,其他引擎也是继承于MergeTree引擎,如果大家感兴趣可以了解一下,以下我大致列出其他几个数据引擎:

  • VersionedCollapsingMergeTree

    • 允许快速写入不断变化的对象状态。
    • 删除后台中的旧对象状态。 这显着降低了存储体积。
  • GraphiteMergeTree

    • 该引擎用来对 Graphite数据进行瘦身及汇总。对于想使用CH来存储Graphite数据的开发者来说可能有用。
    • 如果不需要对Graphite数据做汇总,那么可以使用任意的CH表引擎;但若需要,那就采用 GraphiteMergeTree 引擎。它能减少存储空间,同时能提高Graphite数据的查询效率。
  • AggregatingMergeTree

    • 该引擎继承自 MergeTree,并改变了数据片段的合并逻辑。 ClickHouse 会将一个数据片段内所有具有相同主键(准确的说是 排序键)的行替换成一行,这一行会存储一系列聚合函数的状态。
  • CollapsingMergeTree

    • 该引擎继承于 MergeTree,并在数据块合并算法中添加了折叠行的逻辑。
  • ReplacingMergeTree

    • 该引擎和 MergeTree 的不同之处在于它会删除排序键值相同的重复项
  • SummingMergeTree

    • 当合并 SummingMergeTree 表的数据片段时,ClickHouse 会把所有具有相同主键的行合并为一行,该行包含了被合并的行中具有数值数据类型的列的汇总值。如果主键的组合方式使得单个键值对应于大量的行,则可以显著的减少存储空间并加快数据查询的速度。

ClickHouse 数据库目前梳理的一些注意点

  • 不适合处理事务,没有完整的事务支持。
  • 缺少高频率,低延迟的修改或删除已存在数据的能力。仅能用于批量删除或修改数据,数据更新是异步的
  • 大部分数据查询适用标准的SQL语句,如果一直使用MySQL这种行式数据库,没太多切换成本
  • 定义排序字段或者说是定义索引,存储时候就会按照排序存储,范围查询速度非常快
  • ClickHouse提供各种各样在允许牺牲数据精度的情况下对查询进行加速的方法(目前我没有用过,暂时不知道应用场景)
  • 稀疏索引使得ClickHouse不适合通过其键检索单行的点查询。
  • 数据类型丰富,支持Map(key,value),Tuple(T1,T2,...),Array(T), Geo,Nested嵌套数据结构(类似于嵌套表)
  • Datetime 类型group bytoStartOf*开头的转换函数,如toStartOfDay,相比用DATE函数转换,能提升几十倍性能

欢迎体验 | Android 13 开发者预览版 2

Posted: 17 Mar 2022 08:15 PM PDT

作者 / Dave Burke, VP of Engineering

上个月,我们 发布了 Android 13 的首个开发者预览版。Android 13 围绕我们的核心主题打造,即隐私和安全、开发者生产力,以及支持平板电脑和 大屏幕设备。今天,我们为大家带来了 Android 13 开发者预览版 2,提供了更多的新功能和变更,供您在应用中尝试。您的意见会帮助我们将 Android 打造成更适合开发者和用户的平台,欢迎大家和我们分享测试反馈!

在今天的发布之前,Android 12L 功能更新已经于上周发布至 Android 开源项目 (AOSP) 中,这些功能更新会帮助大家充分利用超过 2.5 亿台的大屏幕 Android 设备。为了帮助大家深入了解 Android 13、平板电脑,以及我们在 Jetpack Compose 中提供的开发者生产力功能,我们准备了新一期的 #TheAndroidShow 节目,欢迎大家 点击这里 观看。

Android 12L 功能更新,现已加入 AOSP

在开始介绍本次的开发者预览版之前,让我们先回顾一下上周的新闻: 我们已经正式将 Android 12L 功能更新发布至 AOSP,并会在未来几周内推送至所有支持的 Pixel 设备。Android 12L 优化了 Android 12 在平板电脑上的体验,其中包括一些更新,比如用户可以在新的任务栏中通过拖放让应用进入分屏模式,通知栏和锁屏中新的大屏幕布局,以及改进的应用兼容模式。您可以阅读 发布博文 了解更多信息。

从今年晚些时候开始,Android 12L 将在三星、联想和微软的平板电脑和可折叠设备上按计划更新,现在是时候让您的应用做好准备了。我们强烈建议大家在分屏模式下用不同大小的窗口测试应用,以及针对不同的屏幕方向进行测试,并检查新的兼容模式的变化 (如果适用的话)。请访问 官方文档 了解更多关于 Android 12L 的开发信息。

最重要的是: Android 12L 的大屏幕功能也是 Android 13 的基础之一,所以您可以在 Android 13 上进行开发和测试,因为 Android 13 覆盖了运行 Android 12L 的平板电脑受众。我们将大屏幕视为 Android 未来的关键方向之一,我们将继续投入资源,为您提供更多相关工具,帮助您为平板电脑、Chromebook 和可折叠设备构建良好体验。您可以通过官方文档了解更多关于如何 开始为大屏幕进行优化 的信息,并查阅我们的 大屏幕开发者资源

下面让我们深入了解今天的 Android 13 开发者预览版 2 中的新内容。

隐私和用户信任

人们希望使用值得信赖的操作系统和应用,来处理他们最私密、最敏感的信息以及设备上的资源。隐私和用户信任是 Android 产品原则的核心。在 Android 13 中,我们将继续专注于在设备上提供更安全的环境,以及为用户提供更多的控制权,从而为所有人打造一个负责任、高质量的平台。以下是开发者预览版 2 中的新增内容。

通知权限 - 为了帮助用户专注于对他们最重要的通知,Android 13 为从应用发送通知引入了一个新的 运行时权限: POST_NOTIFICATIONS。以 Android 13 为目标平台的应用现在需要在发送通知前向用户申请通知权限。以 Android 12 或更低版本为目标平台的应用,系统将代您处理升级流程。我们也会持续优化这个流程。为了给您的用户提供更多的背景信息和控制权限,我们鼓励您尽早将目标平台设为 Android 13,并在您的应用中申请通知权限。请访问 官方文档 了解详细信息。

△ Android 13 中的通知权限对话框

△ Android 13 中的通知权限对话框

开发者可降级权限 - 一些应用可能不再需要某些之前由用户授予过的权限,这些权限曾用于开启某项特定功能或保留旧的 Android 版本中的敏感权限。在 Android 13 中,我们提供了 新的 API),让您的应用通过降级以前被授予的运行时权限来保护用户隐私。

更安全地导出上下文注册的接收器 - 在 Android 12 中,我们要求开发者针对应用清单中声明的 Intent 接收器来声明其可导出性。在 Android 13 中,我们要求您也为上下文注册的接收器做同样的事情: 在为非系统来源注册接收器时,为其添加 RECEIVER_EXPORTEDRECEIVER_NOT_EXPORTED 标志。这将有助于确保接收者不会被其他应用发送广播,除非其有意为之。虽然在 Android 13 中对此没有强制要求,但我们建议您声明可导出性,迈出保护您的应用的第一步。

开发者生产力

在 Android 13 中,我们正在努力为您提供更多的工具,帮助您为用户提供更精致的体验和更好的性能。以下是今天的开发者预览版中的一些更新。

日语文本折行改进 - TextView 现在可以按词句 (文節/Bunsetsu,听起来觉得自然的最小语义单位) 或短语来折行文本,而不是按字符折行。这会让日语应用有更佳的阅读体验。您可以通过在 TextView 中使用 android:lineBreakWordStyle="phrase" 来启用这种折行:

△ 不启用 (上) 和启用 (下) 短语折行的日语文本对比

△ 不启用 (上) 和启用 (下) 短语折行的日语文本对比

改进了非拉丁文字的行高 - Android 13 使用适合每种语言的行高,改进了非拉丁文字 (如泰米尔语、缅甸语、泰卢固语和藏语) 的显示效果。新的行高可以防止出现剪切,并改善字符的定位。您的应用只需将目标平台设为 Android 13,即可获得这些改进。请大家务必在使用新的行距时测试您的应用,因为变化可能会影响非拉丁语系的用户界面。

△ 以 Android 13 为目标平台的应用中的非拉丁文字行高改进效果 (下)

△ 以 Android 13 为目标平台的应用中的非拉丁文字行高改进效果 (下)

文本转换 API - 日语和汉语等语言的用户会使用拼音输入法,这往往会拖慢搜索和自动补全等功能。在 Android 13 中,应用可以调用新的 文本转换 API,让用户可以更快更容易地找到需要的内容。例如,以前的搜索需要日语用户 (1) 输入平假名来表示搜索词的发音,可能是一个地名或一个应用名 (2) 使用键盘将平假名字符转换为汉字 (3) 使用汉字字符重新搜索 (4) 获得搜索结果。在新的文本转换 API 的帮助下,日语用户只需输入平假名,就可以立即看到日文汉字的搜索结果,相当于跳过了第 2 和第 3 步。

彩色矢量字体 - Android 13 增加了对 COLR 版本 1 (请参考详细 规格介绍视频) 字体的渲染支持,并将系统表情符号更新为 COLRv1 格式。COLRv1 是一种新的、高度紧凑的字体格式,在任何尺寸下都能快速清晰地渲染。对于大多数应用,这项修改完全无法被感知到,系统会自动处理好一切。从开发者预览版 2 开始,您可以选择在应用中使用 COLRv1。如果您的应用是自己实现文本渲染,并使用系统字体,我们建议您使用 COLRv1 并测试表情符号的渲染效果。您可以在 Chrome 发布公告 中了解更多关于 COLRv1 的信息。

△ COLRv1 矢量表情符号 (左) 和位图表情符号

△ COLRv1 矢量表情符号 (左) 和位图表情符号

蓝牙低功耗 (LE) 音频 - 低功耗 (Low Energy, LE) 音频是下一代的无线音频,用于取代经典蓝牙,并实现新的用例和连接拓扑结构。用户可以通过它与朋友和家人分享和广播自己的音频,或订阅公共广播以获得信息、娱乐或无障碍辅助信息。它旨在确保用户能够在不牺牲电池寿命的情况下接收高保真音频,并在不同用例之间无缝切换,这是经典蓝牙无法做到的。Android 13 增加了对低功耗音频的内置支持,因此开发者们无需做任何操作即可在兼容的设备上获得这个新功能。

MIDI 2.0 - Android 13 增加了对新的 MIDI 2.0 标准的支持,包括通过 USB 连接 MIDI 2.0 硬件的能力。这个更新的标准提供了一些新功能,如更高的控制器分辨率,更好地支持非西方音调,以及通过单一音符控制器提供更佳的表现力。

应用兼容性

在发布新的平台版本时,我们优先考虑应用的兼容性,让系统更新的体验更快、更顺畅。在 Android 13 中,面向应用的变更都是可选的,以便您有更多的时间进行调整和适配,我们还更新了工具和流程,帮助您更快地做好准备。

随着开发者预览版 2 的到来,我们得以在 Android 13 的发布历程中更进一步,并继续改善整体的稳定性,现在是时候尝试新功能和变更,并向我们提交反馈了。我们特别希望得到关于 API 的反馈意见,以及 平台变更 对您的应用造成影响的详细信息。欢迎大家前往 反馈页面,与我们分享您的想法或报告问题。

现在您也可以开始进行 兼容性测试,并确定接下来需要完成的工作。我们建议大家尽早安排这些工作,以便在 Android 13 Beta 1 推出时发布与之兼容的更新。目前暂时不需要更改应用的 targetSdkVersion,但我们建议使用开发者选项中的 行为变更开关 来初步了解 Android 13 中的可选变更对应用造成的影响。

我们将在 2022 年 6 月抵达 平台稳定性里程碑,届时所有面向应用的系统行为、SDK/NDK API 和非 SDK 清单都将最终确定。到那时,您可以完成最终兼容性测试,并发布完全兼容的应用、SDK 或开发库。详见 发布时间表

△ 开发者选项中的应用兼容性开关

△ 开发者选项中的应用兼容性开关

即刻开始体验 Android 13

不论您是想体验 Android 13 的功能、测试应用还是 提交反馈,都可以从这次的开发者预览版开始。只需将系统映像 下载并刷入 Pixel 6 Pro / 6 / 5a 5G / 5 / 4a (5G) / 4a / 4 XL / 4 设备,即可立即开始使用。如果您没有 Pixel 设备,则可以在 Android Studio Dolphin 中使用带有 64 位系统映像的 Android 模拟器。如果想进行更广泛的测试,我们也提供了 GSI 映像。如果您已经在 Pixel 设备上安装了上一个预览版,则会通过 OTA 方式自动获得更新,包括之后的预览版和 Beta 版。请访问 官方文档 了解详细信息。

了解本次发布的完整信息,请访问 Android 13 开发者网站

欢迎您 点击这里 向我们提交反馈,或分享您喜欢的内容、发现的问题。您的反馈对我们非常重要,感谢您的支持!

小推理:React18比老版React更优秀的一个地方

Posted: 16 Mar 2022 10:17 PM PDT

大家好,我卡颂。

React18已经进入RC(release candidate)阶段,距离正式版只有一步之遥了。

v18新增了很多特性,今天,我们不聊新特性,而是来讲讲v18相比老版更优秀的一个细节:

v18中,组件render的次数可能更少

欢迎加入人类高质量前端框架群,带飞

状态从何而来

在如下组件中:

function App() {   const [num, update] = useState(0);   // ...省略 }

App组件render后会执行useState,返回num的最新值。

也就是说,组件必须render,才能知道最新的状态。为什么会这样呢?

考虑如下触发更新的代码:

const [num, update] = useState(0); const onClick = () => {   update(100);   update(num => num + 1);   update(num => num * 3); }

onClick执行后触发更新,更新导致App组件render,进而useState执行。

useState内部,会遵循如下流程计算num

  1. update(100)num变为100
  2. update(num => num + 1)num变为100 + 1 = 101
  3. update(num => num * 3)num变为101 * 3 = 303

即,App组件render时,num为303。

所以,状态的计算需要先收集触发的更新,再在useState中统一计算。

对于上述例子,将更新分别命名为u0~u2,则状态的计算公式为:

baseState -> u0 -> u1 -> u2 = newState

Concurrent带来的变化

Concurrent(并发)为React带来了优先级的概念,反映到状态计算上,根据触发更新的场景,更新拥有不同优先级(比如onClick回调中触发的更新优先级高于useEffect回调中触发的更新)。

表现在计算状态中的区别就是,如果某个更新优先级低,则会被跳过。

假设上述例子中u1优先级低,那么App组件render时,计算num状态的公式为:

// 其中u1因为优先级低,被跳过 baseState -> u0 -> u2 = newState

即:

  1. update(100)num变为100
  2. update(num => num * 3)num变为100 * 3 = 300

显然这个结果是不对的。

所以,并发情况下React计算状态的逻辑会更复杂。具体来讲,可能包含多轮计算。

当计算状态时,如果某次更新被跳过,则下次计算时会从被跳过的更新继续往后计算。

比如上例中,u1被跳过。当u1被跳过时,num为100。此时的状态100,以及u1他后面的所有更新都会保存下来,参与下次计算。

在例子中即为u1u2保存下来。

下次更新的情况如下:

  1. 初始状态为100update(num => num + 1)num变为100 + 1 = 101
  2. update(num => num * 3)num变为101 * 3 = 303

可见,最终的结果303与同步的React是一致的,只是需要render两次。

同步的React render一次,结果为303。

并发的React render两次,结果分别为300(中间状态),303(最终状态)。

新旧Concurrent的区别

从上例我们发现,组件render的次数受有多少更新被跳过影响,实际可能不止render两次,而是多次。

在老版并发的React中,表示优先级的是一个被称为expirationTime的时间戳。比较更新是否应该被跳过的算法如下:

// 更新优先级是否小于render的优先级 if (updateExpirationTime < renderExpirationTime) {   // ...被跳过 } else {   // ...不跳过 }

在这种逻辑下,只要优先级低,就会被跳过,就意味着多一次render

在新版并发的React中,优先级被保存在31位的二进制数中。

举个例子:

const renderLanes = 0b0101; u1.lane =           0b0001; u2.lane =           0b0010;

其中renderLanes是本次更新指定的优先级

比较优先级的函数为:

function isSubsetOfLanes(set, subset) {   return (set & subset) === subset; }

其中:

// true isSubsetOfLanes(renderLanes, u1.lane)  // false isSubsetOfLanes(renderLanes, u2.lane)

u1.lane包含于renderLanes中,代表这个更新拥有足够优先级。

u2.lane不包含于renderLanes中,代表这个更新没有足够优先级,被跳过。

但是被跳过的更新(例子中的u2)的lane会被重置为0,即:

u2.lane = 0b0000;

显然任何lanes都是包含0的:

// true isSubsetOfLanes(renderLanes, 0)

所以这个更新一定会在下次处理。换言之,在新版并发的React中,由于优先级原因被跳过,导致的重复render,最多只会有2次。

总结

相比于老版并发的React,新版并发的Reactrender次数上会更有优势。

反映到用户的感官上,用户会更少看到未计算完全的中间状态

No comments:

Post a Comment