提交 1189fbb6 编写于 作者: 沉默王二's avatar 沉默王二 💬

分库分表

上级 05ad5982
---
title: 编程为什么那么难
shortTitle: 编程为什么那么难
author: 林子er
category:
- 博客园
---
**面向失败编程**是编程中最难的事情。
话说程序员小林的某一天:起床->吃饭->坐地铁->到公司->敲代码->回家->玩游戏->睡觉。
这一天的另一个版本:起床->吃饭->坐地铁->到公司->突然要 24 小时健康码->进不了公司->坐地铁回去->地铁停运了->上厕所->踩到屎滑倒->摔成脑震荡。
第二个版本充满意外,貌似有些极端,但你我天天在新闻上看到类似的事情,说明它其实每天都在发生。
程序也是如此。
程序员小林给公司开发的某个系统,用户量暴涨;三年后公司上市了,小林喜迎白富美。
另一个版本:上线后第二天被 SQL 注入删库了,造成大量投诉;小林被老板痛骂一顿后,卷铺盖走人了。
程序的世界充满意外,你我的每一行代码几乎都是 bug。
写出可用的系统很容易,但写出健壮的系统很难。
### 一个”简单“的例子
我们通过储值卡消费这个例子来看看如此”简单“的案例到底存在多少让人眼花缭乱的失败场景。
假设我们给某个加油站开发个储值卡系统,用户可以往里面充钱,可以用储值卡加油消费,类似你在理发店、洗脚店开的那种充值卡。
我们看看车主加油消费的场景——而且只看这个场景中的”储值卡扣款“这一个结点。
正常流程(简化版)大致是这样的:
![image-20220420094234062](https://img2022.cnblogs.com/blog/1997761/202204/1997761-20220421092032877-918840117.png)
流程很简单,加油员加完油后,用户掏出手机扫码进入付款页面,输入油枪、金额,选储值卡支付,输完密码后点提交;后端创建订单后调卡服务的扣款接口执行扣款(传入卡号、订单号、金额);卡服务扣款成功后返回告知用户付款成功。
”这个需求大概要几天开发?“产品经理问小林。
”五天。“小林觉得五天绰绰有余。
”三天吧,这周我们就要上线。“
”那就三天。“小林觉得其实三天足够——不就一两个接口调用嘛,卡服务是现成的。
于是小林撸起袖子开始敲代码。进展比预想得要顺利,两天就敲完了(多少加了点班),一天测试完成,第四天就上线了!
某天夜里,小林正在撸猫时,运营同学打来电话:某车主的卡被莫名其妙扣款了!
事情是这样的:车主鲁某加了 3000 元的油,选择用储值卡支付,结果系统提示扣款失败,于是鲁某换微信付款成功,开车走人了。
蹊跷的是:鲁某十分钟后收到消息说卡扣掉了 3000 元!
明明说支付失败,怎么扣了 3000?于是鲁某打电话找油站闹。
小林赶紧排查日志,发现**上图中地第 3 步(调卡服务的扣款接口)超时了,于是业务系统告知前端扣款失败**
调卡服务扣款接口超时,业务系统能直接返回失败给前端吗?
不能!
**因为接口超时并不能说明卡服务那边实际上到底有没有扣成功**(有可能卡服务处理成功了,但返回的时候网络出问题;也有可能卡系统负载高,业务系统等待超时从而断开连接)。
我们看看上面的异常是怎么发生的:
![image-20220420111711176](https://img2022.cnblogs.com/blog/1997761/202204/1997761-20220421092032652-912788247.png)
第四步超时后,业务后台直接告知车主支付失败,但实际上卡系统仍然在扣款!
那怎么办?告诉车主”请您稍后查看支付结果?“
怎么可能!
一个想法是超时后业务系统调卡服务的查询接口,看看这笔订单实际是否支付成功。
问题是,如果查询接口调用也超时呢(卡系统负载高的情况下这个概率很大)?
另外,查询接口返回没有扣款成功就能直接告诉用户扣款失败吗?
不能!
因为查询接口查数据库的时候,数据库里面没有记录,但有可能前面发起的那个扣款逻辑仍然在执行,稍后仍然会发生扣款。
既然怕查询的时候扣款逻辑仍然在执行,那我们能不能等一会(比如五分钟)再查结果呢(等那个可能的扣款执行流跑完)?
也不能!
因为车主在那等着呢!难道手机上一直在那转圈,跟车主说现在负载高,请先喝杯茶,让子弹飞一会?
因为必须要立即告知用户处理结果,所以这种情况下(扣款超时且未查到扣款记录)只能告诉用户扣款失败。
只不过,在告知用户之前,业务系统需要先撤销本次扣款申请,告诉储值卡系统本次扣款流程不能执行了(回滚本次事务)。
于是小林做了如下优化:
![image-20220420114150613](https://img2022.cnblogs.com/blog/1997761/202204/1997761-20220421092032636-1770812664.png)
现在系统健壮多了,很久没出现上次的问题了,小林又跑去撸猫了。
某天深夜,小林又接到运营同学电话:上次的问题重现了!
尼玛,见鬼了!
小林又跑去查日志,发现确实是扣款接口超时了,但撤销接口调成功了(虽然调了几次才成功)——那为毛还扣了钱啊?
想了半天,小林终于发现了问题:和前面提到的查询问题一样,撤销的时候同样无法保证那个该死的扣款流程已经跑完了啊!这次是因为撤销逻辑确实执行了,但执行的时候扣款逻辑还在跑(还没写库)!
所以撤销接口必须考虑两种情况:
1. 撤销的时候,扣款已经发生了——此时能正确撤销;
2. 撤销的时候,扣款还没发生,但扣款流程正在执行——此时撤销会失败,稍后钱仍然会被扣掉;
于是小林就想:既然扣款超时后立即调撤销接口有可能因时序问题导致撤销失败,那我把撤销操作做成异步调度不就行了嘛——在一段时间内(比如五分钟)如果因未找到记录而撤销失败,就稍后重试。
小林的撤销逻辑是这样的:
![image-20220420143256054](https://img2022.cnblogs.com/blog/1997761/202204/1997761-20220421092032651-1838918260.png)
原本由业务系统同步调撤销接口,现在改成走调度系统异步撤销,业务系统投递撤销任务完成后立马返回结果给客户端。
因为有异步重试机制,撤销总是能成功(除了实际中几乎不会发生的极端情况),因而这次一定能保证不会意外扣钱!
小林同学抱着如释重负的心态继续撸猫。
然而,安稳日子没过几天,一个雷电交加的夜晚,手机再次响起:车主储值卡消费的钱莫名其妙给人家退回去了!油站打电话要我们赔偿!
小林赶紧查日志,发现场景是这样的:车主王某用储值卡支付 1000 元油款,失败了;十几秒后车主再次用储值卡发起支付,成功了。
支付最终成功了,莫非人工退钱了?没看到任何退款记录啊?
抓耳挠腮,百思不得其解。小林只能打电话给储值卡系统负责人小李。
小李一顿查日志,最终发现这笔钱是被调度系统调撤销接口给撤销了!
小林如梦方醒,才知道之前自己自鸣得意地犯了个天大的错误。
本次消费,业务系统共向储值卡系统发起了两次扣款申请——**虽然都是同一笔订单的扣款,却是两个独立的事务**
小林(以及储值卡系统)的错误在于,撤销操作是作用在订单上,而不是事务上。
在本次事故中,第一次扣款超时后,业务系统投递了撤销任务;而后车主又对该笔订单(订单号相同)发起了第二次扣款,成功了;与此同时,调度系统第一次撤销失败(卡系统未找到消费记录,或者接口超时),一段时间后又发起第二次撤销——而这个时候,车主已经完成了第二次扣款且成功了,于是这次的撤销便作用在这个成功的扣款上(储值卡系统的扣款和撤销接口都是根据订单号来的,它能保证同一笔订单不会重复扣款,但撤销的时候无法区分扣款是哪次发起的)。
我们画下流程:
![image-20220420152519360](https://img2022.cnblogs.com/blog/1997761/202204/1997761-20220421092032630-1781175288.png)
如图,第二次的扣款被调度系统撤销了。
小林和小李这才发现**需要给扣款和撤销接口增加事务编号**
之前扣款接口主要参数是 card\_no、order\_code、amount,现在变成 card\_no、order\_code、trans\_id、amount。
之前撤销接口参数是 order\_code,现在变成 order\_code、trans\_id。
通过 trans\_id 将扣款和撤销绑定到同一个操作事务上,只会撤销相应 trans\_id 的扣款操作。
trans\_id 由客户端根据当前时间毫秒数生成(后面会说为啥取毫秒时间戳),它不一定需要全局唯一,只需要针对同一个订单是唯一的即可。
加了事务的概念后,小林和小李发现压根不需要通过调度系统不断尝试,只要保证撤销接口调成功就能保证对应的扣款事务一定能够被撤销(或者阻止执行)。
现在撤销接口做两件事:
1. 写入一条撤销记录;
2. 试图撤销掉已经产生的扣款;
撤销逻辑如下:
![image-20220420205541356](https://img2022.cnblogs.com/blog/1997761/202204/1997761-20220421092032593-203950863.png)
再看看扣款的逻辑。
扣款记录表大致长这样子:
![image-20220420184516421](https://img2022.cnblogs.com/blog/1997761/202204/1997761-20220421092032847-1446149878.png)
扣款逻辑如下:
1. 先检查是否存在对该扣款事务的撤销记录,如果存在,则拒绝处理;不存在则继续;
2. 再检查是否存在该订单的扣款记录:
2.1 如果不存在,则走正常扣款流程;
2.2 如果存在记录,则要比较事务编号:如果已存在的那条事务编号小于当前的,则用当前的事务编号覆盖,否则不做任何处理(后面会解释这么做的原因);
流程图如下:
![](https://img2022.cnblogs.com/blog/1997761/202204/1997761-20220423192133909-244591209.png)
现在我们看看当撤销流程执行时,被撤销的扣款事务处于不同状态下的情况:
1. 扣款事务执行失败。此时压根不会产生扣款;
2. 扣款事务已经执行完毕,产生了实际扣款。此时撤销流程会撤销掉这笔扣款;
3. 扣款事务正在执行中,还没有写库,但稍后会写库。扣款事务实际写库之前,会先检查是否存在对该事务的撤销记录,因为先前撤销流程已经写入了一条对该事务的撤销记录,扣款事务此时会查到撤销记录,从而阻止本次扣款事务写库(本次事务主动回滚)。
由于撤销的时候是按事务编号来的,所以不会撤销别的事务的扣款。
现在我们解释下为何要用当前时间的毫秒时间戳作为事务编号。
回到上面车主王某的场景。王某第一次用卡支付超时,于是他决定重试。该场景中,卡系统接收到同一笔订单的两次扣款事务以及一次撤销事务。假如两次事务都尝试写库,那么当后面的事务(不一定是第二次扣款的那个)尝试写库时,肯定已经存在一条扣款记录,此时后面这个事务要如何做?
1. 用后者的事务编号替换掉前者的。
2. 不做任何处理。
> 两次事务的执行逻辑完全相同,产生的扣款记录数据也是完全相同的——除了事务编号和扣款时间。
这里的关键是,**我们无法确定第一次扣款、第二次扣款、对第一次扣款的撤销这三个请求写库的先后顺序**
所以,如果采用方案 1,替换事务编号,那么当第二次的提交先写库时,后面事务(第一次提交的扣款请求)的替换会导致事务编号变成了待撤销的那个,因而很可能会被撤销掉,这就会导致用户付的钱莫名其妙被退回了。
如果采用方案 2,不做任何处理,那么当第一次的提交先写库时,事务编号就一直是待撤销的那个,也会被撤销掉,导致用户付的钱莫名其妙被退回。
也就是说,无脑替换或不替换都是有问题的。
![image-20220420220641797](https://img2022.cnblogs.com/blog/1997761/202204/1997761-20220421092032842-1060919890.png)
*第一次扣款事务先写库的情况*
![image-20220420221122078](https://img2022.cnblogs.com/blog/1997761/202204/1997761-20220421092032649-874208200.png)
*第二次扣款事务先写库的情况*
实际的业务场景是,对于同一笔订单,无论发出多少次扣款请求,只允许一次成功,而且这次成功的扣款不能被误撤销。有很多方案可以实现这一点,不过有些方案需要增加额外表,有些则需要为同一笔订单维护多条扣款记录,这些都会带来额外的复杂性。
我们采取事务序列号(毫秒时间戳)的方式来保证扣款事务的时序性,只允许后面覆盖前面的,不允许反过来覆盖。其基于这样的事实:用户如果对同一笔订单发出多次扣款请求,那一定是前面扣款失败了,因而业务系统会为前面那些失败的扣款发出撤销请求,所以只要保证仅允许后面覆盖前面的事务,就不会造成误撤销(因为唯有最后那个扣款事务不会存在撤销请求。感兴趣的可参照上面的图示推演一下)。
> 这里说的事务是指一次扣款处理流,不是指数据库事务。
### 所以呢?
我不想编程了,说真的,这么个简单的扣款场景就扯出这么多幺蛾子,太难了!
现实中比这复杂的场景多得是。
程序员到底是怎么活下来的?
答案是,他们的一生是在没完没了的 bug 中度过的。
90% 以上的 bug 都是因为对失败场景考虑不周导致。
如果把现实看成事件流,那么事件流中的绝大多数节点都有不止一个出口分支(典型的是”正常“和”异常“)。2022 年 4 月 30 日晚,小林同学可能躺在床上玩游戏,也可能躺在 ICU。
系统(特别是业务系统)是对现实世界业务的反映,每个节点同样存在多种可能。
典型的业务流分析步骤是这样的:
![image-20220420225002673](https://img2022.cnblogs.com/blog/1997761/202204/1997761-20220421092032841-805463935.png)
几乎所有的结点都要考虑失败场景,而对于一些失败场景的补偿措施仍然可能失败,如此递归,最终由自动补偿系统(如漏单检测/补偿系统)或人工处理来兜底。
失败的一大重要根源是分布式。
不要提什么单体架构,做 web 开发的,自入行第一天起就面对分布式系统。
典型的分布式是前后端交互。自 ajax 出世以来,前后端接口交互成为常态,接口失败也是每个程序员都会遇到的问题。很大部分的前后端交互失败的场景没有得到很好地处理(特别是超时),比如没有去重,导致重复写入数据。
自从微服务横行以来,后端开发人员无不被分布式事务搞得焦头烂额。业界也总结了些解决方案,比如两阶段提交、SAGA、TCC 等,但真正实现起来都不简单,一个看似简单的业务都会搞得很复杂。所以业界又搞了些现成的开源方案如 seata、DTM。
### 还有救吗?
好消息是,不是所有的系统都需要那么高的可靠性保证,也不是所有的失败场景都要做补偿处理。
你可能是在一家初创公司,别说系统一分钟不可用了,就是库被删了估计也没事。
你做的系统可能只是给内部人员用用,凡是遇到失败就抛异常,大不了人工去修复数据也是可以的。
这些情况下,很可能你并不需要去开发高可用系统,他们更讲究效率,把正常流程码出来基本就完事了。
讲究点可用性的,稍微把代码写好点,服务器配置堆高点,业务流程设计上注意点,基本也能规避大部分祭天性的问题。
等你公司真的发展成 BAT 那种了,是真正拼刀工的时候,万分之一概率的异常场景可能就会让系统天天宕机,账户天天少钱。那时候各种方案、架构、分析都要拿到桌面上来了。
所以,面向失败编程诚然很难,但不代表你必须得天天面对着失败抓耳挠腮,**你需要评估你所负责的系统在成本、效率、健壮性上应做怎样的取舍**
![](https://img2022.cnblogs.com/blog/1997761/202204/1997761-20220422101501383-874386105.png)
>转载链接:[https://www.cnblogs.com/linvanda/p/16172767.html](https://www.cnblogs.com/linvanda/p/16172767.html),整理:沉默王二
---
title: (二)工作三年的一些感悟
shortTitle: (二)工作三年的一些感悟
author: Craftsman
category:
- 博客园
---
## 前言
你的未来时刻因你现在的努力而改变!
在努力不够的时候,永远不要谈天赋!
  工作三年,时不时会和身边的朋友、同学,谈论开发这个岗位的前途性,总觉得事多且累,学不完的新技术。
  A同学:放弃了一个更高薪的工作,去了一个外企“躺平”,不怎么加班,几乎大家都是到点就走。
  B同学:在互联网工作两年后,开始想要去考公务员,也确实去考了,但是好像没考上。
  其实,不管在哪里,都需要努力。没有那个行业是不卷的,而你要做的,是今天的自己比昨天更厉害!当然,如果你就想安于现状,选择自己喜欢的生活方式,可以忽略本篇。
## 一、个人能力
  首先,提升自己是永远不变的硬道理,自己不提升,永远没有人能帮你!引用《西游记》中的故事:当孙悟空还是一只猴的时候,他只能和一群猴玩耍;当他从菩提祖师那里学艺归来的时候,他可以和牛魔王称兄道弟;大闹天宫前,众神都瞧不起他,叫他弼马温;大闹天宫后,众神都叫他齐天大圣;取经前,他对菩萨只能顶礼膜拜;取经后,他成了斗战胜佛,与菩萨平起平坐。
(1)【知识储备】工科,计算机专业的相关知识一定要熟练掌握。Java语言,《数据结构与算法》,《操作系统》,《计算机网络》,《微积分》,《计算机组成原理》,《计算机系统结构》等等。
(2)【坚持阅读】工作中,需要对企业级架构有所了解以对应日常开发。阅读书单:
  《阿里巴巴Java开发手册》
  《大话数据结构》
  《大话设计模式》
  《深入理解Java虚拟机》
  《Java并发编程的艺术》
  《剑指Offer》
  《高性能Mysql》
  ……
  以上并未罗列玩完。当然,也不是要完全读完后,才能开发。而是一边开发,一边阅读,脚踏实地,仰望星空。
  除了技术书以外,本人也推荐以下书单:
  《中国哲学简史》:了解中国社会发展的概况,掌握思考问题的方法论。
  《西游记》:可以让你了解社会是如何运作的?深刻的理解社会规则与政策的制定以及人性的冷漠。
  《天龙八部》:虽然是一部小说,但小说中体现的"侠之大者,为国为民"以及"儿女情长"也值得读者深深体会。
  《人性的弱点》:了解别人在想什么,更好的与他人相处。
  《三国演义》:理解社会中的"套路",与"反套路",与"反反套路"。
  《先秦诸子·百家讲坛》:人性善恶?德治法治?有为无为?一场持续三百年的大辩论,没有答案,却影响了中华民族几千年。
  ……
  请务必读原著,而不是仅仅在电视剧。
(3)【读书笔记】坚持写读书笔记,有道云,印象等等都可以。将自己的体会写下来,温故而知新。
(4)【善于总结】学习中,或者是工作遇到的困难,或者不理解的地方,多总结,多思考,多与人交流。无论是工作还生活中,凡事都多问三个"为什么"。
## 二、个人管理
(1)【坚定信心】很多人都会有一种想法,我先干什么,如果不行,大不了回家,基本都是子承父业(当然你家有矿,当我没说)。当然,如果能把父业发扬光大,也是可以的。
只是,如果选择了一条路,就请坚定自己的信心。切忌,厨师干不好,就去理发,理发干不好,就去电焊。除非你的时间和金钱,能承担这些试错成本。
(2)【自律生活】坚持健身与跑步。随着年龄的增加,有的不仅是对未来未知的恐惧,也来自于身体。身体是革命的本钱,不要熬夜,不要熬夜,不要熬夜。年轻时可能你不会有太大的感觉,随着年龄的增加,不要在后面,身体积累各种毛病。上医院花钱,就是无底洞。
(3)【减少社交】尽量不参加无效的社交,下班后,不和同事喝酒,泡吧。人的时间与精力都是有限的,多专注于对自己有价值,有意义的人与事。
(4)【学会理财】养成存钱的习惯,切记,切记,切记。学会不同的投资,不把所有的鸡蛋都放在一个篮子里,它可以让你在未来的路中多一份选择。
(5)【杜绝焦虑】前段时间,与一个女性朋友(非IT行业)聊天。她提出,女性在社会活动中,比男性付出的更多,也比男性受到更多的不公平待遇,有工作,生活,家庭,父母年龄大等多方面。其实她提出的,我都承认!
  然后她问了我一个问题,说现在好多企业招人都说要年龄35以下的,那35以上的人去哪里了?
  我当时惊了一下,心想,35不是我们IT行业的梗吗?怎么你们也有吗?然后我回答了她:其实社会中,90%的工作,随着年龄的增加,都会变的不适合你干。比如,公司的前台小姐姐,基本都是18~25,形象好的,我从来没见过30以上的前台小姐姐。她们的工作可替代性太强了(没有职业歧视,只是想举例说明每个行业都卷)。
  一样的,毕业时,我是这样,三年之后,我不能还是这样。如果你毕业时企业校招给15w,三年之后,还做着同样的工作,自身没有多少提升,那,请思考一个问题:企业为什么不同样花15w招一个校招生呢?你和校招生的区别在哪里呢?今年我读一年级,那明年我就要读二年级,我不能还在读一年级。
  我承认女性在职场中比男性机会少,有不公平待遇。要兼顾家庭和工作,但是要知道,其他的女性也和你一样。她们同样面临生产,工作,父母的压力,而在竞争中,我们要做的就是打败同龄人。
  试想一下,如果现在让你做一年级的题目,你会焦虑吗?不会,因为你都会。你之所以焦虑,就是因为快考试了,你还没有预习。一次又一次,自然,每次你的成绩都不好看。不同的是,以前有老师家长盯着你学习,现在没有了。
  解决焦虑的唯一办法,就是提升自己。
## 三、团队管理
(1)【团队定位】求同存异:学会接纳不同类型的人,奋斗型、知足型、创新型、领导型、稳定型,为每个成员找到自己的定位。扬长避短,使团队效率更高,效益最大化。
(2)【注重沟通】主动沟通:平时多一起吃饭,多一起聊生活,快速了解成员是一个怎样的人。针对其个性的管理,使其快速融入团队。
(3)【做事方法】换位思考:对事不对人,无论是产品还是测试,提需求或是提bug,都先排查出问题,再回复!事后复盘,记录事件过程,然后邮件给领导说明。
(4)【结果导向】关注结果:不做无效加班,不内卷加班。只要在指定的时间,保质保量的完成工作量,就不在意过程如何。
(5)【价值认可】给与肯定:多给与成员价值认可,多肯定成员的付出。
## 四、项目管理
(1)【需求评估】弄清楚要做什么很重要。不然竹篮打水一场空。
(2)【技术选型】一般来说,公司技术选型都是确定的。要熟练掌握相关技术知识点以及工具的使用。
(3)【合理排期】结合不同的成员,技术能力,性格等,合理分配工作量与功能点。保质保量,按时提测,不压缩测试时间。如遇延期风险,提前反馈,尽快解决。
(4)【打破阻塞】木桶效应:在协同开发时,A需要调用B,B需要调用C时。不能阻塞团队整体开发进度。
(5)【核心原则】不接受中途改需求,加需求等,尽量排在下一个版本;如遇紧急需求,必须邮件各个leader,重新评估,不得私自接需求;版本控制在每个月最多两次。
(6)【总结复盘】对每一次的版本迭代,开团队例会,做总结复盘。有则改之无则加勉。
## 五、职业规划
(1)【职业规划】展望未来,自己想得到什么,想成为一个什么样的人,要达到一个什么样的目标。然后制定长期和短期目标,并坚持一个一个小目标的实现。
(2)【保持初心】永远不要忘了最初为什么上路。懂得取舍,得到什么,失去什么。选择 大于 努力。
(3)【放下执念】更专注于"事",而不是"钱"。也不要执念于35岁。
(4)【突破瓶颈】保持一颗持续学习的心,提升自我。量变引起质变。
(5)【发展副业】这一点来说,恐怕有点远。当自己差不多能实现财富自由以后,投资一些自己喜欢的副业。比如:投资朋友的奶茶店、火锅店之类的。前期自己还没有原始的资本积累(存钱的意义)时,不要做这一条。毕竟市场有风险,投资需谨慎。
## 最后
  【享受孤独】人生而孤独,这世间所有的人都只是过客。所有的关系,到最后不过是相识一场。没有任何一个人会陪你一辈子,学会独处,享受独自一人沉浸在知识的海洋的感觉。
  随着时间的推移,不能别人都在涨薪涨职位,就你在涨年龄涨体重。
  努力,是为了让自己多一份选择;努力,是为了让自己不被抛弃。才能有说走就走的能力与勇气。希望各位男孩子在该吃苦的年纪,好好赚钱,不要老想着情情爱爱。慢慢等,成功后,该来的,都会来。
  最后引用一句《百年孤独》里的话:生命中所有的灿烂,终究需要用寂寞来偿还。
  谨以此篇,献给不甘于平庸的你,与君共勉!
>转载链接:[https://www.cnblogs.com/originator/p/15588705.html](https://www.cnblogs.com/originator/p/15588705.html),整理:沉默王二
---
title: 平时的工作如何体现一个人的技术深度?
shortTitle: 平时的工作如何体现一个人的技术深度?
author: 沧海月明FE
category:
- 博客园
---
今天在公司内网看到一个讨论帖,原文如下:
> **平时的工作如何体现一个人的技术深度?**
>
> 平时工作中很多时候需求细而碎的,如何在工作中积累技术深度?又如何体现一个人的技术深度?
# 思考:做需求与做需求的差异
再回答问题之前,我想先抛开「技术深度」这次词,讲讲做需求这件事,说说我对做需求的理解。每一个程序员都是从刚毕业做需求开始,为什么有的人逐渐成为大牛,主导大型技术项目或走向团队管理岗位,而有的人一直还在做需求。我觉得这里面的差异在于,每一个对做需求这件事的理解有所不同。
这里面的差异在于,你是抱着一种什么样的心态去完成这个需求,是把这个需求做到极致,还是只是当做任务完成这个需求,达到产品想要的功能就行。这两个表面上看似差不多其实相差极大,差异在于,你有没有站在更高的角度,希望这件事做到完美。从需求角度有没有思考产品设计当中的缺陷,能不能反向为产品设计提供建议,从技术角度能不能做到高质量高兼容性无bug,以及下次再有类似的需求能不能快速高效率的迭代。
用一句话来描述就是,**能不能跳出自己是一个程序员是一个被动执行人的角色,而是将自己当做产品当做技术负责人的心态去做这件事**
# 业务需求该怎么做
知易行难,如果一开始做不到,那就先着眼小事,关注细节,从需求开始的需求评审,编写技术方案文档,设计文档,到开发的代码注释,结构设计,保证高质量,完善无漏洞的代码逻辑,再到异常埋点,指标监控,线上可用性运维等等,认真对待一个需求的每一个环节。
当你自认为已经做好整个流程的每一件小事之后,接下来可以 **通过深入细节,思考整个流程是否存在问题**。做需求过程中沟通协作的有没有问题,流程规范的有没有问题,机制环节哪方面问题,代码公共基础能力的是否有缺失,开发过程中你所遇到的问题是不是一个通用问题,能不能抽象出一个公共库解决大家的问题,能不能制定一个SOP的解决方案流程,亦或是提炼出一个最佳实践在组内外分享经验。
通过这些一件件小事来锻炼自己解决问题的能力,以及更深层级的发现问题的能力。再通过不断的发现问题,思考问题出现的原因,拿出解决方案,最终落地解决了自己或组内或协作方的问题,锻炼自己的综合能力逐步慢慢成长。
# 再说「技术深度」
说了这么多,你可能会说,这跟我问的技术深度有什么关系?我想说:**抛开业务需求谈技术深度都是耍流氓**
举一个例子,数据可视化方面3D three.js,视频直播方面的编解码压缩,客户端安全方面的攻防渗透,每一个都是有技术深度的事情,但问题是即使你掌握了这些领域拥有了非常高的技术深度之后呢,不能应用于业务需求,不能解决产品急迫要解决的问题,不能完成你老板的OKR,达成部门的战略目标,还是英雄无用武之地(当然你也可以选择一个可以用得上的团队,那是就是另外一回事了)。
由于这些单点的有技术深度的事情,不能为你带来直观和显而易见的**「回报」**(也就是颜如玉 黄金屋与金榜题名),也就间接的打击了积极性(当然自己对某门技术感兴趣而钻研的不再本次讨论之列)。所以提升自己的技术深度,最好的方式还是在公司业务中,发现有深度的事,然后去在攻克这个问题的过程中,提升了自己的技术深度,即跟随公司业务的发展的同时自身也获得了成长,**你用技术能力为公司解决了业务发展过程中的问题,自然也就从公司获得了该有的回报**。这是一个ROI投入产出比最高的获得技术深度的方式。
# 获取做有深度事情的授权
当想明白获取技术深度的路径之后,接下来要解决的就是,**如何让领导给你安排有技术深度的事情?**
业务发展中有很多有技术深度有技术难度的事情,为什么领导要安排你来做这件事呢?你凭什么让领导觉得你**「有能力」****「有意愿」**来完成这件事?能力与意愿是作为领导在分配工作当中的最重要的两个决策项(有机会的话我会再写一篇,从管理视角是如何做分工的)。既然你能提问如何积累技术深度,我相信你一定是有强烈意愿的,那么剩下的就是如何让领导认为你有完成这个有技术深度的事情的能力?关于这个问题,可以参考我之前写的一篇回答:[如何管理leader对你的能力预期?](https://www.cnblogs.com/xxcanghai/p/ru-he-guan-lileader-dui-ni-de-neng-li-yu-qi.html) 最简单来讲就是我在前面讲的,你能不能在开发需求中做到深度思考,追求极致,精益求精,有责任心,有主人翁意识与主R意识,在每件小事中能做到**「自闭环」**,之后才会逐步让你承担更大范围更高挑战更大深度的事情,形成正向循环。
这也是我前面为什么要先重点强调做好每一件小事的重要性。
# 技术深度不是唯一标准
作为一个程序员,在职业生涯的初期,确实是以技术深度也就是技术能力作为最大的衡量标准。但随着职业生涯的发展,职级从L5到L8,站在从公司角度,对一个人的需求,也会从能完成一个业务需求,变成能带领一帮人完成一个更大的维度的需求,能不能为组织解决问题,为事业部达成战略目标,对人的要求的重心也会慢慢发生变化,这种变化可以参考公司的职级能力模型体系的雷达图。
所以一味的追求积累技术深度就跑偏了,但不是说技术深度不重要,技术能力是作为程序员的安身立命之本,但是在积累技术深度的同时,也需要学习锻炼技术深度以外的能力。具体到底是什么其他能力,这就够再展开好几篇文章的篇幅了,今天在这就不细说了,有机会可以谈谈我对这方面的理解。
# 最后
故不积跬步无以至千里,不积小流无以成江海。先从做好每一件小事开始,把每个业务需求做到120分,深度思考,发现问题,解决问题,逐步建立起靠谱有责任心技术牛的人设,逐步负责有技术难度的事情,跟随公司业务发展积累自己的业务领域经验与技术深度,从而获得双赢的回报。
这是我对如何积累技术深度这件事的理解,或许会有一些片面和偏激,**毕竟不是谁都有一个能知人善任的好领导,不是谁都能遇到一个快速发展的业务,不是谁都能遇到有技术难度与技术挑战的场景**,无论我怎么说,都会有幸存者偏差的存在。
努力与机遇并存,机遇可遇不可求,所以我们能做的事,就是学会正确做事的思路和方法,并为之坚持不懈的践行它。知易行难,学会方法容易,坚持践行难于上青天。自己该做的都做好了,机遇来了就可以抓住,即使抓不住,**你也有了「选择的能力」,有了选择更好机遇更好公司的能力**
以上均为个人主观且片面的看法,欢迎批评讨论~~。
>转载链接:[https://www.cnblogs.com/xxcanghai/p/ping-shi-de-gong-zuo-ru-he-ti-xian-yi-ge-ren-de-ji.html](https://www.cnblogs.com/xxcanghai/p/ping-shi-de-gong-zuo-ru-he-ti-xian-yi-ge-ren-de-ji.html),整理:沉默王二
此差异已折叠。
此差异已折叠。
此差异已折叠。
---
title: 1024 写给程序员的一些建议
shortTitle: 1024 写给程序员的一些建议
author: —阿辉
category:
- 博客园
---
首先祝大家节日快乐,身体健康、快快乐乐、开心编码。
今天是个属于程序员的日子,刚好和夫人来图书馆看书,有时间整理整理自己最近的思绪,找找写文的感觉。
好久没有认真坐下来写文章了,很难进入心流状态。
> 发现自己还是输给了自己的懒散与拖延。其实备忘录里面有好多要写的灵感,后面会安排时间都分享出来。
1024是一个神奇又具有特殊含义的数字,有很多讲究。如果要细说可能互联网上的很多东西,都可以通过这个1024推演产生。
在这一行也有将近4年之久,虽说都是在小公司干活,可对从事这一行的感悟也越来越多,很多时间就会觉得自己其实不适合做程序开发,又有时候觉得自己好像只能做这个。
下面是最近的一些思考,从工作习惯,成长经历,职业发展等方向进行阐述,是自己从业这几年来的一些小总结,也希望能对大家有帮助。
## 一、进入心流的状态
进入心流的状态,现在的我们太难进入心流了。
其实很大一部分原因是手机对我们的控制越来越大。
不知道大家有没有这样一种错觉,好像我们随时随地的都在找手机,不管我们去干什么,都必须拿着手机。好像手机不在手上或者口袋里,总觉得缺少点什么?
昨天上班(**工作单休**)手机被我遗忘在家里,一整天都没有带手机。刚开始的时候,总感觉有人会给我打电话,发消息。甚至刚到公司我都有想法,让夫人帮忙发个朋友圈说下本人今天没带手机,请大家别联系我。最后想了想还是算了,毕竟自己不是什么大忙人,没必要那么搞。
晚上回去,看了看手机,一整天也没有人联系我(**自己想多了**)。
说说昨天的感觉:
* 心里面空落落的,总觉得少点什么
* 工作的时候很专注
* 工作效率明显提高
* 中午午休睡了30分钟
以前带手机的时候,上班期间很难让自己进入状态,时不时的就会看下手机:看时间、登录微信、QQ,进知识星球,刷微博热搜。
不管有没有人联系我,@我,给我发消息,反正我都要进去看看,生怕自己错过什么大事情。
没带手机后,工作效率提高了,午休的时候不看视频,午休睡眠质量很好。
一整天很容易进入心流状态(专注、不分心、持续时间长),写代码思路也很清晰。
* * *
通过昨天的事情,让我在工作和生活上有了一些感悟,在这里和大家分享下。
在平时的工作和生活中,要让自己多多进入心流,减少外界事物对自己的干扰。尤其是从事编码工作的朋友,进入心流状态,能让自己静下心来,思考问题的深度就会加深,会让自己沉浸在一种状态下,一种持续精进的状态中。
所以对于即将踏上工作岗位的朋友们,在这里我希望大家能渐渐培养自己的心流状态,看看自己到底在什么样的环境,什么场景下能快速进入状态,持续的进行内容创作。
> 记住,这是一种本领,也是我们在工作和生活中要及时学会的技能
## 二、让自己拥有随时离开的本领
做技术到底能干多久?程序员是吃青春饭的吗?程序员经常加班压力大,容易秃顶?等等等等,关于程序员有很多很多的问题,最近居然还有一个脑残剧<程序员那么可爱>也上映了。
随着互联网的发展,程序员这个行业慢慢进入大众化,基本上每个行业都会或多或少和程序员打过交道、不管是业务合作还是朋友间交谈,大家可能都觉得程序员干不长久,但是有一点大家都有共鸣。就是挣得多。基本刚毕业就能拿到别的行业4~5年后的薪资。
确实,这个不可否认。这个行业相比较别的传统行业确实薪资差距特别大。尤其是今年的大厂校招,基本都是倒挂(太羡慕了)。
可是难度、压力也不是那么轻松。面试时,对计算机知识、对算法、对数据结构、对项目经验的考察都是特别严格的。尤其是大厂,特别关注这个。
这个行业你不得不随时都准备学习,可以说是把终身学习理念贯彻最彻底的行业。
是的,我们要让自己拥有随时离开的本领,时刻精进自己的技能点,别工作4~5年后让新技术把自己淘汰掉。
这个行业技术的更新换代是巨快的,很难一招吃遍天下。你要时刻更新技能,时常出去面试,看看自己在市面上的价格(很难相信吧,是不是觉得就好像商品一样)。
不管是在大公司还是小公司,都别做表面的螺丝钉。要让自己具有核心竞争力和不可替代性,也要有随时离开的本领。
想拥有随时离开的本领,那么你就得从以下几点做起:
* 精进技能
* 掌握核心技术
* 在公司别只做业务实现者,要多想想做这个能否对自己有所提高
* 平时多思考、多问些为什么
* 别在温水里面泡太久
* 要向前看,有时候就得加速跑
这方面自己做的很不足,所在公司业务太单一,和市场脱轨过于严重。平时也没有在社区跟着做开源项目。最近开始扩展技能树,希望自己好好努力,在把平时基本工作做好的前提下,多多复盘总结,学习上进。
## 三、年轻时别把钱看的太重
刚毕业从事工作的时候,什么都想要。
看着同事结婚、买房、买车,自己好生羡慕。
也曾想过结婚、想过买房、想过买车,可有心无力。
可是随着年龄的渐长你会发现这些东西,都会慢慢的跟着时间一点一滴的来到你的身边,有时候都不用你去着急,它就不管不顾的来了。
年轻的时候别把钱看的太重。
对于钱财,我们对待它的心态要好,别功利心太重,要有得之我命失之我幸的态度,要记住刚开始先提升自己的能力,这才是至关重要的。
当你的能力和技术到达一定高度后,这些都会自然而然的到来。
## 四、劳逸结合注意休息
这个行业要经常加班,这是所有人对于程序员的认知。
刚进入这行的时候,在学校大家经常探讨关于程序员加班到天亮的事情,还有加班猝死的新闻,说的事情特别多。
可是毕业从事编码工作后,发现干着干着就没心劲了。
* 长时间面对电脑会导致头晕目眩(**和CF打多了吐是一样的道理**)
* 长时间高强度精力集中会导致身体机能出现问题
* 经常和程序打交道和人交流的机会越来越少(**木讷,没情趣**)
* 需要经常业余时间学习新技术
* 居然还有单休的企业(**千万别去单休的公司,除非钱到位**)
* 钱确实很多但休息时间很少
这里我想说的是大家要学会劳逸结合,别一头扎进工作中。要学会工作,让自己有所提高。
如果感到累了该请假休息就休息,别觉得公司离开你就会破产,想多了。
程序员很多的,老板随时都能招一个替代掉你。
如果刚毕业能去大公司就去大公司,能去大城市就去大城市,刚毕业的那几年是你人生职业发展的黄金时间,耽误不得。
还有别去单休的企业,要有自己的时间提高技能和安排生活。
## 五、懂得责任的重要性
要让自己有责任心、在其位谋其政。
如果你还在岗位上,就要尽自己最大努力干好自己的工作,精进技术满足老板的要求及时完成工作内容。
责任心不管是在工作中还是在平时的与人交流相处中,都至关重要。
作为男人,更要有责任心。
你要记得你将来会成为一个家庭的支柱,你得挑起自己肩膀上应有的责任和义务,你得对你的夫人、孩子有所交代。
你得让他们快乐幸福。
* * *
* * *
* * *
朋友你好,我是阿辉,很高兴认识你。
一个长期在小公司上班的程序员。使用C#开发设计过网站、通过Android开发过手机APP,会一点前端技术,励志做一个终身学习者。如果你想了解关于小公司程序员的日常生活,欢迎加我微信,我们一起进步、交流学习。
>转载链接:[https://www.cnblogs.com/netxiaohui/p/15468786.html](https://www.cnblogs.com/netxiaohui/p/15468786.html),整理:沉默王二
---
title: 阿里二面:为什么要分库分表?
shortTitle: 阿里二面:为什么要分库分表?
description: 在高并发系统当中,分库分表是必不可少的技术手段之一,同时也是BAT等大厂面试时,经常考的热门考题。
author: 苏三呀
category:
- 微信公众号
head:
- - meta
- name: description
content: 在高并发系统当中,分库分表是必不可少的技术手段之一,同时也是BAT等大厂面试时,经常考的热门考题。
---
## 前言
在高并发系统当中,分库分表是必不可少的技术手段之一,同时也是BAT等大厂面试时,经常考的热门考题。
你知道我们为什么要做分库分表吗?
这个问题要从两条线说起:`垂直方向``水平方向`
## 1 垂直方向
`垂直方向`主要针对的是`业务`,下面聊聊业务的发展跟分库分表有什么关系。
### 1.1 单库
在系统初期,业务功能相对来说比较简单,系统模块较少。
为了快速满足迭代需求,减少一些不必要的依赖。更重要的是减少系统的复杂度,保证开发速度,我们通常会使用`单库`来保存数据。
系统初期的数据库架构如下:
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-alemwsyyfkfb-965dbc52-1fad-4cf7-a6fc-f04ff445101f.jpg)
此时,使用的数据库方案是:`一个数据库`包含`多张业务表`。用户读数据请求和写数据请求,都是操作的同一个数据库。
### 1.2 分表
系统上线之后,随着业务的发展,不断的添加新功能。导致单表中的字段越来越多,开始变得有点不太好维护了。
一个用户表就包含了几十甚至上百个字段,管理起来有点混乱。
这时候该怎么办呢?
答:`分表`
`用户表`拆分为:`用户基本信息表``用户扩展表`
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-alemwsyyfkfb-32cbe5e1-b88b-4125-b990-8f901467fbb2.jpg)
用户基本信息表中存的是用户最主要的信息,比如:用户名、密码、别名、手机号、邮箱、年龄、性别等核心数据。
这些信息跟用户息息相关,查询的频次非常高。
而用户扩展表中存的是用户的扩展信息,比如:所属单位、户口所在地、所在城市等等,非核心数据。
这些信息只有在特定的业务场景才需要查询,而绝大数业务场景是不需要的。
所以通过分表把核心数据和非核心数据分开,让表的结构更清晰,职责更单一,更便于维护。
除了按实际业务分表之外,我们还有一个常用的分表原则是:把调用频次高的放在一张表,调用频次低的放在另一张表。
有个非常经典的例子就是:订单表和订单详情表。
### 1.3 分库
不知不觉,系统已经上线了一年多的时间了。经历了N个迭代的需求开发,功能已经非常完善。
系统功能完善,意味着系统各种关联关系,错综复杂。
此时,如果不赶快梳理业务逻辑,后面会带来很多隐藏问题,会把自己坑死。
这就需要按业务功能,划分不同领域了。把相同领域的表放到同一个数据库,不同领域的表,放在另外的数据库。
具体拆分过程如下:
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-alemwsyyfkfb-9cab165b-f2e0-4c80-9bd1-cc272a78429c.jpg)
将用户、产品、物流、订单相关的表,从原来一个数据库中,拆分成单独的用户库、产品库、物流库和订单库,一共四个数据库。
> 在这里为了看起来更直观,每个库我只画了一张表,实际场景可能有多张表。
这样按领域拆分之后,每个领域只用关注自己相关的表,职责更单一了,一下子变得更好维护了。
### 1.4 分库分表
有时候按业务,只分库,或者只分表是不够的。比如:有些财务系统,需要按月份和年份汇总,所有用户的资金。
这就需要做:`分库分表`了。
每年都有个单独的数据库,每个数据库中,都有12张表,每张表存储一个月的用户资金数据。
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-alemwsyyfkfb-38434e50-9dce-467f-a036-e75038d9f3f6.jpg)
这样分库分表之后,就能非常高效的查询出某个用户每个月,或者每年的资金了。
此外,还有些比较特殊的需求,比如需要按照地域分库,比如:华中、华北、华南等区,每个区都有一个单独的数据库。
甚至有些游戏平台,按接入的游戏厂商来做分库分表。
## 2 水平方向
`水分方向`主要针对的是`数据`,下面聊聊数据跟分库分表又有什么关系。
### 2.1 单库
在系统初期,由于用户非常少,所以系统并发量很小。并且存在表中的数据量也非常少。
这时的数据库架构如下:
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-alemwsyyfkfb-965dbc52-1fad-4cf7-a6fc-f04ff445101f.jpg)
此时,使用的数据库方案同样是:`一个master数据库`包含`多张业务表`
用户读数据请求和写数据请求,都是操作的同一个数据库,该方案比较适合于并发量很低的业务场景。
### 2.2 主从读写分离
系统上线一段时间后,用户数量增加了。
此时,你会发现用户的请求当中,读数据的请求占据了大部分,真正写数据的请求占比很少。
众所周知,`数据库连接是有限的`,它是非常宝贵的资源。而每次数据库的读或写请求,都需要占用至少一个数据库连接。
如果写数据请求需要的数据库连接,被读数据请求占用完了,不就写不了数据了?
这样问题就严重了。
为了解决该问题,我们需要把`读库``写库`分开。
于是,就出现了主从读写分离架构:
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-alemwsyyfkfb-e56c2d22-9aa2-4502-8d2a-f4416ce20db9.jpg)
考虑刚开始用户量还没那么大,选择的是`一主一从`的架构,也就是常说的一个master一个slave。
所有的写数据请求,都指向主库。一旦主库写完数据之后,立马异步同步给从库。这样所有的读数据请求,就能及时从从库中获取到数据了(除非网络有延迟)。
读写分离方案可以解决上面提到的单节点问题,相对于单库的方案,能够更好的保证系统的稳定性。
因为如果主库挂了,可以升级从库为主库,将所有读写请求都指向新主库,系统又能正常运行了。
> 读写分离方案其实也是分库的一种,它相对于为数据做了备份,它已经成为了系统初期的首先方案。
但这里有个问题就是:如果用户量确实有些大,如果master挂了,升级slave为master,将所有读写请求都指向新master。
但此时,如果这个新master根本扛不住所有的读写请求,该怎么办?
这就需要`一主多从`的架构了:
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-alemwsyyfkfb-1fc49e99-76ac-4aea-8c3d-e438de2cdd1c.jpg)
上图中我列的是`一主两从`,如果master挂了,可以选择从库1或从库2中的一个,升级为新master。假如我们在这里升级从库1为新master,则原来的从库2就变成了新master的的slave了。
调整之后的架构图如下:
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-alemwsyyfkfb-bbf8495d-2338-4bcf-b8e3-cebaea4fac29.jpg)
这样就能解决上面的问题了。
除此之外,如果查询请求量再增大,我们还可以将架构升级为一主三从、一主四从...一主N从等。
### 2.3 分库
上面的读写分离方案确实可以解决读请求大于写请求时,导致master节点扛不住的问题。但如果某个领域,比如:用户库。如果注册用户的请求量非常大,即写请求本身的请求量就很大,一个master库根本无法承受住这么大的压力。
这时该怎么办呢?
答:建立多个用户库。
用户库的拆分过程如下:
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-alemwsyyfkfb-f2354590-0161-45e7-afb7-4ad07a3763fb.jpg)
在这里我将用户库拆分成了三个库(真实场景不一定是这样的),每个库的表结构是一模一样的,只有存储的数据不一样。
### 2.4 分表
用户请求量上来了,带来的势必是数据量的成本上升。即使做了分库,但有可能单个库,比如:用户库,出现了5000万的数据。
根据经验值,单表的数据量应该尽量控制在1000万以内,性能是最佳的。如果有几千万级的数据量,用单表来存,性能会变得很差。
如果数据量太大了,需要建立的索引也会很大,从小到大检索一次数据,会非常耗时,而且非常消耗cpu资源。
这时该怎么办呢?
答:`分表`,这样可以控制每张表的数据量,和索引大小。
表拆分过程如下:
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-alemwsyyfkfb-8830dc2f-b088-4190-bc9a-a19bf55de99b.jpg)
我在这里将用户库中的用户表,拆分成了四张表(真实场景不一定是这样的),每张表的表结构是一模一样的,只是存储的数据不一样。
如果以后用户数据量越来越大,只需再多分几张用户表即可。
### 2.5 分库分表
当系统发展到一定的阶段,用户并发量大,而且需要存储的数据量也很多。这时该怎么办呢?
答:需要做`分库分表`
如下图所示:
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-alemwsyyfkfb-1cfacfbf-283a-4415-91da-c0c2616c2f0a.jpg)
图中将用户库拆分成了三个库,每个库都包含了四张用户表。
如果有用户请求过来的时候,先根据用户id路由到其中一个用户库,然后再定位到某张表。
路由的算法挺多的:
* `根据id取模`,比如:id=7,有4张表,则7%4=3,模为3,路由到用户表3。
* `给id指定一个区间范围`,比如:id的值是0-10万,则数据存在用户表0,id的值是10-20万,则数据存在用户表1。
* `一致性hash算法`
这篇文章就不过多介绍了,后面会有文章专门介绍这些路由算法的。
## 3 真实案例
接下来,废话不多说,给大家分享三个我参与过的分库分表项目经历,给有需要的朋友一个参考。
### 3.1 分库
我之前待过一家公司,我们团队是做游戏运营的,我们公司提供平台,游戏厂商接入我们平台,推广他们的游戏。
游戏玩家通过我们平台登录,成功之后跳转到游戏厂商的指定游戏页面,该玩家就能正常玩游戏了,还可以充值游戏币。
这就需要建立我们的账号体系和游戏厂商的账号的映射关系,游戏玩家通过登录我们平台的游戏账号,成功之后转换成游戏厂商自己平台的账号。
这里有两个问题:
1. 每个游戏厂商的接入方式可能都不一样,账号体系映射关系也有差异。
2. 用户都从我们平台登录,成功之后跳转到游戏厂商的游戏页面。当时有N个游戏厂商接入了,活跃的游戏玩家比较多,登录接口的并发量不容小觑。
为了解决这两个问题,我们当时采用的方案是:`分库`。即针对每一个游戏都单独建一个数据库,数据库中的表结构允许存在差异。
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-alemwsyyfkfb-f9f55b53-0465-48d1-9715-241129fe7833.jpg)
我们当时没有进一步分表,是因为当时考虑每种游戏的用户量,还没到大到离谱的地步。不像王者荣耀这种现象级的游戏,有上亿的玩家。
其中有个比较关键的地方是:登录接口中需要传入游戏id字段,通过该字段,系统就知道要操作哪个库,因为库名中就包含了游戏id的信息。
### 3.2 分表
还是在那家游戏平台公司,我们还有另外一个业务就是:`金钻会员`
说白了就是打造了一套跟游戏相关的会员体系,为了保持用户的活跃度,开通会员有很多福利,比如:送游戏币、充值有折扣、积分兑换、抽奖、专属客服等等。
在这套会员体系当中,有个非常重要的功能就是:`积分`
用户有很多种途径可以获取积分,比如:签到、充值、玩游戏、抽奖、推广、参加活动等等。
积分用什么用途呢?
1. 退换实物礼物
2. 兑换游戏币
3. 抽奖
说了这么多,其实就是想说,一个用户一天当中,获取积分或消费积分都可能有很多次,那么,一个用户一天就可能会产生几十条记录。
如果用户多了的话,积分相关的数据量其实挺惊人的。
我们当时考虑了,水平方向的数据量可能会很大,但是用户并发量并不大,不像登录接口那样。
所以采用的方案是:`分表`
当时使用一个积分数据库就够了,但是分了128张表。然后根据用户id,进行hash除以128取模。
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-alemwsyyfkfb-884b58a4-3009-4056-904d-cd235db41aa6.jpg)
> 需要特别注意的是,分表的数量最好是2的幂次方,方便以后扩容。
### 3.3 分库分表
后来我去了一家从事餐饮软件开发的公司。这个公司有个特点是在每天的中午和晚上的就餐高峰期,用户的并发量很大。
用户吃饭前需要通过我们系统点餐,然后下单,然后结账。当时点餐和下单的并发量挺大的。
餐厅可能会有很多人,每个人都可能下多个订单。这样就会导致用户的并发量高,并且数据量也很大。
所以,综合考虑了一下,当时我们采用的技术方案是:`分库分表`
经过调研之后,觉得使用了当当网开源的基于jdbc的中间件框架:`sharding-jdbc`
当时分了4个库,每个库有32张表。
![](http://cdn.tobebetterjavaer.com/tobebetterjavaer/images/nice-article/weixin-alemwsyyfkfb-ed3d9006-7dd5-4f90-a566-8874020df4ea.jpg)
## 4 总结
上面主要从:垂直和水平,两个方向介绍了我们的系统为什么要分库分表。
说实话垂直方向(即业务方向)更简单。
在水平方向(即数据方向)上,`分库``分表`的作用,其实是有区别的,不能混为一谈。
* `分库`:是为了解决数据库连接资源不足问题,和磁盘IO的性能瓶颈问题。
* `分表`:是为了解决单表数据量太大,sql语句查询数据时,即使走了索引也非常耗时问题。此外还可以解决消耗cpu资源问题。
* `分库分表`:可以解决 数据库连接资源不足、磁盘IO的性能瓶颈、检索数据耗时 和 消耗cpu资源等问题。
如果在有些业务场景中,用户并发量很大,但是需要保存的数据量很少,这时可以只分库,不分表。
如果在有些业务场景中,用户并发量不大,但是需要保存的数量很多,这时可以只分表,不分库。
如果在有些业务场景中,用户并发量大,并且需要保存的数量也很多时,可以分库分表。
>转载链接:[https://mp.weixin.qq.com/s/klkD8xea0gQ96Mh1Q1MHLw](https://mp.weixin.qq.com/s/klkD8xea0gQ96Mh1Q1MHLw),出处:macrozheng,整理:沉默王二
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册