Sunskey

日拱一卒,不期而至

0%

55 跳跃游戏

题目描述

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标。

说明:

  • 1 <= nums.length <= 3 * 104
  • 0 <= nums[i] <= 105

示例1:

1
2
3
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

示例2:

1
2
3
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。

思路

比较当前的 i + num[i]maxLength 如果相等并且 maxLength== i 则说明永远到不了。如果 maxLength >= nums.length - 1 说明可以到达,返回true即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public boolean canJump(int[] nums) {
if(nums == null || nums.length < 2){
return true;
}
int maxLength = 0;
for(int i = 0; i < nums.length; i++){
int currentMax = i + nums[i];
if(currentMax == maxLength && maxLength == i){
return false;
}
maxLength = Math.max(maxLength, currentMax);
if(maxLength >= nums.length -1){
return true;
}
}

return true;
}

83 删除链表的重复数组

题目描述

存在一个按升序排列的链表,给你这个链表的头节点 head ,请删除所有重复的元素,使每个元素 只出现一次

说明:

  • 链表中节点数目在范围 [0, 300]
  • -100 <= Node.val <= 100
  • 题目数据保证链表已经按升序排列

示例1:

1
2
输入:head = [1,1,2]
输出:[1,2]

示例2:

1
2
输入:head = [1,1,2,3,3]
输出:[1,2,3]

思路

一次遍历,比较node结点和node.next,如果相同,则直接指向node指向node.next.next即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
public ListNode deleteDuplicates(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode current = head;
while( current != null ){
while (current.next != null && current.val == current.next.val){
current.next = current.next.next;
}
current = current.next;
}
return head;
}

39 组合总数

题目描述

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates中的数字可以无限制重复被选取。

说明:

  • 所有数字(包括 target)都是正整数。
  • 解集不能包含重复的组合。

示例1:

1
2
3
4
5
6
输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]

示例2:

1
2
3
4
5
6
7
输入:candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]

思路

​ 分治,把target 分解成 target - num[i]的问题,进行dfs 深度优先搜索即可。可以先进行排序,方便后续的处理。可以把每次选中的元素记录下来,进行累加得到sum,最终与target进行比较。如果 sum >= target ,则停止。

错误解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class CombinationSum {

private List<List<Integer>> result = new ArrayList<>();

public List<List<Integer>> combinationSum(int[] candidates, int target) {
if (candidates == null || candidates.length == 0) {
return result;
}
Arrays.sort(candidates);
if (candidates[0] > target) {
return result;
}
dfs(0, target, candidates, new ArrayList<>());
return result;
}


public void dfs(int sum, int target, int[] candidates, List<Integer> currentList) {
if (sum > target) {
return;
}
if (sum == target) {
result.add(new ArrayList<>(currentList));
return;
}
for (int i = 0; i < candidates.length; i++) {
int temp = candidates[i];
currentList.add(temp);
dfs(sum + temp, target, candidates, currentList);
currentList.remove(currentList.size() - 1);
}
}
}

采用上述代码执行后,发现存在重复集的情况

原因如果选中2,则可形成[2,2,3]的组合,如果第一次选中3,则可形成[3,2,2]。很容易看到,进行向下dfs的时候,2的分支所有能到达target的组合都在选中2已经选中。所以在选3的分支,剔除2,继续从3的分支开始选即可。因此,我们dfs层面加入一个begin,表示从begin开始选即可。

正确解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class CombinationSum {

private List<List<Integer>> result = new ArrayList<>();

public List<List<Integer>> combinationSum(int[] candidates, int target) {
if (candidates == null || candidates.length == 0) {
return result;
}
Arrays.sort(candidates);
if (candidates[0] > target) {
return result;
}
dfs(0, target, candidates, new ArrayList<>(), 0);
return result;
}


public void dfs(int sum, int target, int[] candidates, List<Integer> currentList, int begin) {
if (sum == target) {
result.add(new ArrayList<>(currentList));
return;
}
for (int i = begin; i < candidates.length; i++) {
int temp = candidates[i];
if (temp + sum > target) {
break;
}
currentList.add(temp);
dfs(sum + temp, target, candidates, currentList, i);
currentList.remove(currentList.size() - 1);
}
}

结果

关于未来房价的思考

前言

伴随着前两年的股市大涨,上证指数从2800点涨到了3600点。虽然自己亲身经历了这样的大浪潮,但是心中仍旧有种种的不安。正如段永平大佬所说:“利用常识进行投资”。我的常识告诉我:“在没有任何业绩增长的前提下,股市这样的暴涨是不符合常理的”。例如:贵州茅台现在的市值已经3.09万亿,动态市盈率6,3.09万亿已经是部分省份的年度GDP的总和。即使是考虑未来2-3年稳定的业绩增长,估值仍然偏高。不仅仅是茅台,很多股票的股价已经到达了历史最高。那2021年投资什么便成了我需要思考的事情。房地产便成为了我的备选投资对象之一。

房价与政府、个人的关系

  • 政府

卖地目前作为绝大多数地方政府的主要财政收入,财政收入与城市的发展、未来规划息息相关。财政收入的上涨会促进地方政府的基础设施建设(医疗、交通、学校),随着基建的完善,往往会伴随着社会生产力的提高,从而促进城市员工的福利、工资的上涨,从而导致消费、生产效率的提高,进一步导致财政收入的上涨,整个过程是一个正和游戏。因此,房价将会很大一定影响未来城市的发展方向和政策。

  • 个人

根据《2018 全球财富报告显示》,中国家庭总财富已超过50万亿,紧跟美国之后,位居全球第二。然而在中国家庭资产中,房产的占比过高。广发银行和西南财大在调查了国内7大区域,23个城市近万户家庭后,发布了《中国城市家庭财富健康报告》。该报告显示,中国家庭的财富管理都处于一个“亚健康”的状态,平均财富得分仅有68.5,有将近 80% 的资产都是房子,资产配比严重不合理,房价的涨跌与家庭财富息息相关。

房价的趋势

房价对政府、个人的影响如何之大?那未来的趋势是什么呢?

我个人推测是核心城市的核心地段会涨、非核心城市的房价会逐步下跌。原因如下:

  • 优质的房产属于优质的资产

房子终究是属于商品,只要是属于商品就一定会符合市场规律,存在高估、合理、低谷的三种情况。即使是现在价格存在价值偏移,只要有交易和市场,终有一天,价格会回归价值,而一切的驱动力就是供求关系。正常的市场中,商品的价格跌了,人们就会增加购买;商品的价格涨了,人们就会减少购买。而股市具有适反性, 股票的价格跌了,人们就会疯狂的抛售,股票的价格进一步下跌。股票价格上涨,人们反而进一步购买,导致股价上升。房子也是如此。价格上涨,人们担心价格进一步上涨,从而增加购买意愿。当房价下跌的时候,人们反而会减少购买意愿。这也就是为什么网上我们我会看见诸如“怎么看未来房价”等问题。我们从来不会问“苹果涨价该怎么办?”、“蔬菜涨价了怎么办?”。因为它们涨价之后,我们可以选择少吃或者选择其他的替代品即可。而房子和股票则相反,我们把它们视为资产,资产的最重要的任务就是给我们带来收益,带来的收益越大,造成的适反性则越强。

优质的房产犹如优质的股票一样,很难以低价买到。由于马太效应,往往会存在一定的溢价。我们只能尽可能的以一个合理的价格买到。房产和人都是符合金字塔模型。而优质的房产是有限的,随着社会生产力的提高,人们的购买力会增加,那么优质房产的房价也增长的。例如: 优质的房产的占比是10%, 那我只要让10%的人买的起就可以。那会不会一直涨?答案其实很好回答,只要精英人群的收入增加,那么优质房产的房价一定会增长,让房价的增幅速度和精英人群的收入增长一致即可

  • 城镇化率

城市的核心竞争力其实是人,而城镇化率则一项关键指标。城镇化率的提高,往往会导致生产力的提高,促进城市的经济发展,从而衍生出对房地产的需求。目前,以发达国家作为参照,发达国家已经基本完成城市化,城市化率大多在75%以上。截止2016年末,德国75.5%,法国79.8%,美国81.8%,加拿大82%,英国82.8%,日本93.9%。而截止到2019年末,我国平均的城镇化率仅为60%,其中,上海城镇化率最高达88.30%,北京、天津排名第二和第三,城镇化率分别为86.60%、83.48%。此外,广东、江苏、浙江城镇化率超70%,分别为71.40%、70.61%、70.00%。贵州、云南、甘肃、西藏城镇化率不足50%。我国的城镇化率仍然有一定的上涨的空间,进而会导致核心城市的房价上涨。

  • 人口老龄化

房子终究是商品,价格会随着供求关系的变动而变动。如果伴随着需求的下降,那么房价下跌也是迟早的事情。人口老龄化的原因主要有2方面:低生育率和寿命的增加。

自1840年以来,由于人民生活的富裕和现代医疗的双重作用下,人类的平均寿命由40岁增长到70多岁,寿命几乎翻倍。据国家统计局报告显示,中国人的平均寿命也从1949年的35岁增长到2018年的77岁。

img

img

中国65 岁及以上人口占比走势预测(单位:%)

上图可知道,中国人口老龄化将会是未来中国面临的一个主要问题。人口老龄化带来的问题主要有3方面。

  1. 养老矛盾的加剧以及慢性病发生率和种类数量大增加。

  2. 老龄化对政府分配养老金与社会福利产生巨大的压力。

  3. 老龄化是得劳动力萎缩,造成更为缓慢的经济增长。

目前能想到解决老龄化一方面会使经济增加变慢,另一方面,购房的需求主要是青年人,随着青年人的减少,需求年减少,房价不可避免的将会呈现下跌。由于城镇化率的仍然有一定的空间,非城镇化的居民将会消耗一部门核心城市的房子。而非核心城市的房子由于需求和人口的下降,房价将会呈现下降的趋势。

  • 国家政策调控

近些年,我国政府出台了“房住不炒”、限购和加大2套房的首付比例等策略去调控房价,有效减小了一定比例以炒房的部分投资者。而优质的房产因为无论在何时存在需求的,这个是由市场决定的;同时,一个城市的优质房产也是有限的,这就会导致纵然政府有相应的调控策略,优质房产的房价依然保持稳定或者逐步上涨。而去除一些非核心城市的房地产泡沫也有助于社会的进一步的发展,解放房地产产业中资金,提高资金的利用率,促进消费和扩大生产,有助于社会的经济发展。

结论

买入优质的房产犹如买入优质的股票,价格会不以人为意志的自发性地增长。而伴随着老龄化问题、国家的政策调控以及城镇化的进一步加深,一些非核心城市的房产将会呈现下跌的趋势。引用一句话吴军博士的一句话:“科技的发展不是均匀的,而是以浪潮的形式出现。对个人来讲,看清楚浪潮,赶上浪潮,便不枉此生”。浪潮往往也寓意着危险,我们能否赶上浪潮,也源于我们对世界的认知和理解。努力提高认知,争取早日能成为购买优质资产的社会精英。

前言


某日早上, Sunskey 正喝着咖啡,悠闲的听着小曲(不存在的,苦逼写代码)。领导火急火燎的走到了我面前,咱们的加密工具 .so 出现了安全隐患,需要清除符号表,提升安全性。我一脸的懵逼 “什么是符号表“。但是还是毫无犹豫的答应了“好的”。经过简单的查阅相关资料,对符号表有了一个基本认识。

一. 符号和符号表


1.1 什么是符号表

程序编译成可执行文件后,文件中会有一个专门的表用来保存函数名,变量名,段名和代码或者数据的对应关系,这个表就是符号表(编译阶段产生的)。

ELF文件中通常会有两张符号表。一张叫符号表(.symtab) ,另一张叫动态符号表(.dynsym),一般只用对符号表(.symtab) 进行去除的工作。

ELF(Executable and Linkable Format)即可执行可链接文件格式,目前常见的Linux、 Android可执行文件、共享库(.so)、目标文件( .o)以及Core 文件(吐核)均为此格式。

符号表类型 说明
.symtab 包含大量的信息(包括全局符号global symbols)
.dynsym 只保留.symtab中的全局符号
1.2 符号表的作用

1.收集符号属性

编译程序扫描说明部分收集有关标识符的属性,并在符号表中建立符号的相应的属性信息。

1
2
例如:int A;  float B[5];
则记录的了符号A是一个整型的变量,而符号B则是一个浮点数组。

2.上下文语义的合法性检查

同一个标识符可能出现在不同的地方,需要在上下文中对标识符的属性进行一致性和合法性的检查。

1
2
例如:int A[3,5];  float A[3,5];
很明显这段代码将会出现语义上的冲突

3.作为目标代码生成阶段地址分配的依据

符号变量需要由它符号类型以及次序确定该变量存储在符号表的哪个区域。

1
例如:常见的区域有公共区(extern)、文件静态区(extern static)、函数静态区(函数中static)、还是函数运行时的动态区(auto)等。
1.3 符号表的种类
  • Global symbols(模块内部定义的全局符号) 由主模块定义并能够被其他模块引用的符号。

  • External symbols(外部定义的全局符号) 由其他模块定义并被主模块引用全局符号。

  • Local symbols(本模块的局部符号) 仅由主模块定义和引用的本地符号。

二. 怎么去除符号表


2.1 strip 命令

strip 经常用来去除目标文件(.so、.a、可执行程序等)中的一些符号表、调试符号表信息,以减小静态库、动态库和程序的大小。

使用方法:strip 文件名

通过ls -l 命令可知道原文件的大小。

通过file 命令可知,原文件是否经过stripped过,也就是strip 处理过的。

通过nm 命令,可查看原文件的符号表。

strip命令用法:https://www.linuxidc.com/Linux/2011-05/35773.htm

2.2 链接阶段使用-s 和 -S参数

-s:用于去除所有符号信息

-S:用于去除调试符号信息

也可以直接在GCC中通过 -Wl,-s 和-Wl,-S来移除符号信息。

三 . 总结


符号表基本只在编译阶段和链接阶段有用,移除符号表能够减少程序的大小也能提高一定的安全性能,但是移除符号表之后没办法调试,程序出了问题后,给定位问题带来了一定的难度。可以根据自己的实际情况,去选择是否需要去除符号表。

参考文献


《文化、现代化、价值投资与中国的读书笔记》

现代化

现代化的本质就是现代科技与自由市场经济的结合,使得人类经济进入到一个持续复合的一个状态。进行社会分工和交换的时候,最终创造出来的价值反而更高,也就是常说的1+1>2。

现代化在西方产生的原因

英国、美国能诞生现代文明,得益于历史原因,他们的政府时有限政府,为商人服务,而中国从汉朝以后,就不可能存在这样的政府了,而且中国自古以来就是重农抑商.

全球化

未来会形成一个全球的、统一的、共同的、以自由贸易、自由交换、自由市场经济为主要特性的全球经济体系。

工业化的三个阶段
  • 工业化早期

在工业化早期,农村的剩余价值劳动人口不断被吸引到城市工业中,但是随着工业发展到一定的规模之后,农村剩余劳动人口从过剩到短缺,这个拐点被成为刘易斯拐点。这个阶段资本具有绝对的掌控力,劳工很难讨价还价的能力,企业自然就会剥削工人。

  • 刘易斯拐点的成熟阶段

进入到经济发展的成熟阶段后,因为劳动人已经开始短缺、经济发展会导致工资水平已经开始短缺,经济发展会导致工资水平不断上升,工资上升又引起消费水平上升、储蓄水平和投资水平也会上升,这样公司的利润也会上升。这个阶段被成为黄金时代。

  • 刘易斯拐点被追赶的阶段

工资增长到一定的水平后,在海外其他新兴经济就会变的更有吸引力。这个阶段社会的各个阶层已经不再均衡。劳工需要自己生存。社会的总体工资水平会停滞不前,国内的投资机会大大减少,这一阶段后被成为刘易斯拐点后的被追赶的阶段。

政府在三个时期的职能

在早期工业化中,政府的财政政策会发挥巨大的作用,投资基础设施、资源、出口相关等服务又助于帮助国家迅速进入工业化状态。

进入成熟阶段,依靠财政政策的进一步投资就会开始和私营部门的投资相互冲突、互相竞争资源。而这一时期、货币政策更能调动给私营部门的积极性,促进经济的发展。

被追赶阶段,由于私营部门不愿意投资国内,此时政府的财政政策可以以基础设施等促使国内的经济的发展。

文化与文明

真知

人生的意义在于真知,人生的意义就是获得真知,并以此让个人、社会、世界变得更加富足、公平、进步、美好、唯一可靠的知识就是用科学的方法获取知识。所有的邪恶源于缺乏社会的认知。

文化与文明的区分

文化用于区不同地区、不同人群之间的区别,而文明则是用于描述人类发展的共性,并区别人类与动物的祖先。

通过生物(DNA)和文化基因两种方式进化,因此人类进化的速度远远大于其他生物。

中西方文化的差异

地理位置一方面决定的一个地区的自然禀赋,另一方面也决定了它和最先进的文明交流的机会。由此造成了各个地区发展的差异。中国的两河流域以及西面背靠喜马拉雅,北方广阔的大平原,有助于维护农业文明时代的稳定发展和统一。而西方由于多条河流以及广阔的森林不利于形成稳定、和谐的统一社会。

文明的发展的三个时代
  1. 采集狩猎文明1.0

    大约7万年前发生了一次巨大的飞跃,人类走出非洲并遍布全世界,人类先祖表现出其他动物完全不具备的智慧、想象力、创造力和进去心,虽然生活方式上并没有比在非洲是发生巨大的变化。但是生活方式没有发生太大改变,不过人口数量迅速增长并覆盖全球,濒临灭绝的机会大大降低,为下一次文明奠定了基础。

  2. 农业文明 2.0

    公元前9600年,地球变暖给人类带来的礼物,农业文明出现在所谓的幸运维度带上,中国在两千年之后出现在黄河长江流域。中国由于陆地的整体统一、“科举制”、以及两大河流的因素使得中国在农业文化上到达了顶峰。

    “科举制”提供了一种政治平等,使得人才的效能得到最大的发挥,更有助于国家的稳定和可持续发展。

  3. 现代文明 3.0

    18世纪,欧洲发现美洲后,促进了大西洋经济的发展,催生出自由经济市场。同时,由于工业革命发生,使得科技得到长足的进步,从而导致现代科技和市场经济相结合产生经济无限累进增长。

“经济贤能制”是这一阶段最为伟大的最伟大创新制度。无论出身如何,都有了经济层面上的自由上升的通道,可以通过努力飞蝗腾达。这种体系有助于释放个人和小集体的潜力。

文化进化与生物进化的关系

生物进化:大脑是我们几亿年自然选择形成的应对社会的反映机制,大部分的行为是大脑条件反射的结果,而这种结果自然选择常常会误导我们、也让我们看不清世界和自我。

文化进化:人可以通过强化或弱化赏罚机制你、有意识的切断一些从“感觉”到“思维模块”再到“行动”的传导机制,也就是将自然所设计的大脑重新设计一遍。

“真、善、美” 只有指出了人类生存状况的真相、在这个真相上提出来的道德主张就更可能形成道德基础,也就是“善”,有了真和善之后,才会有对于美的重新的认识。

人身上大约有七、八分的动物性,两分人性,再加上半分神性。文化进化的意义在于让人凸显人性、扩大神性。

中国未来发展

经济发展趋势

中国未来几十年在经济上最核心的将是从政府主导的市场经济转变为以政府为辅导的全面自由市场经济。内需、服务将占GDP主要部分。

文化发展趋势

科学技术的发展事是一个不断积累、循序渐进的过程、需要广泛、长期的合作。如果没有诚实作为基础,很难建立起这样一个信用合作体系。在政府的强力推动下,”六伦“可能会成为中国未来的文化发展核心理念之一。

五伦,指的是古代中国的五种人伦关系和言行准则。

即古人所谓君臣、父子、兄弟、夫妇、朋友关系,用忠、孝、悌、忍、善则为五种关系准则。

六伦则是除无论外对陌生人施以”诚实“的关系准则。

政治发展趋势

与科技文明相适应的现代政治需要多一些道家底色,与民休养让利,轻罚轻放,并兼顾个人与集体的利益。

尊重人性当然也必须尊重人性中所有的自私、懦弱、不完美、市场经济和科技创新恰恰给了所有不完美的个人无穷的空间和意义。对利润的追求、自私甚至贪婪也是市场经济蓬勃向上的动力,懦弱和懒惰成为科技探索及普及提供了丰富的土壤,攀比、炫耀甚至也是消费增长的一部分。

中国未来的巨大潜力
  • 人才

到2020年,中国预计总共会有近2亿大学生,以及接近整个美国的工作人口。受过高等教育的人口持续增加,再加上科研上的持续的高投入,会进一步加深科技创新和市场经济相互作用的结果。

  • 城镇化

目前,发达国家的城市化率都在70%左右。而目前,中国的城镇化率只有55%。一旦进一步城镇化,就会刺激消费,结果就是可持续的经济的增长。

而中国高居不下的45%的储蓄率可进一步支持消费和投资的资源。

价值投资

投资的核心是对未来的预测,投资某个国家的企业确实需要对这个国家本身有一个基本认知。

投资人的两个原则
  1. 把对真知、智慧的追求当作自己的道德责任,要有意识杜绝一切屁股决定脑袋的理论。
  2. 有真正建立起受托人责任。把投资者的钱当作自己的钱,去取得最大化的收益。
投资投资的四个概念
  1. 购买的股票是购买公司所有权的一部分。
  2. 理解市场,市场的存在就是发现人性的弱点。
  3. 安全边际,投资不可能100%正确。
  4. 通过自己的努力、建立起自己的”能力圈“。
投资和投机的区别

对投资者而言,如果投资的公司遇到一个可以累进、持续增长的经济,它的利润和投资回报会持续增长。

对于投机者而言,如果只是猜测他人在短期之内的买卖行为,其实只是一种零和游戏。

指数投资: 基本上就是全部投资者和投机者的总和,如果投机者最终的结果归零,那么指数投资的结果实际上就是投资者的净结果。

芒格成功的四个因素

1、独立 2、相对客观 3、果断并且耐心 4、兴趣

能力圈的建立
  1. 清楚自己知道什么
  2. 清楚自己不知道什么
  3. 清楚自己不需要知道什么
  4. 意识到自己总有事情不知道
股市的适反性

事实上,短期内股票的价格与经典经济学中的商品价格规律通常正好想相反。在经典经济学中、商品价格上升、大家就买的少些,商品大甩卖,买的人就会多些。而股市中,则是股价上涨越多,大家反而会去买,上涨越快买的人越多;股价下跌,大家就会卖出,熊市越厉害,大家也越厉害。

摘录

查理·芒格:“去有鱼的地方补鱼。”捕鱼的第二条规则: “千万别忘记第一条”。

形容师恩:“夫子之墙数仞,不得其门而入,不见宗庙之美,百官之富”。

莫里斯:“历史,就是懒惰、贪婪、又充满恐惧的人类,在寻求让生活更容易、安全、有效的方式时创造的,而人类对此毫无意识”。

”硅材料的智能计算能力,按照目前这个进度发展,就可以和人脑的计算能力相当 ,甚至赶超大脑。这样人脑,就可以和机械的大脑里面的所有的存量、记忆、DNA输入机器上、用这种形式延长大脑的寿命“。

“人类就本性而言情感上追求结果平等,理性上追求机会平等。对结果平等的追求使得人类文明的任何进步都会最终传播到地球上的每一个角落;建立了提供机会平等制度的社会都会繁荣进步、长久治安“。

赌性:人类从本性而言都想走捷径,不劳而获,通过付出最少方式获得最多的利益,并甘愿冒险,也就是所谓的牺牲,这也就是为什么赌博在历史上几乎所有时期一直都存在的原因。

常识:对于事情的判断不受别人赞成或者反对的影响,而纯粹基于你的逻辑和证据。

查理·芒格:“除非我能驳倒那些最聪明和我持有相反观点的人,否则我不敢声称自己有一个观点”。

中过文明的灵魂其实就是士大夫文明,士大夫的机制观所体现的就是一个如何提高自我修养,自我超越的过程。

《大学》曰:正心,修身,齐家,治国,平天下。

自1900年以来,全球平均寿命已增加已一倍多,达到70多岁,人类100多年的时间里,人类寿命的延长幅度比之前10万多年里人类在全部历史中的增长幅度还要大。

有一次我问诺曼·利尔:“你感觉自己多大年纪?” 他答:“我永远和与我对话者同龄”。年龄最好的解答。

MySQL主备高可用

MySQL是如何保证主备一致的

建议把节点B设置成read only 状态

  1. 有时候一些运营的查询语句会被放到备库上去查,设置只读可以防止误操作。

  2. 防止切换逻辑有bug,比如切换过程中出现双写,造成主备不一致。

  3. 可以用read only,来判断节点的角色。

binlog备份执行过程

  1. 在备库 B 上通过 change master 命令,设置主库 A 的 IP、端口、用户名、密码,以及要从哪个位置开始请求 binlog,这个位置包含文件名和日志偏移量。

  2. 在备库 B 上执行 start slave 命令,这时候备库会启动两个线程,就是图中的 io_thread 和 sql_thread。其中 io_thread 负责与主库建立连接。

  3. 主库 A 校验完用户名、密码后,开始按照备库 B 传过来的位置,从本地读取 binlog,发给 B。

  4. 备库 B 拿到 binlog 后,写到本地文件,称为中转日志(relay log)。

  5. sql_thread 读取中转日志,解析出日志里的命令,并执行。

binlog三种格式的对比
  1. statement:记录的是具体的语句。可能会因去主备不一致。

  2. row:记录的是逻辑上具体的行为。日志的更加的详细。

  3. mixed:由于采用row会记录具体逻辑上的日志,所以占用的空间比statement更大。MySQl会自己判断这条语句是否可能引起主备不一致,如果有可能,就用statemnt。

使用row格式的好处在于,更方便恢复数据

循环复制问题

双M架构和M-S结构,节点A和节点B之间总是互为主备。

业务逻辑上在节点A上更新一条语句后,把生成的binlog发送给节点B,节点B执行完也会生成binlog,这样节点A又会去执行节点B生成的binlog。从而造成循环执行,怎么解决?

  1. 规定两个库的server id必须不同,如果相同,则它们之间不能设定为主备关系。
  2. 一个备库接到到binlog并在重放的过程中,生成与原binlog的server id相同的新的bingo.
  3. 每个库收到从自己主库发送过来的日志后,先判断server id,如果跟自己相同,表示这个日志是自己生成的,则直接丢弃这个日志。
线上数据库设置成“非双1”的场景。
  1. 业务高峰期。

  2. 备库延迟,为了备库尽快赶上主库。

  3. 用备份恢复主库的副本,应用binlog的过程。

  4. 批量导入数据的时候。

MySQL怎么保证高可用

“同步延迟”
  1. 主库A执行完成一个事务,写入binlog,我们把这个时刻记为T1。

  2. 之后传给备库B,我们把备库接受的这个binlog称为T2。

  3. 备库B执行完成这个事务,把这个时间成为T3。

所谓的主备延迟,就是在同一个事务中,在备库完成的时间和主库执行完时间之间的差值,也就是T3 - T1。

可以在show slave status,seconds_behind_master用于表示当前备库延迟了多少秒。

主备延迟的主要原因是备库消费日志的速度(relay log),比主库生产的binlog的速度慢的多。

主备延迟的来源
  1. 有些部署条件下,备库的所有机器性能要比主库所在的机器性能差。
  2. 备库的压力大了。例如:一些后台分析的语句,只能在备库上跑,不能影响正常的业务。

解决方案:

  • 一主多从。除了备库外,可以多接几个从库,让这些从库来分担读的压力。
  • 通过binlog输出到外部系统。比如Hadoop这类系统,让外部提供统计和查询的能力。

3.大事务。例如:不要用delete一次性删除太多的数据。大表的DDL,建议采用gh-ost方案。

4.备库的并行复制能力过低。

可靠性优先策略

1.判断备库B上的seconds_behind_master,如果小于某个值继续下一秒。否则重试

2.把主库A改成只读状态,即把readonly设置为true

3.判断备库B的seconds_behind_master,直到这个值变为0

4.把备库改写成可读写状态,把readonly设置为false;

5.把业务请求切到备库B

这个切换流程,一般是由专门的 HA 系统来完成的,我们暂时称之为可靠性优先流程。

可用性有限策略

把步骤4,5调整到最开始执行,不等主备数据同步,直接把连接切到备库B,并且让备库B可以读写,那么系统几乎没有不可用时间了。但是可能会产生数据不一致的情况。

例如:可用性策略,且binlog_format=mixed时的切换流程和数据结果。

  1. 步骤 2 中,主库 A 执行完 insert 语句,插入了一行数据(4,4),之后开始进行主备切换。

  2. 步骤 3 中,由于主备之间有 5 秒的延迟,所以备库 B 还没来得及应用“插入 c=4”这个中转日志,就开始接收客户端“插入 c=5”的命令。

  3. 步骤 4 中,备库 B 插入了一行数据(4,5),并且把这个 binlog 发给主库 A。

  4. 步骤 5 中,备库 B 执行“插入 c=4”这个中转日志,插入了一行数据(5,4)。而直接在备库 B 执行的“插入 c=5”这个语句,传到主库 A,就插入了一行新数据(5,5)。

可用性优先策略binlog_format=row时下的结果。

备库的B的(5,4)和主库A(5,5)这两行数据均不会被对方执行。

  1. 使用row格式的binlog时,数据不一致的问题更容易被发现。

  2. 主备切换的可用性优先策略会导致数据不一致。建议针对大多数应用来说,数据可靠性一般还是要优于可用性的。

MySQL的可用性时依赖主备延迟的,延迟时间越小,在出库故障的时候,服务恢复需要的时间就越短,可用性就越高。

备库延迟过高怎么解决

如果备库执行日志的速度持续低于主库生成日志的速度,那这个延迟就有可能成了小时级别。而且对于一个压力持续比较高的主库来说,备库很可能永远都追不上主库的节奏。

如果图中sql_thread 更新数据DATA是采用单线程,就会导致备库应用日志不够快,造成主备延迟。

5.6版本前,MySQL只支持单线程,由此在主库并发高,TPS高时就会出现严重的主备延迟问题。

多线程模型

图中的coordinator就是原来的sql_thread模型,只负责读取中转和分发事务。work线程的个数由slave_parallel_workers决定,建议设置在8~16之间最好。

coordinator在分发的时候,需要满足以下两个基本的要求

1.不能造成更新覆盖。这要求 更新同一行的事务,必须被分发到同一个worker中。

2.同一个事务不能被拆开,必须放到同一个worker中。

按表分发策略

每个事务在分发的时候,跟所有worker的冲突关系包括以下三种情况:

1.如果跟所有worker都不冲突,coornaditor线程就会把这个事务分配给最空闲的worker。

2.如果跟多于一个worker冲突,coordinator进入等待状态,直到和这个事务存在冲突关系的worker只剩一个。

3.如果只跟一个worker冲突,coordinator线程就会把这个事务分配给由冲突关系的worker。

缺点:如果碰到热点表更新的时候,所有的事务都会被分配同一个worker,就会变成单线程复制。

按行分发策略

如果两个事务没有更新相同的行,它们在备库上可以并行执行。显然这个模式binlog必须要求row。

按行复制和按表复制的数据结构查不多,为每一个worker,分配一个hash表,只要是按行分发,这时候的key,就必须是“库名+表明+唯一键的值”。

相比按表并行分发策略,按行并行策略在决定线程分发的时候,需要消耗更多的计算资源,同时还存在一些约束条件。

  1. 要能够从binlog里面解析出表名、主键值和唯一索引的值。binlog必须为row。

  2. 表必须由主键。

  3. 不能有外键。表上如果有外键,级联更新的行不会记录在binlog中,这样冲突检测就不准确。

缺点:1、耗费内存。2、耗费cpu。

MySQL5.6版本的并行复制策略

用于决定分发策略的hash表里,key就是数据库名。

优势: 1、构建hash的值时候跟快。 2、不要求binlog格式。

如果主库上有多个DB,并且各个DB的压力均衡,使用这个策略的效果会很好。

MariaDB的并行策略

Maria 并行复制 利用了redo log组提交的两个特性。

1.能够在同一组提交的事务,一定不会修改同一行。

2.主库上可以并行执行的事务,备库上也一定可以并行执行的。

在实现上,MariaDB是这么做的

1.一组提交事务,里面有一个相同的commit_id,下一组就是commit_id+1;

2.commit_id 直接写到binlog里面。

3.传到备库应用的时候,相同commit_id的事务分发到多个worker执行。

4.这一组全部执行完成后,coordinator再去去下一批。

缺点:要等第一组事务完全执行后,第二组事务才能开始执行,这样系统的吞吐量不够。这个方案很容易被大事务拖后腿。

MySQL 5.7的并行复制策略

1.配置DATABASE,表示使用MySQL5.6版本的按库执行

2.配置LOGICAL_CLOCK,提供了类似MariaDB的策略。

并行复制策略的思路:

1.同时处于prepare状态的事务,在备库执行时可以并行的。

2.处于prepare状态的事务,与处于commit状态的事务之间,在备库执行时也是可以并行的。

可以增加binlog_group_commit_sync_delay和binlog_group_commit_sync_no_delay_count参数,让主库提交慢一些,从而达到提升备库复制并发度的目的。

MySQL 5.7.22的并行复制

MySQL新增了一个新的并行度复制策略,基于WRITESET的并行复制。

相应地,新增binlog-transaction-dependency-tracking,来控制是否启用。

1.COMMIT_ORDER,同时进入prapare和commit来判断是否可以并行的策略。

2.WRITESET,表示的是对于事务涉及更新的每一行,计算出这一行的 hash 值,组成集合 writeset。如果两个事务没有操作相同的行,也就是说它们的 writeset 没有交集,就可以并行。

3.WRITESET_SESSION,是在 WRITESET 的基础上多了一个约束,即在主库上同一个线程先后执行的两个事务,在备库执行的时候,要保证相同的先后顺序。

hash值是通过“库名+表名+索引值+值”计算出来的,如果一个这个表上还有除主键索引外,那么对于每个唯一索引,insert语句对应的hash的值。

1.writeset是在主库生成后,直接写入,在备库执行的时候,节省了很多计算量。

2.不需要把整个事务的binlog都扫一遍才能决定分发到哪个worker,更省内存。

3.由于备库的分发策略不以来于binlog内容,所以binlog是statement格式也可以的。

主库出问题了,从库怎么办?

一主多从的结构

其中A和A’互为主备,从库B、C、D指向的是主库A。

相比一主一备,一主多从结构在切换完成后,A’会成为新的主库,从库B、C、D也要改接到A’,所以主备切换的复杂性也相应增加了。

当我们把节点B设置成节点A’的从库的时候,需要执行一条change master命令:

![image-20201208164303574](/Users/dxm/Library/Application Support/typora-user-images/image-20201208164303574.png)

关键在于如何设置从主库的master_log_name文件以及位置。

一种取同步位点的方法

1.等待新主库A’把中转日志(relay log)全部同步完成

2.在A’上执行show master status,得到当前A’最新的File和Position

3.取原主库A故障的时刻T

4.用mysqlbinlog工具解析A‘的File,得到T时刻的位点。

但是有可能存在主键冲突等错误。通常,在切换任务的时候,要先主动跳过这些错误。

1.主动跳过一个事务。set global sql_slave_skip_counter=1;每次碰到这些错误 ,执行一次跳过命令,直到不再出现停下来的情况。

2.通过设置slave_skip_errors参数,直接设置跳过制定的错误,等主备的同步关系建立后,并稳定执行一段时间后,把这个参数设置为空,以免真的出现主从数据不一致,也跳过了。

  • 1062 错误插入数据时唯一键冲突。
  • 1032 错误是删除数据时找不行。
GTID

GTID的全称是GTID 的全称是 Global Transaction Identifier,也就是全局事务 ID,是一个事务在提交的时候生成的,是这个事务的唯一标识。它由两部分组成,

格式是:GTID=server_uuid:gno

其中:

server_uuid是一个实例第一次启动时自动生成的,是一个全局唯一的值。

gno是一个整数,初始值是1,每次提交事务的时候分配这个事务,并加1。

事务id(transaction id)是在事务执行过程中分配的,如果这个事务回滚了,事务id也会增加,而gno是在事务提交的时候才会分配。

在GTID模式下,每一个事务都会跟一个GTID一一对应。

MySQL 扩展知识点

Grant和权限

Grant 语句会同时修改数据表和内存,判断权限的时候使用的内存数据,因此,规范的使用grant和revoke 语句,是不需要随后加上flush privileges。

flush privileges语句本身会用数据库的数据重建一份内存权限,所以在权限数据可能存在不一致的情况下再使用。而这种不一致往往是由于直接用DML语句操作系统权限导致的

My SQL系统自增id

表定义自增值id

表定义的自增值达到上限后的逻辑是:再申请下一个id时,得到的值不变。

InnoDB 系统自增 row_id

写入表row_id时从0开始到2^48-1。达到上限后,下一个值就是 0,然后继续循环。

Xid

MySQL内部维护了一个全局变量global_query_id,每次执行语句的时候将它赋值给Query_id,然后给这个变量加1。如果当前语句时这个事务执行的第一条语句,那么MySQL还会同时把Query_id赋值给这个事务的Xid。

MySQL重启后会生成新的binlog文件,这就保证了,同一个binlog文件,Xid一定时唯一的。达到上限后,就继续从0开始计数。

global_query_id定义的长度是8个字节,这个自增值的上限是2^64-1。

Innodb trx_id

Xid是由server层维护,InnoDB内部使用Xid,就是为了能够再InnoDB事务和server之间做关联。InnoDB 自己的 trx_id,是另外维护的。

InnoDB 内部维护了一个 max_trx_id 全局变量,每次需要申请一个新的 trx_id 时,就获得 max_trx_id 的当前值,然后并将 max_trx_id 加 1。

InnoDB 数据可见性的核心思想是:每一行数据都记录了更新它的 trx_id,当一个事务读到一行数据的时候,判断这个数据是否可见的方法,就是通过事务的一致性视图与这行数据的 trx_id 做对比。

thread_id

系统保存了一个全局变量thread_id_counter,每新建一个连接,就将thread_id_counter赋值给这个新连接的线程变量。thread_id_counter 定义的大小是 4 个字节,因此达到 232-1 后,它就会重置为 0,然后继续增加。

分区表的使用

MyISAM分区表使用的策略,称为通用策略,每次访问分区都由server层控制。通用分区策略,是MySQL一开始支持分区表就存在的,在文件管理、表管理的实现上很粗糙,因此有比较严重的性能问题。

MySQL5.7.9 ,InnoDB引擎引入了本地分区策略.InnoDB内部管理分区的行为。

1.MySQL在第一次打开分区表的时候,需要访问所有的分区;

2.在server层,认为这是同一张表,因此所有分区公用同一个MDL锁。

3.在引擎层,认为这是不同的表,因此MDL锁之后的执行过程,会根据分区表规则,只访问必要的分区。

Insert语句为什么加那么多锁

Insert….select 是很常见的在两个表之间拷贝数据的方法。在可重复读隔离级别下,这个语句会给select的表扫描到的记录和间隙加读锁。

如果insert和select的对象是同一个表,则有可能会造成循环写入,需要引入用户临时表来优化。

insert语句出现唯一键冲突,会在冲突的唯一值上加共享的next-key lock(S锁)。因此,要尽快提交或者回滚事务,避免枷锁时间过长。

自增主键为什么不是连续的

自增值的存储

表的自增值是保存在表结构定义里,表的结构定义放在后缀名为.frm的文件中,但是并不会保存自增值。

不同的引擎对于自增值的保存策略不同。

  • MyISAM引擎保存在数据文件中。
  • InnoDB引擎自增值,其实保存在了内存里。5.7版本以前会去寻找最大的max(id),放入内存中。而5.8以后则是记录在了redo log中。

自增值修改机制

  1. 如果插入数据时id字段指定为0、null或者未指定值、把就这个表当前的AUTO_INCREMENT值填到自增字段;
  2. 如果插入数据id字段指定了具体的值,就直接使用语句里指定的值。

假设,某次要插入的值是X,当前的自增值是Y。

  1. 如果X<Y,那么这个表的自增值不变;

  2. 如果X≥Y,需要把当前自增值修改为新的自增值。

新的自增值生成算法是:从auto_increment_offset 开始,以auto_increment_increment为步长,直到着到第一个大于X的值,作为新的自增值。

自增值的修改时机

自增数据的是在真正执行插入数据的操作之前。

自增主键id不连续的原因

1.唯一键冲突

2.事务的回滚

3.批量插入

InnoDB语句执行失败也不回退自增id。所以才只保证了自增id是递增的,但不保证是连续的。

自增锁优化

自增锁id并不是一个事务锁,而是每次申请完就马上释放。

对于批量插入数据的语句,MySQL有一个批量申请自增id的策略

1.语句执行过程中,第一次申请自增id,会分配1个;

2.1个用完以后,这个语句第二次申请自增id,会分配2个;

3.2个用完以后,这个语句第三次申请自增id,会分配4个;

4.依次类推,同一个语句去申请自增id,每次申请到的自增id个数都是上一次的两倍。

在生产上,尤其是有insert…select这种批量插入数据的场景时,从并发插入数据的角度考虑,建议设置

innodb_autoinc_lock_mode=2 ,并且 binlog_format=row.

MySQL 快速复制一张表

1.使用mysqldump方法,将命令数据导成一组INSERT语句。

mysqldump -h$host -P$port -u$user –add-locks=0 –no-create-info –single-transaction –set-gtid-purged=OFF db1 t –where=”a>900” –result-file=/client_tmp/t.sql

  1. –single-transaction 的作用是,在导出数据的时候不需要对表db1.t加锁,而是使用START TRANSACTION WITH CONSISTENT SNAPSHOT 的方法;
  2. –add-locks 设置为 0,表示在输出的文件结果里,不增加” LOCK TABLES t WRITE;” ;
  3. –no-create-info 的意思是,不需要导出表结构;
  4. –set-gtid-purged=off 表示的是,不输出跟 GTID 相关的信息;
  5. –result-file 指定了输出文件的路径,其中 client 表示生成的文件是在客户端机器上。

之后执行mysql -h127.0.0.1 -P13000 -uroot db2 -e “source /client_tmp/t.sql”

2.导出csv文件

另一种方法是直接将结果导出成.csv文件。

select * from db1.t where a>900 into outfile ‘/server_tmp/t.csv’;

  1. 这条语句会将结果保存在服务端。

  2. into_outfile指定了文件的生成位置(/server_tmp/),这个位置必须受参数secure_file_priv的限制。

  3. 这条命令不会帮助覆盖文件,因此确保文件不存在,存在则会报错。

  4. 这条命令生成的文本文件中,原则上一个数据行对应文本的一行。

得到.csv 导出文件后,你就可以用下面的 load data 命令将数据导入到目标表 db2.t 中.

load data infile ‘/server_tmp/t.csv’ into table db2.t

load data 命令有两种用法

  1. 不加“local”,是读取服务端的文件,这个文件必须自secure_file_priv指定的目录或者子目录下
  2. 加上“local”,读取的是客户端的文件,只要mysql客户端有访问这个文件的权限即可。

select …into outfile 方法不会生成表结构文件,单独导出需要使用如下命令。

mysqldump -h$host -P$port -uuser —single-transaction –set-gtid-purged=OFF db1 t –where=”a>900” –tab=$secure_file_priv

3.物理拷贝方法

MySQL5.6版本引入了可传输表空间的方法,可以通过导出+导入表空间的方式,实现物理拷贝表的功能。

  1. 执行 create table r like t,创建一个相同表结构的空表。

  2. 执行alter table discard table space,这时候r.ibd文件会被删除。

  3. 执行flush table t for export, 这时候db1目录下会生成一个t.cfg文件。

  4. 在db1目录下执行cp t.cfg r.cfg; cp t.ibd r.ibd;这两个命令

  5. 执行unlock tables,这时候t.cfg文件会被删除;

  6. 执行alter table r import table space, 将这个r.ibd文件作为表r的新空间。

注意事项

1.在执行import tablespace的时候,为了让文件里的表空间id和数据字典中的一致,会修改r.ibd的表空间id。

2.flush tables命令之后,db1.t整个表处于只读状态,知道执行unlock tables 命令才释放读锁。