发现自己没玩过的事情好多,上周末便实施了一次露营⛺️之路,叫上小伙伴一起报了个户外团,第一次去选择的比较休闲级别的,然后自带了几十斤装备在山上扎营看天。

去之前连帐篷都没有打开过,不过好在很好弄,去到了迅速选择好营地很快就扎好了。然后在山顶四处转了转。第一次出来玩还是挺兴奋的,但没多久就冻得回帐篷换上了衣服,对比去的时候早上北京的天气超级闷热。

周围也有好多同行的人在扎营,还有两只🐶子互相溜着玩,和它们的主人一样的兴奋。

到了傍晚,就开始下起了大雾,一直持续到了第二天早上,后来想想从山脚下看来这应该是☁️吧,那就是身在云中了。在这么个野外环境下,找厕所也是很方了,哈哈哈。

下了这么一晚上的雾,有点遗憾的是没能看到星空🌃,而且没能选到一个防露水的帐篷晚上被滴醒好几次😶。。。

早上还是挺可以的,看到了日出🌅和云海,还有四处觅食的🐂。其实云海肉眼看上去不太明显,但是做成一个加速的短视频就很好看了。

露营前简单做的攻略

说明:

  1. 加粗为必备
  2. 标 A 的说明已准备好

装备:

帐篷A防潮垫A睡袋A、气垫(防咯)、头灯A、手电A、登山包A充电宝A、手机

雨衣A、雨鞋套A、登山鞋或运动鞋长袖防风衣物、羽绒服、头巾、棉帽

暖贴A、水杯、水5L A湿巾A纸巾A、洗漱用品、垃圾袋A、耳塞(风声吵)、眼罩

自发热小火锅A、八宝粥、面包、筷子、红牛、便携气炉

药物(快客、思密达+ 、藿香正气、创可贴、防虫喷雾、布洛芬)、风油精 AAAAA

银行卡一张A、工具刀A、备用手机A

这里说一些注意点

  1. 帐篷一定要四季防雨防露水的,赶上下雨就惨了
  2. 防潮垫一定要有,并且根据情况准备厚点的防咯
  3. 暖贴可以准备一些晚上睡觉冷贴身上
  4. 垃圾袋还是多带点将垃圾带走
  5. 工具刀准备一下防止意外保护安全
  6. 如果带气炉的话要注意地铁不让进
  7. 吃的尽量带一些高热量食物,自发热小火锅我觉得必备

最后附上一张小朋友拍的很棒的照片。

跳表(Skip List)

怎样理解跳表

用一种比较通俗的方式去说,跳表是一种带有 N 级索引的有序链表,其中 N 级索引的作用是可以加速查找到链表的目标节点。

比较大众化的解释是,跳表是一个随机化的数据结构,实质就是一种可以进行『二分』查找的有序链表。这里对于随机化的理解是 N 级索引节点的选择上。

跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。在一定程度上可以代替红黑树(Red-black tree)。

跳表的结构

从链表说起,对于普通链表中,即使其存储的数据是有序的,当我们要寻找一个数据,时间复杂度也会比较高 O(n)。

然后我们对链表建立一级索引,每两个节点提取一个节点放到索引层,其中的 down 指针指向下一级。

当我们寻找节点 16,只需要走过 1' -> 4' -> 7' -> 9' -> 13' -> 13 -> 16 节点即可(17'也要访问判断),而原始链表需要走过 10 个节点,节省了 2 个节点路径。如果我们再抽出二级索引后是这样子的。

寻找节点 16 只需要走过 1'' -> 7'' -> 13'' -> 13' -> 13 -> 16 节点。这样我们又可以加快查找到目标节点,图中举例节点比较靠前,试想节点靠后,并且增加了 N 级索引之后效率一定会提升很多。

跳表的性能指标

单一链表的查询时间复杂度是 O(n),插入、删除的时间复杂度也是 O(n)。

以两个节点为跨度的话,那么跳表有如下总结:

  1. 第 k 级索引的节点个数是 n/(2^k)
  2. 假设有 h 级索引,最高级索引有 2 个节点,高度 h=log2n - 1 (2为底数),每一层都遍历 m 个节点,时间复杂度为 O(m*log n)。此时算得 m=3。

以多个节点为跨度,可以节省更多节点,是空间和时间上的相互折中。在实际开发中,索引节点只存储关键值和关键指针,之后链表节点才存储实际对象。

跳表的的查询、插入、更新、删除时间复杂度均为 O(log n)。

如何选择索引层?通过一个随机函数决定将节点插入到哪几级索引中,随机函数特点是要保证跳表索引大小和数据大小平衡性。

跳表在 Redis 中的应用

Redis 中有序集合通过散列表 + 跳表实现的,主要支持的功能有:

  • 插入一个数据;
  • 删除一个数据;
  • 查找一个数据;
  • 按照区间查找数据;
  • 迭代输出有序序列;

相比红黑树,跳表在区间查找上有更好的性能;并且实现起来也相对容易;可以通过调整索引策略来平衡性能和内存使用;红黑树插入删除时为了平衡高度需要旋转附近节点,高并发时需要锁,跳表不需要考虑。

关于源码分析:https://segmentfault.com/a/1190000013418471

参考

https://www.jianshu.com/p/dd01e8dc4d1f
https://blog.csdn.net/pcwl1206/article/details/83512600

整数集合

在一个集合中只有为数不多的整数时,Redis 使用 intset 整数集合存储数据。具有如下特性:

1
2
3
4
5
6
7
8
typedef struct intset {
//编码方式
uint32_t encoding;
//元素数目
uint32_t length;
//保存元素的数组
int8_t contents[];
} intset;
  1. 数据从小到大排序并且自动去重。
  2. 数据类型实际存储在 encoding 中。
  3. 当 encoding 中的数据类型不能满足时会自动进行类型升级。
    1. 重新分配空间
    2. 迁移
    3. 添加新元素
    4. 时间复杂度为 O(n)
  4. 不支持降级操作。

优点:

  1. 灵活,不用考虑整数集合类型,直接添加自动升级。
  2. 节省空间,只在必要时进行升级。

升级操作是指将整数由 16 位、32 位、64 位的方式增加支持范围。

IO 模型

通俗的理解 Redis 是一个单进程单线程模型的 KV 内存数据库,截止到目前官方会在年底发布多线程版本,并且 Redis 也有自己的持久化方案。采用 I/O 复用模型和非阻塞 IO 技术,被广泛应用在高并发场景中。

Redis 高性能的几个关键点:

  • 完全基于内存操作,数据也是存储在内存中。
  • 数据结构简单,很多查找和操作的时间复杂度在 O(1)。
  • 单线程模式,避免了上下文的切换和锁竞争。
  • 使用了 I/O 多路复用模型和非阻塞 IO。

Redis 同时支持多个客户端连接,采用 I/O 多路复用模型(select\poll\epoll)可以同时监听多个 IO 流事件。

多路指的是多个网络连接,复用指的是复用同一个线程。
采用多路IO复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗),且Redis在内存中操作数据的速度非常快(内存内的操作不会成为这里的性能瓶颈),主要以上两点造就了Redis具有很高的吞吐量。

TODO I/O 多路复用

参考

http://researchlab.github.io/2018/10/08/redis-11-redisio/

在这几年的微服务开发过程中遇到过两次因为网络问题导致的系统故障,并且没有做好降级策略,导致系统的不可用时间增加,所以今天专门整理一篇关于网络故障的问题分析处理以及开发中需要注意的地方。

基础部分

TCP 连接,先抛大图:

a

主要分为三部分:

  1. 建立连接
  2. 传输数据
  3. 关闭连接

原理不做过多介绍,主要说说常见的异常和模拟方式。

常见的异常类型

b
上面的异常是一些常见的功能性异常,其它性能方面的异常不在本文讨论范围。

实施手段

需要的工具

  • python 脚本
  • iptables,对网络流量进行规则过滤
  • tcpkill,用来断开网络构造异常
  • curl,发起 http 访问请求

Python脚本

主要作用是启动一个TCP监听,然后将接收到的数据在转发回去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#! /usr/bin/python
# -*- coding:utf-8 -*-
import socket
import sys
def start_tcp_server(ip, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (ip, port)
print('Starting listen on ip %s, port %s' % server_address)
sock.bind(server_address)
try:
sock.listen(1)
except socket.error as exc:
print('Fail to listen on port %s' % exc)
return
while True:
print("Waiting for connection")
client, addr = sock.accept()
data = client.recv(1000)
client.send(data)
client.close()
print(data)
if __name__ == '__main__':
start_tcp_server('0.0.0.0', 12345)

iptables 基本使用

1
2
3
4
5
6
// 查看当前生效规则
iptables -L -n
// 清空所有规则
iptables --flush
iptables -F

tcpkill 基本使用

https://yq.aliyun.com/articles/59308

1
2
3
4
// 安装
sudo apt-get install dsniff
// 使用
...

curl 超时设置

使用 curl 有两个超时时间,一个是连接超时时间,另一个是数据传输的最大允许时间。
连接超时时间用 --connect-timeout 参数来指定,数据传输的最大允许时间用 -m 参数来指定。

1
curl --connect-timeout 10 -m 20 "http://192.168.1.110:12345"

实施过程

  1. A机器启动Python脚本,监听12345端口。
  2. B级器通过curl命令进行访问。
  3. 在访问过程中通过配置iptables来实现网络的各种异常情况。
  4. 通过 tcpkill 来实现连接中断的异常情况。

正常访问

1
2
3
4
5
6
xyz@xyz-pc:~$ curl "http://192.168.1.110:12345"
GET / HTTP/1.1
Host: 192.168.1.110:12345
User-Agent: curl/7.58.0
Accept: */*

查看和清除规则

1
2
3
4
5
6
7
8
9
10
11
12
13
xyz@xyz-pc:~$ sudo iptables -L -n
[sudo] xyz 的密码:
Chain INPUT (policy ACCEPT)
target prot opt source destination

Chain FORWARD (policy ACCEPT)
target prot opt source destination

Chain OUTPUT (policy ACCEPT)
target prot opt source destination
DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:12345 flags:0x17/0x02

xyz@xyz-pc:~$ sudo iptables -F

连接超时

1
2
3
xyz@xyz-pc:~$ sudo iptables -A OUTPUT -p tcp --syn --dport 12345 -j DROP
xyz@xyz-pc:~$ curl --connect-timeout 10 "http://192.168.1.110:12345"
curl: (28) Connection timed out after 10001 milliseconds

读取数据超时

1
2
3
xyz@xyz-pc:~$ sudo iptables -A OUTPUT -p tcp -m state --state ESTABLISHED --dport 12345 -j DROP
xyz@xyz-pc:~$ curl --connect-timeout 10 -m 20 "http://192.168.1.110:12345"
curl: (28) Operation timed out after 20001 milliseconds with 0 bytes received

拒绝连接

1
2
3
xyz@xyz-pc:~$ sudo iptables -A OUTPUT -p tcp --dport 12345 -j REJECT
xyz@xyz-pc:~$ curl --connect-timeout 10 -m 20 "http://192.168.1.110:12345"
curl: (7) Failed to connect to 192.168.1.110 port 12345: 拒绝连接

连接被重置

这里需要将Python脚本的 client.close() 注释掉。

1
2
3
4
5
6
7
xyz@xyz-pc:~$ sudo tcpkill -iwlp8s0 port 12345
xyz@xyz-pc:~$ curl --connect-timeout 10 "http://192.168.1.110:12345"
GET / HTTP/1.1
Host: 192.168.1.110:12345
User-Agent: curl/7.58.0
Accept: */*
curl: (56) Recv failure: 连接被对方重设

总结

在越来越多的企业微服务化进程中,肯定会遇到网络请求的各种问题,当我们在做一个基础组件或者进行网络通信请求时需要考虑到这些异常情况,最好还是将各种常见的情况模拟实施一下,来保证服务的稳定性。首先要说的是请求的超时设置,不论是在进行 HTTP 访问还是封装后的 RPC 请求,超时设置是最基本的。
基于不同语言的不同组件实现质量来说。曾经遇到过一个问题是,一个服务处于假死状态,Java 的客户端中默认超时和多线程可以使主线程服务不会受到过多影响,golang 中的客户端默认设置了一个很长的超时时间,服务在一定程度上受到了影响,而Python的客户端超时时间也是很长,还有就是Python只有一个主线程再跑,所以此时服务会被 hang 住了。
所以这里还有一个问题就是服务降级,当前服务如果出现问题,重试几次后仍然失败,那么是否降级来保证当前服务的可用性,其实考虑的是异常服务对于当下的重要性,是否在整个核心服务链路当中,否则的话进行降级处理。
还有一个关键点是慎用重试,偶然的网络波动导致的异常在重试下会很有效,但是当遇到服务性能导致的超时问题时,就遇到大量的客户端重试导致请求翻倍,很可能会直接把服务打挂,所以不要轻易使用重试,可以通过一些额外的补偿机制来提高服务稳定性。

未防止爬虫盗爬我的文章,在末尾打个广告。这篇文章首发在我的个人博客 知一的指纹 http://noogel.xyz ,有需要技术交流的朋友可以加我个人微信:zhi2012666

参考

https://blog.csdn.net/llianlianpay/article/details/79768890
https://www.cnblogs.com/gl1573/p/10129382.html
https://blog.csdn.net/wangyiyungw/article/details/85004905

从北京出发一路向北有个坝上草原,趁着七月天热的时候去避避暑,看惯了城市的生活,周末偶尔出去放松一下也是好的。便约了几个同事一起自驾过去玩的。

去的前一天预报说下雨☔️,结果当天也只是一路阴天☁️,等到了坝上草原也没有下,恰巧那天晚上北京下的,完美避过。下面这张是在去的时候怀柔拍的,这边的山⛰相对高耸一点,夏天还有点绿色,冬天就真的是凸凸的。

等到了坝上草原这边,真的是天高云阔空气清新。而且也有点冷🥶,哈哈哈也就二十度左右,所以带件外套真的是很有必要的。

在丰宁,一路上都是这样子的。

整个旅途并没有排的很紧凑,而是自驾的舒适随意。导航到了一个草原的入口,然后爬上山坡一路向草原深处走去。顺着一条小路走过一个又一个山丘,草原的广阔无垠尽收眼底。同时,七月的日子里草原也是超级冷的,小风飕飕的,所以准备穿着冲锋衣来是最明智的选择了。

在草原,远远望去会看到一个个的村庄,彼此离得很远感觉出村的路就那么一条,不过生活在这样的地方应该会很惬意。

要说印象最深的还是说草原的夕阳🌇了吧,在城市里因为高楼的遮挡可能早早的就见不到太阳了。而草原的夕阳可以在很晚,直到太阳落到地平线以下。拍下面这张图的时间是在傍晚 7 点半。草原的能见度真的很高,远处的山⛰感觉要几十公里外了。

来这样的环境下偶尔放松放松,心情真的也会很好了。还有一张像极了 Windows 经典的桌面背景,蓝天白云绿草地。

下面👇是去之前简单做的攻略:

要带的东西:

防晒霜、太阳镜、帽子、雨伞

防蚊虫叮咬的、创可贴、风油精、快客

野餐垫、食物、水果、红牛

身份证!!!!!

手机、充电宝、

纸质现金

昼夜温差大(28 - 15度)带一件春秋外套

穿户外登山、运动鞋!!!

摄影装备(选填)

玩点:

看日出

柳树沟(50元)

情人谷(免费)

草原娱乐场(滑草??、射箭、CS、骑马)

烤串

草原天路

路线图:
(需要的可以找我要~)

首先要说的是重构最基本的定义:重构是在不敢编软件可观察行为的前提下改善其内部结构。

每一个开发人员肯定都经历过『坏代码』的味道。在一个古老又庞大的项目中,这里面一些函数的作用和逻辑变的很难理解,没有人了解这里的所有 case,加上没有足够的注释,之前开发的人员离职等诸多因素,可维护性非常低,谁都不愿意碰,这时候再改动一个需求,会很容易引入一些 bug。当你遇到上面的这些情况时那么时候要把这摊『臭水坑』清理一番了。

我们知道要做重构这件事了,那么『工欲善其事必先利其器』,重构也是有诸多手段的,有许多被前人验证过的重构手法来帮助我们改善项目代码的健康状况。接下来讲讲一些小的也是简单实现的重构方式。

flight over Barcelona ...

小重构

重复的代码

重复代码的抽象有几种方式,一种是将重复的代码或者相似的代码,可以提取到一个扩展函数中,然后在多个地方调用;或者将多个相似类中的相同代码抽象到父类中,子类调用,但是按照组合优于继承的设计方式,不建议这样做;再有是对相似流程代码抽象出模板方法,子类实现差异化逻辑。

过长的函数

在计算机领域有这样一句名言:『计算机科学相信所有问题都可以通过添加一个间接层来解决』。如果我们没有良好的系统设计经验和深刻理解面向对象思想(业务系统主流的编程思想),就很容按照过程式的思想去写代码,就会出现职责庞大的函数或类,有着超多的分支判断逻辑,各种补丁代码块。这里一部分是系统设计的问题,另一方面没有很好的拆分职责。一个很好的办法就是将分支中的代码块抽离成小函数,把大类拆分成职责较为单一的小类。再有让小函数容易理解的关键是一个好名字(关于起名字这块可以单独说说);再有大函数中的临时变量可能阻碍你的拆分,可以把这些临时变量通过查询的方式获取,既提高了可读性又能共享给其它地方用。

过大的类

过大的类就像过长的函数,冗杂且难以理解。我们通俗的说这个类的职责太重了,导致里面又很多的实例变量。改造的办法是将多个实例变量分组,然后拆分不同的类去处理,这样来拆分出一些单一职责类。再有就是可以确定类的使用方式,提炼出来接口帮助理解这个类。

过长的参数列表

过长的参数列表可能是这样产生,最初定义接口只有两个参数,那么随着业务扩展,这个函数产生的职责越来越大,随之参数越加越多。这种的解决方案是搞一个参数对象,将原先的参数都保存到参数对象实例中,然后传递这个实例到函数中处理。

Pelee

然后呢

我把这写最基本的风险小的方式叫做小重构,可以让我们的代码变得稍微好一些。其实你在做小重构的过程中可以慢慢形成对于这个系统业务流程的理解,以及对于系统设计(大)重构方向上的思路。那么什么时候或者什么时机就要开始重构?

如果让我接需求改系统一个部分的代码,做完如果再次需求改动不是很容易改的时候,基于事不过三的原则,我会在需求中做一些重构来弥补设计上的缺陷;再有就是修复 bug 的时候,如果不是很好修复,我也是要先进行适当的重构的再去解决的;或者我们集中进行 codereview 的时候提出来需要进行的重构时。

一个好的项目是需要有一个好的设计基础,因为我们不能只想着今天做什么,还要想明天可能会做什么,只做好今天,而明天到来发现无法做到,那么也是失败的,想的多了就会出现过度设计,也是包袱。所以写好代码是一件挺难的事情,写之前多思考一下。今天先写这么多~

首先这篇文章是建立在有一些编程基础之上来展开的,做为一种效率学习编程语言的自我总结输出。把编程语言当做一个工具,而这些不同种类的工具有很多的共通之处,抓住其中的关键之处可以大大提升学习效率,也是一篇自我总结的学习方法论,里面有的方法可能不适合我,但也会讲讲。如果要学习一门编程语言,先要问一下为什么要学?学会了能做什么?要达到什么样的目标?只有把这些问题想清楚了再去做,不然稀里糊涂不知所以,很可能半途而废。想明白了就要坚持去做,不要再东望望西看看,想、做、坚持三位一体是学到的基本三要素。

学习途径分析

网上的学习资料纷繁复杂,各种培训课程满网都有,那么大致分为以下几种吧:

总结来说学习路径是这样的。

  • 入门:适合通过看视频和培训来实现,然后通过搜索引擎和博客文章论坛协助解决遇到的各种问题。
  • 提高:通过看书和大佬的博客文章交流论坛等来加深理解。
  • 实践:实践也是提高的一种方式,可以通过学习别人项目来仿写实践。

适合学习或交流的地方,Stack Overflow、博客园、简书、GitHub、官方论坛等等。关于搜索引擎,入门用百度就可以了,提高和实践建议用谷歌。

指定计划

这个也挺关键的,我就没有做好,也不太好定。

查问题的办法

学习过程中很关键的一点就是遇到问题如何解决问题,解决问题的速度和方法很大程度上决定我们后期学习的进度和自信心,那么我总结了几条比较关键的要素说明。

学会如何看异常信息

不论写 demo 还是实际项目开发中,肯定会遇到一堆异常情况,然后控制台打印一堆杂乱信息,首先要做的是理清楚其中的信息结构,其实就是日志啦,大致分为以下几种:

  • DEBUG:一般在开发调试期间开启,可以比较清楚的了解程序在运行过程中的各种状态和传参等。
  • INFO:正常运行日志信息,当程序访问量很大的情况下很多,会收集到日志系统。
  • WARN:对于程序可能出现的潜在问题的地方记录信息,或者不再支持的方法或库等。
  • ERROR:对于程序运行中的非预期异常访问、状态、请求需要记录错误信息和错误状态。
  • TRACE:这个在不同语言表的可能不同,主要是概括为带有调用堆栈信息的异常日志。

那么在测试过程中为了快速定位问题,还是要打印 TRACE 级别的异常日志,那么异常信息如何看呢?

Python 的异常信息示例

1
2
3
4
5
6
7
8
9
10
11
12
13
xyz-macdeMacBook-Pro:dev-demo xyz$ python mixin_demo.py 
Init people.
People can eat!
People can drink!
People can
Traceback (most recent call last):
File "mixin_demo.py", line 44, in <module>
people = People()
File "mixin_demo.py", line 39, in __init__
print "People can ", self.sleep()
File "mixin_demo.py", line 30, in sleep
raise NotImplementedError(u"can't sleep.")
NotImplementedError: can't sleep.

在执行 Python 脚本的时候执行异常报错,Traceback 是我们要看的异常堆栈信息,自顶向下从 "mixin_demo.py 的 44 行开始执行,执行到 mixin_demo.py 第 30 行报错,异常类型 NotImplementedError,异常内容为 can't sleep.。也就说我们看到报错信息后,然后可以看到对应报错的位置以及调用链,方便快速定位到问题。

Java 的异常结构展示顺序有点不同,它的信息最底部是调用入口,然后向上一直到报错点,还有报错信息也在上面。这里只是一个简单的示例,通常 Java 的异常堆栈信息很长,调用链很深,这就需要有一定的经验来判断问题实际产生的原因。

1
2
3
4
5
6
7
8
9
10
objc[20307]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/bin/java (0x1077344c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x10874d4e0). One of the two will be used. Which one is undefined.
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'teacher2' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:775)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1221)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:294)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy$LookupOverrideMethodInterceptor.intercept(CglibSubclassingInstantiationStrategy.java:290)
at com.noogel.xyz.lookup.GetBeanTest$$EnhancerBySpringCGLIB$$e79f57a6.getBean(<generated>)
at com.noogel.xyz.lookup.GetBeanTest.showMe(GetBeanTest.java:5)
at LookupTestMain.main(LookupTestMain.java:9)

使用IDE断点调试

IDE 可以帮我们做很多事,大大提高了我们的开发效率,那么如何用好这样一个工具也是很关键的,这里说一下利用 IDE 进行 debug 调试。

通过上图我们可以看出 IDE 的调试功能很丰富,如果用好对于提高学习编程效率很有帮助。

查问题打日志

在 Python 中有一个查问题很好的利器 iPython,可以很方便的直接传参执行函数,对于线上问题,这样虽然很方便但是不可取。像 Java8 这样版本下的语言又没有很好用的一个工具,那么可以通过打日志的方式来操作,需要对可能的问题点分析然后增加日志辅助判断,打日志是一个很合适的查线上问题的方式。

向有经验的人请教

老师傅一出手就知有没有,如果遇到问题没有思路或者自己憋住了,需要及时向有经验的人请教,才是一个正确的选择。

了解框架的运行机制

俗话说『打铁还需自身硬』,遇到了问题总不能一直向别人请教吧,还是要自己去搞定的,上面的介绍的方法搞不定怎么办,那就要平时花心思在使用的框架上学习了,因为你不可能直接裸写代码的,一些轮子直接搬过来用固然高效,但是不懂构造轮子还是用不好的,容易出问题,所以要平时多去积累了。

以上讲的都是一些可以提高学习效率的方法。先写这些,后续想到新的再继续更新。

实践一个项目

一般视频和图书的最后一部分都会有初级的实践项目,可以用来综合练习一下所学到的知识。可以看完再自己提需求自己实现一遍。

再有就是 GitHub 上有很多,不过要好好找找合适,可以拿来练手,最好是找一些多 star 的项目,对于技术提升很有帮助,发现问题还可以提 issue 交流,然后提 PR 解决。

另外一个就是某某公司意外泄漏或者开源的项目源码,这些都是经历生产环境反复验证的代码,具有很高的参考性。

举个例子就是在 Java 中 spring MVC 是一个很著名的项目,那么学习它,有的人就手敲 spring 项目,边学习边实现其中关键的代码。GitHub 地址:https://github.com/code4craft/tiny-spring

了解语言的技术栈

学习一门编程语言肯定是用来解决实际问题或找一份工作的,那么你要知道并不仅仅是学习这门编程语言,而是整个技术栈。了解一个语言的技术栈可以去招聘网站上看,一般都会写至少需要精通一门编程语言,熟练使用 MySQL 解决并优化问题,熟练使用并了解各种 MQ 原理等等。那么这些都是需要去了解的,起初学习可以为了用而用的去实践一个更完整的需求。

比如说我需要了解公司的微服务架构,那么我需要看它都用了哪些技术栈,然后自己再手敲一些项目,并且 docker 化,自动注册服务发现来管理多个 RPC 服务等等。。。

总之,一个是多看多实践多思考,然后就是多交流,闭门造车是不行的。

了解语言的运行机制

语言的内存模型?
语言的并发模型?
语言的垃圾回收机制?

基本概念

一个事务在进行数据变更时对另一个事务产生的可见性影响描述,表达为 脏读、幻读、不可重复读三个概念。下面具体解释下对应概念。

  • 脏读:当前事务能够读取其它事务未提交的数据。
  • 幻读:当前事务中在前后两次相同查询中读取的数据不一致,原因在第一次查询后第二次查询前提交了数据产生的。(侧重于插入了新的数据)
  • 不可重复读:当前事务中查询相同的范围数据,同一数据的内容发生了变化。(侧重于数据的更新)
    基于这三个现象描述,主要因为 MySQL 设置的隔离级别不同导致的。

ACID特性

  • 原子性(Atomicity),一个事务中的所有操作要么全部成功,要么全部失败,不能只成功一部分。
  • 一致性(Consistency),从一个一致性状态到另一个一致性状态的转换。(一致性和隔离性保证了数据的一致性)
  • 隔离性(Isolation),一个事务在提交之前对其它事务是不可见的。
  • 持久性(Durability),一个事务一旦被提交就会永久的保存到数据库中。

InnoDB中的事务隔离级别

  • 未提交读(Read Uncommitted),允许脏读,也就是可能读取到其他会话中未提交事务修改的数据。
  • 已提交读(Read Committed),只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别。
  • 可重复读(Repeated Read),在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻读。
  • 串行化(Serializable),完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞。
隔离级别 脏读(Dirty Read) 不可重复读(NonRepeatable Read) 幻读(Phantom Read)
未提交读(Read uncommitted) 可能 可能 可能
已提交读(Read committed) 不可能 可能 可能
可重复读(Repeatable read) 不可能 不可能 可能
可串行化(Serializable ) 不可能 不可能 不可能
1
SELECT @@tx_isolation; 

查询 InnoDB 的默认隔离级别是 RR,按照四种隔离级别的关系来看是会出现幻读情况,但实际上 InnoDB 引擎下的两次查询是一致的,那么它是帮我们解决幻读了吗?

1
2
3
4
5
6
7
8
9
10
11
//设置read uncommitted级别:
set session transaction isolation level read uncommitted;

//设置read committed级别:
set session transaction isolation level read committed;

//设置repeatable read级别:
set session transaction isolation level repeatable read;

//设置serializable级别:
set session transaction isolation level serializable;

MySQL是如何解决幻读的?

  1. 在可重复读隔离级别下,普通的查询是快照读,是不会看到别的事务插入的数据的。因此,幻读在 “ 当前读 ” 下才会出现。
  2. update 语句的修改结果,被之后的 select 语句用 “ 当前读 ” 看到,不能称为幻读。幻读仅专指 “ 新插入的行 ” 。

产生幻读的原因是,行锁只能锁住行,但是新插入记录这个动作,要更新的是记录之间的 “ 间隙 ” 。因此,为了解决幻读问题, InnoDB 只好引入新的锁,也就是间隙锁 (Gap Lock) 。间隙锁和行锁合称 next-key lock ,每个 next-key lock 是前开后闭区间。next-key lock 可能会导致同样的语句锁住更大的范围,这其实是影响了并发度的,在 RR 隔离级别下,两个是事务同时锁住一个不存在的值,之后进行插入操作会引发死锁,因为间隙锁之间并不会冲突。如果设置成 RC 隔离级别的话间隙锁就不存在了,同时需要解决对应的数据和日志不一致问题,需要把 binlog 格式设置为 row 。

参考:

https://www.cnblogs.com/likui360/p/9632641.html
https://en.wikipedia.org/wiki/Isolation_(database_systems)#Phantom_reads

夏天来了,大半夜热的睡不着,最近看过一本书《极简主义》,罗列一下其中的观点,以及作用到生活中的尝试。

首先来引述原文介绍下什么是极简主义:

极简主义是一个工具,我们用它来获得人生的满足感。极简主义中没有规则。确切的说,极简主义只是去除生活中那些无关紧要的事物,从而使我们能够专注于最重要的东西——而它们事实上根本就不是实物。

这并不是它的定义,而是说明了从极简主义带给我们什么,让我们从杂乱的生活中抽离出来,让我们舍弃对于我们不那么重要却又不想割舍的人际关系或事物,专注于重要的事情中去。读这本书的起因是前一段时间对于工作和生活感到很沉重,工作一天带着疲惫身体回家后,面对堆满杂物的宿舍,心情更加烦躁,每天纷繁不断的微信消息偷走了大量的时间。每天都很忙,却又觉得并没做什么事情,睡觉的时间也越来越少。

当我意识到现在的问题,恰好在豆瓣看到这样一本书,下载下来读了一遍,对于书中的部分观点还是比较认可的,便有意识的改变自己的生活方式。这里作出改变只是在生活上,对于工作还是那样,甚至我认为工作是反极简主义的,就像书中的作者是辞去工作开始的极简主义实践。其中有很多讨论我并没有太在意,也是不适合我当下去作出改变,下面只是按照一条实践过的线去聊。

首先,是什么让我们感到不够快乐,让我们感到压力呢?作者确定了一个叫做『锚』的概念,代指我们想要的得到的大房子、高薪、物质财产、公司奖励等,这些欲望占据着我们,给我们带来压力。试想一下如果我们已经拥有或者舍弃掉这些,也许会轻松很多,可以有更多的时间精力专注于我们喜欢的事情上,让人生过得更有意义?好像我现在也无法做到舍弃。如果这个⚓️给你带来太多负担,那你是要舍弃了,而这里我所能做的不是上面那些,下面慢慢说。

首先我们要使人生有意义的五大价值明确:

  1. 健康
  2. 人际关系
  3. 热情
  4. 成长
  5. 奉献

这本书后面的章节也是在围绕着五个主题来开展说明的。这本书有个特点就是并不想书名那样极简,而是在不断赘述一些观点,以加强这些观点在读者心中的印象吧。

作出改变,如果你只是一味在想而没有实际付出行动的话并没有任何作用。首先是必须要作出改变,改变永远不会太迟,你需要的只是在知道该怎么做了之后付诸行动。这点很重要,所以作者也是提前说明。这里推荐去看拖延症相关的知识。

下决心总是困难的。而拖延却很简单——至少拖延一时很简单。但拖延得不到任何回报。

打包派对,打包你大量的闲置物品,保留生活中常用的,然后扔掉、转卖或捐赠。对于闲置物品不仅占用了你的空间,还浪费你的精力,平时你也想不起来用它,然后又要找半天,在生活如此便利的今天,你可以舍弃很多无用的东西了。还有注意不要囤货,日用品很容易买到,你就把楼下的便利店当做你的免费仓库好了。东西要少而精。推荐看一下《怦然心动的人生整理魔法》、《断舍离》还有日剧《我的家里空无一物》。看完书的当周我便清理出了几大袋子暗藏在屋里角落的无用物品,实际上在之前看书的时候就有意识的清理丢弃无用的东西,只是后来没做好,看来要周期性清理一番。

健康,这个老生常谈,却总被人忽视,没有健康,你连生活中最简单快乐都无法享受。这里我一直做不好的就是没能去改变熬夜的习惯。健康的从广义上来讲有以下几种:情绪健康、心理健康、精神健康、财务健康、身体健康等。这里只针对身体健康来讲,分为两个方面,饮食和锻炼。饮食上并没有去尝试改变,因为自己本身很瘦。而锻炼作出了一些,在这之前总是说要去健身房健身,却总是懒的去,读完这本书后努力去作出改变,从跑步开始,因为我觉得跑步是最容易开始的锻炼,现代科技带给人们很多的便利性,借着小米手环的帮助开始了每周三次的夜跑,精神状态也慢慢好转。另外说一句是跑步要是一件技术活,推荐看一下《运动改造大脑》、《跑步圣经》这两本书。平时多看看书还是有好处的,当你尝试作出改变的时候也是能知道如果改变。

讽刺的是,锻炼实际上给了你更多时间,而不是把时间夺走了。它不光能延长寿命,对我个人而言,它还让我跟我最好的朋友建立了更紧密的联系。当我一个人锻炼时,它又给了我独处的时间,留给自己的时间,而我们都知道这是很重要的。锻炼是一种意想不到的帮你重获时间的方式。

人际关系,你的人际关系有你的亲人、朋友、同事、配偶、恋人、室友等。人际关系处理这块是弱项,需要认真🤔一下。

建立关系的三种办法:

  1. 寻找超棒的新人际关系;
  2. 改造你现有的人际关系;
  3. 改变你自己。

必须要牢记的是:你的人际关系在你往后的人生中并不是固定不变的。随着年龄的增长,会有不同的人走入或走出你的生活,而他们与你的关系的重要程度也会改变。许多十年前与你关系紧密的人现在已经不再亲密了,对吧?同样,你未来的人际关系也会继续变化、成长。因此,在这个过程中积极行动就很重要。你主动选择自己的人际关系,而关于前两档人际关系的调整,往往要作出很艰难的抉择。
你唯一能改变的人是你自己。当你以身作则时,与你走得最近的人,往往也会跟着效仿。

断掉网络,这点对我的改变应该说是关键性的。每天下班回家都会有家人和朋友发来各种微信消息,填充了我整个晚上的时间,使我并没有一段固定且连续的个人时间了,也就导致我对于当下缺少反思,活在网络的世界中。还有各种 APP 新闻推送,一刷就停不下来的短视频等,我们的时间被大量的占据,这也是痛苦和拖延的关键,也是一个很重要的⚓️。然后我做了一些改变。

  1. 开始清理📱中那些功能重复的 APP,比如获取热点新闻微信足够了,我对时效性又没有那么高。
  2. 各种短视频 APP,这些起初我觉得有学到东西才装的,但是却背离了初衷,成了娱乐工具,然后果断卸掉。
  3. 还有各种能用微信小程序替代的常用 APP 基本都卸掉了。
  4. 有些保留了,QQ 被我用 Tim 替代,有些不得不看的群内容我也是订好提醒,每周看一周的内容。无关紧要的推送都被我禁掉了。
  5. 还有最重要的一点是每天非特殊情况下执行断舍离计划,晚上 10 点到 11 点断网一小时,感觉这个世界突然安静了起来,可以专心想和做一些事了。

这里多说一句,网络依赖确实像一种上瘾的症状,而且现在绝大多数人都是这样子,这个还是要从自身开始尝试摆脱这种依赖,呼吁身边的人同样去改变,我们的生活应该会有很大的改变。

认知,认知是我们最宝贵的自由。前几年我们一直在讲认知升级,确实,这就是一种让我们获得自由的方法。这里推荐去看《见识》这本书,不再多说。

回到最初,极简主义是一个工具,这个工具使我们摆脱那些闲杂冗余,从而更容易过上有意义的生活,这个工具使充斥着无尽装饰、看起来错综复杂的世界变得更简单、更容易、更真实。


第二天早起接的🐰主子,来自主子的凝视 qwq…

为什么是天津,一个是离得近往返方便,另一个是没有去玩过,想去好好转转,两天时间能玩个大概吧。

天津卫由来

去之前,对于天津的了解只知道是个直辖市,书本上的了解早已忘干净了。去了才知道天津又称天津卫,其中的『津』意为渡口,『天』意为天子,是明成祖朱棣赐的名,为『天子的渡口』含义。『卫』是明朝的军事建制,当时天津设有天津卫、天津左卫、天津右卫,是这个称呼的由来。

解放桥、世纪钟

解放桥和旁边的世纪钟👇

出了城际站做了一站天津地铁,也是因为不熟,绕了个小弯,出了地铁站就看到了解放桥,解放桥对面是世纪钟。解放桥旧城万国桥,始建于 1927 年,是天津标志性建筑之一,看历史挺波折的,和世纪钟挨着,有兴趣可以去了解一下。

九国租界的事

到了天津才知道战乱时期曾设立过九国租界,对天津的建筑风貌产生很大影响,而天津政府对于这些建筑风貌一定程度上进行了保留,所以在天津经常看到一个外国风貌建筑的背影是现代高楼。

意式风情街

到了天津一定要去的地方,保留了大量的意式风情建筑和名人故居,里面有一个免费的天津历史介绍展馆,可以了解一下天津的历史变革和人文风貌。然后是里面的几个景观建筑,但丁广场和马可波罗广场。再有就是逛了一下梁启超故居,里面介绍了梁启超的生平和贡献。其中的意大利兵营应该是某个部门的办公单位,只是在外面观望了一下。整个意式风情区不需要门票,可以骑着单车在里面随意闲逛,时不时会看到一些名人故居,意大利风情建筑在这里融为一体,颇有一些浪漫氛围,拍婚纱照可以来这里。

马可波罗广场👇

梁启超故居👇

意大利兵营👇

1919

出发前一天晚上看着《爱因斯坦传》入睡的,正好看到了,1919 年发生了一件事,使得几年前爱因斯坦在广义相对论中的预测得到了证实,从此爱因斯坦被推上神坛,想了解可以去看一下。同年梁启超参加巴黎和会,然后巴黎和会上中国外交的失败引发了五四爱国运动。发现今年恰好是这两个事件一百周年之际。

天主堂的故事

接下来的行程是计划去古文化街,在旁边看到一个天主堂,正赶上在看西方哲学故事,便进去听修女讲了一段耶稣受难的故事。

古文化街

古文化街也是网上推荐的打卡地,是一个天津著名小吃街,有着很浓厚的商业气息,就像北京王府井的小吃街,不约而同的都听到过是本地人基本不去的小吃街。在这里我觉得去看一下就行了,个人觉得东西不咋地,吃的也很难吃。

金汤桥的历史

金汤桥和解放桥一样也是架在海河之上的大型铁桥之一。建于清光绪年间,1949 年在平津战役中,解放军分为东西两个突击团对天津守敌发起总攻,在金汤桥胜利会师,所以称为了解放天津的标志性建筑。

站在金汤桥上拍海河👇

天津之眼

我想每个来过天津的人都会推荐去做一下天津之眼,它是一座跨河建设的摩天轮,推荐晚上去做可以欣赏整个天津的夜景,排队一个半小时,坐摩天轮半小时一圈,适合情侣去。
做完摩天轮可以去附近的码头做轮渡欣赏海河夜景,可惜的是人家关门早,没赶上。

海河夜景👇


瓷房子

瓷房子原来是一个座法式洋楼,后来才经人设计贴满了瓷片。里面有讲解主要说的是瓷房子的设计理念和寓意,也是为了弘扬中华文化。初次去看整个瓷房子很惊艳梦幻,因为对瓷器不懂,也只是作为一个门外汉草草欣赏了一番,其实里面有很多细节和故事可讲,也不知从何了解。

五大道

对于五大道的游览仅限于做了一趟观光马车,听了一遍讲解。看里面的景色和建筑很有异国风情,差不多也快百年历史了吧,春天适合情侣们来拍照。里面的建筑大部分都被一些公司租下来用来办公了。

西开教堂

西开教堂是天津最大的罗马式建筑,从外面看上去气势恢弘,说道这里,其实也想去通过天津了解各国的建筑风貌,但是并没有从当地的讲解中了解到,也是一个遗憾吧。对于建筑的讲解大部分还是偏向于外观独特地,比如瓷房子之类的,这样也许更适合游客的接受。

高达宏伟的西开教堂👇

劝业场

买买买,类似于北京的西单商业街,各种一二线衣饰品牌。

交通

上面所说的几个景点基本都挨着很近,所以整个游玩并没有做地铁,而是骑地随处可见的电助力车,可以走走停停自由把握节奏,当然也可以做公交。总之交通是很方便,比起偌大的北京城,可以很快的在景点之间切换。

最初规划要去的地方👇

关于吃这可就难到我了,古文化街的小吃真心不建议了,景点的东西贵倒无所谓,主要是不好吃。地道的小吃又找不到,所以也无法推荐,据说天津人特别重早餐,越小摊做的小吃越地道,可以去试一下,注意要避开景点附近。

这次的天津游玩,更偏重于了解城市文化和历史吧,吃上没怎么下功夫,玩上适合两个人春暖花开来拍照。单单了解一个城市两天肯定是不够的,这次也只是把课本上的历史一部分变成了身边的故事,至于剩下的故事,不知何时再去了解了。

最后附上一张🤳

今日寻得一个好地方,可以静下心来好好回忆下去年的经历与成长。在这一年中遇见了一个不错的领导同时经历了部门比较大的人事变动;谈了女朋友又分手了,改变了我某些观念和生活习惯;首付了一套老家的房子,漂泊的心得以暂时的安稳;认真读完吴军博士的《见识》、《具体生活》两本书,对于我的认知提升帮助很大。

工作的成长

lt4

第一次遇见这个领导,并没有特别的印象,只是他特别女性化的名字很容易让没见过他的人误解。后面慢慢工作中会发现带着之前学长的身影,是那种之前提到过的为了技术而做技术的人,会带着一种求知与好知的心态在工作与生活,在目前这样一家公司部门中很难再发现第二个这样的人,他的强大之处还有就是对于人的影响。我认为知识与观念之类的第一我们要做到的是输入,需要我们去主动学习;第二便是内化,如何将所看所听转化为自身的感悟,需要我们去多思考;第三便是输出,将我们自身的认知与见解传达给别人,从而影响和改变别人。所以能做到第三点的人真的是很厉害,当然这里所说影响一定要是积极的一面。他的影响力在周边同事相较是要高出很多。而我自评来说,第一和第二尚可,第三却是几乎没有了,所以表达力这一层也是我很缺失的一部分,再有就是过于习惯性做事,缺乏足够的思考。这里说的更多还是在于工作和技术方面。

失去的开始

lt2

失去了什么?时间还是人。有个深刻的体会就是失去便是成长的过程,想起电视剧《男人帮》中的一句颇有哲理性的一句话『有一些东西我们盼不到,有一些事情我们回不去。但每次想到你带给我的变化,我就充满感激。我想起你,就是想到现在变得更好的自己,这样的自己是你带给我的。』,你身边的恋人或每一个同学朋友,你们互相交谈一起玩耍,后来你们不再联系,彼此陌生,或许有一天你会突然回想起他(她)的一句话,你觉得有道理去改变了。其实在这些亲密关系中,一个很重要的角色就是你的恋人,因为熟悉,便可以心安理得地对自己的缺点置之不理,很容易忽略对方的吐槽,直到有一天过不下去了,你便开始察觉开始改变。也或者说,其实你一直在改变中,对方也一直在改变中,只不过之间有道无法逾越的鸿沟,或是没有留给时间足够的耐心。

改变的过程大部分情况下是很不舒服的,时常会抱怨,会拒绝,但有时如果你能从更长的时间范围去思考你现在所处的环境下,也许就会变得从容很多,我说的时间长度超越生命。很多事物我们不能掌控,比如时间、生命、人与人之间的关系,唯一能掌控的便是我们的心灵,所以外在的事物变得不那么紧要。

最近在看和听一些关于哲学方面的书,其中有些哲学学派的理念与我现在生活的状态有些契合,并且能带来新的观念认知,接下来还需要一个比较长时间的内化过程,过段时间想整理一篇关于哲学感悟的文章。

习惯与认知

lt3

去年带给我认知升级的一本书便是《见识》,有很多更高视角的观点与态度认识;另外便是我的领导,从他身上看到了求知好知的习惯;再有便是看到了前女友的行胜于言。每一个人每一本书都有我们值得学习与借鉴的地方。

保持一颗好奇心很重要,不知道从哪里有人提到过康德的《纯粹理性批判》这本经典哲学书,又听说过王思聪大学读的是哲学这种基础学科,加上身边很多人对于哲学的态度,便想一探究竟,咨询过朋友后便从个《苏菲的世界》这本西方哲学入门书开始了,加之樊登读书会中听了很多地关于东方哲学书,有些感悟,又有些混乱,这个过程又带来了很多快乐,有时间真的要好好整理一番。

可能因为我们领导是个数学谜,所以或多或少都会受到他的一些影响,印象深刻的便是他曾问我『如何证明存在 0 这个数?』这种奇怪的问题,还有要我们新系统都是以数学家的名字命名的,这些事情也改变了我对于数学的一些看法,发现数学之美,但是却难以实践其中真髓,这里推荐《费马大定理》、《数学之美》这两本书,你会发现你之前学的只是数学,数学之外的延伸才是真正的数学之道。

还有一点必须要的说的便是『知行合一』,是王阳明心学中的主要理念。我们经常说『知易行难』,说的是知道很容易但是做起来很难,其实按照王阳明的观点来说,知行必合一,你只有知道了才会去做,如果你不去做你肯定是没有真正知道。在这里前女友践行的是『行胜于言』,她的执行力与沟通能力都要高出周围人很多,也是我所见到过的。所以我最近养成的一个习惯是,周末不要宅在家里,多出去走走,看看不同的人,学习接受新的观念与知识,扩展自己不熟悉的领域。忽然发现『读万卷书,行万里路。』与『知行合一』有种异曲同工之妙。

在路上

lt1

今天又是一个周六,没有计划出去玩,偷了一个懒觉,下午便寻得一个安静的小地方,写完这篇文章,也是对去年和最近一段时间的总结与感悟。

对了,这个安静的小地方叫『三味书屋』,至于哪三味能让我如此安静的写完文章,待我下次再来问一问。