我们不能失去信仰

我们在这个世界上不停地奔跑...

0%

分析一个排序算法的执行效率

  • 最好情况、最坏情况、平均情况时间复杂度

排序算法在面对排序数组的原始情况时,会出现不同的复杂度。有的接近有序,有的完全无序,有序程度不同的数据,对于排序的执行时间肯定是有影响的。

  • 时间复杂度的系数、常数、低阶

时间复杂度反应的是数据规模n很大的时候的一个增长趋势,所以它表示的时候会忽略系数、常数、低阶。但是实际的软件开发中,我们排序的可能是10个、100个、1000个这丫昂规模很小的数据,所以在对同一阶时间复杂度的排序算法性能对比的时候,我们就要把系数、常数、低阶也考虑进来。

  • 比较次数和交换(或移动)次数

基于比较的排序算法的执行过程,会涉及两种操作,一种是元素比较大小,另一种是元素交换或移动。所以,我们在分析排序算法的执行效率的时候,应该把比较次数和交换(或移动)次数也考虑进去。

阅读全文 »

大多数编程语言中,数组要从0开始编号,而不是从1开始呢?

从数组存储的内存模型来看,“下标”最确切的定义应该是“偏移(offset)”。
如果用 a 来表示数组的首地址,a[0] 就是偏移为0的位置,也就是首地址,a[k]就表示偏移k个type_size的位置,
所以计算a[k]的内存地址只需要用这个公式:
a[k]_address = base_address + k * type_size

但是如果数组从1开始计数,那我们计算数组元素a[k]就会变成为:
a[k]_address = base_address + (k - 1) * type_size

对于以上两个公司,从1开始编号,每次随机访问数组元素都多了一次减法运算,对于 CPU 来说,就是多了一次减法指令。

另一方面,可能也是历史原因造成的,C语言设计者用0开始计数数组下标,之后的 Java、JavaScript 等高级语言都效仿了 C 语言,或者说,为了在一定程度上减少 C 语言程序员学习 Java 的成本,因此沿用了从0开始计数的习惯。

阅读全文 »

封装

对于封装的理解就是提供有限必要的方法给调用者,而不是全部让调用者自行选择,随意修改任何东西。比如在 Java 中, public 、protected、private 就可以实现对变量的访问控制,实现封装。函数也是封装。

举例: 封装的好处,比如在一个类中,不加以任何限制,调用者可以随便修改类的属性,这样不仅会增加调用者上手的难度,并且还容易出错,某些变量可能和具体业务有关,并不能随意修改。就好比一个冰箱,只给你常见的几个按键让你使用,和给你一堆按键让你使用,后者出错的概率大大增加,也会增加上手的难度。

邓庄也叫做信息隐藏或者数据访问保护。类通过暴露有限的访问接口,授权外部仅能通过类提供的方式来访问内部信息或者数据。它需要编程语言提供权限访问控制语法来支持。封装特性存在的意义,一方面是保护数据不被随意修改,提高代码的可维护性;另一方面是仅暴露有限的必要接口,提高类的易用性。

抽象

抽象就是隐藏方法的具体实现,让使用者只需要关心提供了哪些功能,不需要知道这些功能是如何实现的。抽象可以通过接口类或者抽象类方法实现。抽象存在的意义,一方面是提高代码的可扩展性、维护性,修改实现不需要改变定义,减少代码的改动返回;另一方面,它也是处理复杂系统的有效手段,能有效地过滤掉不必要关注的信息。

定义类或命名类的方法的时候,也需要有抽象思维,不要再方法定义中,暴露太多的实现细节,以保证在某个时间点需要改变方法的实现逻辑的时候,不用去修改其定义。

阅读全文 »

让 git log 的每一条记录都有价值

合作共同开发项目的大致流程是:

  1. 分配任务给每个开发者,每个开发者负责某块业务
  2. 开始开发,开发者拉取 master 分支的代码,并且创建自己的分支
  3. 某个开发者开发完成后,将自己的代码推送至自己本地分支对应的远程分支
  4. 在 GitLab 页面上进行 merge request 操作,请求合并到 测试分支如 dev 分支
  5. dev 分支测试完毕后,将 dev 分支合并至 master 分支。

稍微看一下项目的 git log 日志,会发现每个人写的 commit message 基本都不一样,还可能过一阵后自己的 commit message 自己都看不懂了。

阅读全文 »

当需要查看负载情况的时候,总要看一下cpu的硬件信息。其中常说的有物理cpu、逻辑cpu。

物理cpu 就是 cpu 的个数,一般家庭电脑都是只有一个 cpu 的。

cpu 中包含核数,通过 cpu 数量 乘以 核数 就等于逻辑cpu个数了。

可以通过查看 /proc/cpuinfo 来获取 cpu 的详细信息。

  1. 查看 cpu 个数:

grep 'physical id' /proc/cpuinfo | uniq -c | wc -l

阅读全文 »

在 MySQL 中可以使用 show processlist 来观察 sql 的执行状态。

举个例子: 我们现在查询一个很大的表,100G 左右,超过了我们的 16G 内存,那么它是怎么执行的呢?

  1. 获取一行,写到 net_buffer 中。这块内存的大小是由参数 net_buffer_length 定义的,默认16Kb。
  2. 重复获取,直到 net_buffer 写满,然后就会调用网络接口发送出去。
  3. 如果发送成功,就清空 net_buffer,然后继续取下一行,并写入 net_buffer.
  4. 如果发送函数返回对方网络栈写满了,就进入等待。直到网络栈重新可写,再继续发送。

由于MySQL 是”边读边发“的。这就意味着如果客户端接收得慢,会导致 MySQL服务端由于迟迟得不到响应,这个事务的执行时间就会变长。就是说如果客户端和服务端网络不佳的情况下,并且又对MySQL查了大量数据,就可能造成 MySQL 的性能急剧下降。

我们使用 show processlist 会看到有关的状态:

阅读全文 »

在 MySQL 中,使用 join 语句的时候会涉及到驱动表和被驱动表,MySQL 会自动为你优化,选择数据量较小的表作为驱动表。在分析性能的时候,可以使用straight_join 替换 join 可以便于我们分析执行中的性能问题。

1
select * from table1 staight_join table2 on (table1.name=table2.name)

上面这条 SQL 语句中,table1 是驱动表,table2 是被驱动表。

这条语句的执行流程是:

  1. 从表table1 中读入一行数据 R;
  2. 从数据R中,取出name字段到表table2里去查找
  3. 取出表table2中满足条件的行,跟R组成一行,放到结果集中
    阅读全文 »

Github-docker-openvpn地址

新增加一台服务器,于是就把以前的虚拟机删除了,关一台性能低的服务器(考虑到节约用电),重新配置下vpn服务,才可以轻松愉快的访问家里的服务器。

openvpn 两种模式的区别

  • tap 模式: 这种模式可以实现桥接功能,处于网络层中比较底层的地位,就相当于你直接拿网线连接上了路由器,或者你就在家里通过 wifi 连接了路由器。如果你的 vpn 服务器是直连路由器(大概意思就是你在路由器上面看到的ip 和你在你电脑上看到的内网ip 是一致的),那么远在其他地方的设备连接上了vpn,你可以在路由器上面发现一个IP,这个IP既不是wifi连接的,也不是网线连接的,而是通过 vpn 连接的。即使你不在路由器旁边,依旧可以通过 vpn 实现在同一个网段。

    这种方式比较底层,会接收到一些群发的包,可能会导致耗费流量增大。

    使用场景:这个应该是在数据链路层打通了一条通路,所以主要是用在某些需要数据链路层支持的应用。 比如 Itunes,我可以在 nas 服务器上面放很多歌曲,然后使用 itunes 去读取,并且作为歌曲的播放器,因为这种模式需要处于同一网段,不能是子网,所以需要使用这种桥接来进行实现。

  • tun模式: 相当于打通了一条隧道,拿到的ip是vpn分配的私有ip,就相当于在路由器子网中的子网,一般来说去访问家里的内网设备是没有问题的,但是如果家里的设备要反向来访问就不行了,而 tap 模式可以实现,在tap模式下甚至,我在连接 vpn 的情况下,晚上回到家里,打开家里的电脑可以直接连接公司的电脑。

    阅读全文 »