Google 和 Facebook 为什么不用 Git 管理源码?

摘要

本文给大家剖析了一个有趣的现象:IT 业界使用*广泛的版本管理系统 Git,却不被硅谷*的科技公司 Google、Facebook 等垂青的原因。分析了 Google 的版本和分支管理模式、Git 的设计理念和存储结构,为企业 IT 的决策者提供一个版本管理系统技术选项的决策思路。

%title插图%num

背景

版本控制系统(VCS,Version Control System),或叫源码管理系统(SCM,Source Code Management)是几乎所有IT人员都熟悉和每天工作使用的工具。提到 VCS,你会想到哪个工具?估计大部分人都会想到 Git,尤其对于85后年轻一代 IT 人,甚至可能只知道 Git 这一种版本管理工具。

Git 是目前*流行的代码版本管理工具。它*初由 Linux 之父 Linus Torvalds 开发出来,Linus 2007年在某次演讲中提到他开发 Git 的几个准则:

•分布式:代码存储在多个机器、每个副本是平等的、支持离线工作•高性能:每次 commit、branch、log、diff 等操作都非常快•可靠:确保从 Git 签出(Checkout)的代码跟签入(Checkin)的代码完全一致

除了 Git,业界流行的版本控制系统还有 Subversion、Mercurial等。

%title插图%num

问题

Git 是一个非常适合开源社区的优秀的版本管理系统。但包括 Google 和 Facebooke 等多个硅谷巨头都没有采用,微软 Windows 开发团队虽然用 Git,但用的是经过深度改造后的 Git。很奇怪,对不?

其实,Google 公司并非完全没有考虑过采用 Git,Linus 本人在 2007年曾到 Google 公司进行过一次介绍 Git 的演讲,有兴趣的朋友可以参考:Linus 在 2007 年 Google Talk 上介绍 Git。当时 Google 采用一个商业软件 Perforce 作为代码版本管理工具,正为 Perforce 无法继续支持 Google 巨大代码仓库而寻觅替代方案。但*终 Google 选择了基于 Bigtable 自行研发版本管理工具 Piper,而没有采用 Linus 大神开发的、名满天下的 Git。

这到底是为什么呢?

%title插图%num

答案

Git 并不比 Subversion 更好,它只是不同

首先,让我们来看看 Git 是否(比其他流行的版本管理工具)更好,甚至*好。以 Git 市占率成为第1前*流行的 Subversion(简称 SVN)来对比。

在美版知乎网站 StackOverflow 上曾经有一个问题《Why is Git better than Subversion?》,被采纳的高赞回答是这样说的:

Git is not better than Subversion. But is also not worse. It’s different.

是的,只是 different。有哪些不同呢?从 Git 官网的介绍和 Subversion 官网的介绍可以看出来:

# Git Subversion
#1 distributed centralized
#2 branching and merging simplicity of its model and usage
#3 small and fast wide variety, from individuals to large-scale
#4 data assurance reliability
#5 open source open source

上表是 Git 和 Subversion 官网强调的几个特性,我们来分析一下:

1.分布式和中心化,Git 和 Subversion 完全不同的思路;对于开源社区(比如 Linux),显然分布式更合理,但对于商业公司,恐怕中心化更方便运维和备份;2.Git 更强调低成本的分支(和合并),Subversong 也支持分支和合并,但更强调简易的模型和易用性;3.Git 强调的是轻量和快(性能),而 Subversion 强调多用途(不仅仅是代码,还支持二进制文件)和规模;4.说法不同本质一致,强调数据的可靠性;代码是 IT 公司的核心资产,可靠性怎么强调都不为过;5.都是开源和免费的软件;

Git 非常适合开源社区

开源社区的核心诉求是开放、自由、共创,因此对版本管理系统的需求是:

1.开发者随时都可以克隆和分叉任意一个代码库;2.开发者都可以在自己的分叉或分支上为所欲为;3.开发者可以按自己意愿把源码存储在任何机器上,不论是自己的 PC 还是 Github服务器;4.开发者可以发起合并回原代码库的请求(Pull Request),而是否接受则由原项目所有者决定。

因此,Linus 设计的 Git 非常强调分布式(Distributed),以满足开源社区的代码存储自由;另外,*低成本的分支(branching)、分叉(fork)和合并(merge),满足开源社区自由分叉代码和合入的需求。

依托 Linus 本人的超级影响力,加上 Git 本身非常适合开源协作的特性,Git 几乎成为开源社区唯一的代码版本管理系统。

Git 并不特别适合企业

然而,Git 并不适合企业,尤其是企业中大型的软件系统。因为企业对源代码管理的诉求截然不同。源代码作为 IT 企业或企业的 IT 部门*核心的资产,管理需求是:

  1. 安全:包括代码权限(代码访问权限可控)和数据安全(不丢失、一致性);
  2. 易用性:简单的代码签出(Check-out)、嵌入(Check-in)、分支、合并等操作;
  3. 多种类的资源版本管理,包括源代码,也包括资源文件(图片、音乐、视频、设计文件等);
  4. 规模:支持数百 GB 甚至 TB 级规模的代码仓库,设想一个数千人的开发团队超过10年代码积累的大型软件,这个规模的代码仓库是完全可能的。

而这,恰恰就是 Google 为例的大型 IT 公司所需求的。Linus 2007年在 Google 的演讲中,2名 Google 员工提出了2个问题:

1.如果你有一个超级超级大的代码库(repository),想用 Git 来管理,还不能让业务中断6个月,你怎么做?2.使用 Git 怎么只 pull 代码库的其中一部分 path?

Linus 大神没有正面回答这2个问题。

事实是:Git 做不到。

Git 之所以无法存储巨大的代码库,也无法clone、pull、push 代码库文件树的某个分支,是由其存储结构和设计理念所制约的,并不是简单增加一个特性就可以解决的。这也是为什么 Google 员工2007年就向 Linus 提出这2个问题,但到今天为止,Git 仍然不能支持的根本原因。(后来版本的 Git 支持通过 filter 和 sparce checkout 只克隆/拉取某些目录的代码,但性能非常低)。

在 Git 对象模型里,所有对象都以 SHA-1 id 表述,包括4类对象:

  1. blob:用于存储文件数据;
  2. tree:可以理解为目录,它指向其他目录或blob;
  3. commit:一棵代码树的提交点;
  4. tag:标记特别的提交(commit),通常用于标记某次发布;

其中,tag 和 branch 只是指向 commit 的一个指针。Git 的核心存储在于前3者。其结构如下图:

%title插图%num

更详细的 Git 存储和访问机制,超出本文的范畴,关注本公众号,我将在未来分享。

源码管理系统的选型取决于研发流程

除了上面所述的考虑因素,源码管理系统的选型*重要的还是取决于研发流程,尤其是分支和版本关联

Google 的分支和版本管理原则包括:

  1. 基于充分测试的主干开发:这意味着并不需要太多分支、代码集中式存储;
  2. 基于大仓源码的主干依赖:就是把所有的模块和子工程都放在同一个代码仓库中,代码间以源码的主干版本为依赖,所以代码仓库会非常巨大。

这2点,都是 Git 无法满足的。

综上所述,Git 并不适合类似于 Google、Facebook 等采用单体代码仓库、主干开发模式的 IT 企业。因此,Google 于2007年自行开发了一套版本管理系统 Piper。可惜 Google 并没有开源出来。

*终结果:Google 在2007年前采用商业软件 Perforce 作为其源码管理系统;2007年后自行研发 Piper 替代;Facebooke 则采用经过深度改造的 Mercurial 系统。

Google 和Facebook 这2个硅谷巨头都没有采用*留下的 Git 来管理源码,根本原因在于其研发流程的核心:

  1.  主干开发;
  2. 基于大仓源码的主干依赖;

%title插图%num

总结

代码版本管理系统的技术选项,对于整个 DevOps 流程的效率和质量有着重要的影响,而且一旦选定,往往迁移成本*大。作为企业 IT 部门的决策者,务必非常审慎的做决策。建议至少从以下维度评估:

  1. 研发流程是主干开发,还是分支开发;
  2. 代码模块之间(包括对公司内部和第三方)的依赖,以制品(编译后的jar、so等二进制或字节码包)还是源代码形式;
  3. 版本发布模式:主干发布、还是分支发布;
  4. 代码的开放程度:是企业全部开放,还是需要局部开发;
  5. 代码的安全要求;

经过多维度的评估,能让企业 IT 的决策者作出更准确的决策。

服务器算力测试

文本分类的项目,有个需求是测服务器算力,*终填写表格如下:

%title插图%num

需要测试的是一秒能跑多少词,多少条文本,以及每个词和文本占用的内存和显存。
测试方式:去除模型加载等不需要的时间,采取逐步加数据量的方式,通过差值计算。

一、测试运行时间

from time import time

start = time()
for i in range(10):
print(“你要测试的代码段”)
stop = time()
print(“Stop: ” + str(stop))
print(str(stop-start) + “秒”)

二、测内存占用

memory_profiler 每行代码的内存使用情况

首先,安装所需要的模块

pip install memory_profiler
pip install psutil

##导包,函数前加装饰器
from memory_profiler import profile

@profile(precision=4,stream=open(‘memory_profiler.log’,’w+’))
def test():
pass

脚本运行方式:
运行方式不同

mprof run memory_profiler_test.py : 分析结果会保存到一个 .dat格式文件中
mprof plot :把结果以图片到方式显示出来(直接在本目录下运行此命令即可,程序会自动找出.dat文件,也可以直接打开,看具体数值) (要安装 pip install matplotlib)
mprof clean : : 清空所有 .dat文件

三、测显存

pip安装pynvml

pip install nvidia-ml-py3

这一块不是太懂,我自己的使用方式是运行我的模型,然后用再运行下面的代码脚本。

import pynvml
pynvml.nvmlInit()
# 这里的0是GPU id
handle = pynvml.nvmlDeviceGetHandleByIndex(0) # 指定GPU ID ,这里指定为0
meminfo = pynvml.nvmlDeviceGetMemoryInfo(handle)
print(meminfo.used)

*后的显示结果应该是字节。

服务器处理能力计算方法

你想建设一个能承受500万PV/每天的网站吗? 500万PV是什么概念?服务器每秒要处理多少个请求才能应对?如果计算呢?

PV是什么:
PV是page view的简写。PV是指页面的访问次数,每打开或刷新一次页面,就算做一个pv。

计算模型:
每台服务器每秒处理请求的数量=((80%*总PV量)/(24小时*60分*60秒*40%)) / 服务器数量 。
其中关键的参数是80%、40%。表示一天中有80%的请求发生在一天的40%的时间内。24小时的40%是9.6小时,有80%的请求发生一天的9.6个小时当中(很适合互联网的应用,白天请求多,晚上请求少)。

简单计算的结果:
((80%*500万)/(24小时*60分*60秒*40%))/1 = 115.7个请求/秒
((80%*100万)/(24小时*60分*60秒*40%))/1 = 23.1个请求/秒

初步结论:
现在我们在做压力测试时,就有了标准,如果你的服务器一秒能处理115.7个请求,就可以承受500万PV/每天。如果你的服务器一秒能处理23.1个请求,就可以承受100万PV/每天。

留足余量:
以上请求数量是均匀的分布在白天的9.6个小时中,但实际情况并不会这么均匀的分布,会有高峰有低谷。为了应对高峰时段,应该留一些余地,*少也要x2倍,x3倍也不为过。
115.7个请求/秒 *2倍=231.4个请求/秒
115.7个请求/秒 *3倍=347.1个请求/秒
23.1个请求/秒 *2倍=46.2个请求/秒
23.1个请求/秒 *3倍=69.3个请求/秒

*终结论:
如果你的服务器一秒能处理231.4–347.1个请求/秒,就可以应对平均500万PV/每天。
如果你的服务器一秒能处理46.2–69.3个请求,就可以应对平均100万PV/每天。

说明:
这里说明每秒N个请求,就是QPS。因为我关心的是应用程序处理业务的能力。

实际经验:
1、根据实际经验,采用两台常规配置的机架式服务器,配置是很常见的配置,例如一个4核CPU+4G内存+服务器SAS硬盘。
2、个人武断的认为在服务器CPU领域Intel的CPU要优于AMD的CPU,有反对的就反对吧,我都说我武断了(请看CPU性能比较),不要太相信AMD的广告,比较CPU性能简单办法就是比价格,不要比频率与核心数,价格相差不多的性能也相差不多。
3、硬盘的性能很重要,由其是数据库服务器。一般的服务器都配1.5万转的SAS硬盘,高级一点的可以配SSD固态硬盘,性能会更好。****重要的指标是“随机读写性能”而不是“顺序读写性能”。(本例还是配置*常见的1.5万转的SAS硬盘吧)
4、一台服务器跑Tomcat运行j2ee程序,一台服务器跑MySql数据库,程序写的中等水平(这个真的不好量化),是论坛类型的应用(总有回帖,不太容易做缓存,也无法静态化)。
5、以上软硬件情况下,是可以承受100万PV/每天的。(已留有余量应对突然的访问高峰)

注意机房的网络带宽:
有人说以上条件我都满足了,但实际性能还是达不到目标。这时请注意你对外的网络的带宽,在国内服务器便宜但带宽很贵,很可能你在机房是与大家共享一条100M的光纤,实际每个人可分到2M左右带宽。再好一点5M,再好一点双线机房10M独享,这已经很贵了(北京价格)。
一天总流量:每个页面20k字节*100万个页面/1024=19531M字节=19G字节,
19531M/9.6小时=2034M/小时=578K字节/s   如果请求是均匀分布的,需要5M(640K字节)带宽(5Mb=640KB 注意大小写,b是位,B是字节,差了8倍),但所有请求不可能是均匀分布的,当有高峰时5M带宽一定不够,X2倍就是10M带宽。10M带宽基本可以满足 要求。
以上是假设每个页面20k字节,基本不包含图片,要是包含图片就更大了,10M带宽也不能满足要求了。你自已计算吧。
(全文完)

附:性能测试基本概念
—————————————————————————————
基本概念:
Throughput(吞吐量):按照常规理解网络吞吐量表示在单位时间内通过网卡数据量之和,其中即包括本机网卡发送出去的数据量也包括本机 网卡接收到的数据量。 一个100Mb(位)的双工网卡,*大发送数据的速度是12.5M字节/s , *大接收数据的速度是12.5M字节/s, 可以 同时 收发 数据。
并发用户数:是同时执行操作的用户(线程数)。
响应时间:从请求发出到收到响应花费的时间 。

QPS – Queries Per Second  每秒处理的查询数(如果是数据库,就相当于读取)
TPS – Transactions Per Second  每秒处理的事务数(如果是数据库,就相当于写入、修改)
IOPS,每秒磁盘进行的I/O操作次数

例如对某个数据库测试,分开两次测QPS与TPS。
QPS(读取)值总是高于TPS(写、改),并且有倍率关系,因为:
1、数据库对查询可能有缓存。
2、机械硬盘或SSD硬盘的读就是比写快。
—————————————————————————————
JMeter测试参数说明:

Label:每一个测试单元的名字。

#Samples:表示一个测试单元一共发出了多少个请求。

Average:平均响应时间——默认情况下是单个 Request 的平均响应时间,当使用了 Transaction Controller 时,也可以以Transaction 为单位显示平均响应时间。,不重要。

Median:中位数,也就是 50% 用户的响应时间,如果把响应时间从小到大顺序排序,那么50%的请求的响应时间在这个范围之内。重要。

90% Line:90% 用户的响应时间,如果把响应时间从小到大顺序排序,那么90%的请求的响应时间在这个范围之内。重要 。

Min:*小响应时间,不重要。

Max:*大响应时间,出现几率只不过是千分之一甚至万分之一,不重要。

Error%:本次测试中出现错误的请求的数量

Throughput:吞吐量——默认情况下表示每秒完成的请求数(Request per Second),当使用了 Transaction Controller 时,也可以表示类似 LoadRunner 的 Transaction per Second 数

KB/Sec:每秒从服务器端接收 到的数据量(只是接收),相当于LoadRunner中的Throughput/Sec
—————————————————————————————
loadrunner测试参数说明:

响应时间: 取90%值,如果把响应时间从小到大顺序排序,那么90%的请求的响应时间在这个范围之内。重要。

每秒点击数 :hits per Second,每秒钟向服务器提交请求的数量。

TPS: Transaction per Second ,每秒事务数,一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程

Throughput(吞吐量): Loadrunner记录的Throughput是接收到服务器返回的所有字节数之和,与本地发出的字节数无关。

Throughput/Sec: 每秒的吞吐量。

对于BS架构的一般分析 响应时间、点击率、吞吐量、TPS(每秒事务数)。
对于CS架构的一般分析 TPS(每秒事务数)

如何正确查看Linux机器内存使用情况

背景

只要工作上涉及到Linux机器,基本上都会有这样一个需求,查看内存使用情况,但是怎么看才正确呢?之前使用的是top命令,一直存在一个误区。

为什么top命令看内存会有误区?

top是个很好用的系统分析工具,可以实时查看进程,cpu使用率,内存使用率等情况,有点像windows下的任务管理器。我以前一直以为top看到的就是真正的内存使用情况,后来baidugoogle好久,才发现自己图样。= =||
首先看下top命令后展示出来的内存使用情况,我用自己一台搬瓦工vps做示范:

Cpu(s):  0.0%us,  0.3%sy,  0.0%ni, 99.7%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:     73728k total,    70048k used,     3680k free,        0k buffers
Swap:    16384k total,     4696k used,    11688k free,    64716k cached

可以看到Mem: 73728k total, 70048k used, 3680k free, 0k buffers这一行,就是内存使用情况。一开始我也只看这一行。后来在公司的生产机子上看时,觉得很疑惑,只要是运行了一段时间的机子,内存的 total 和 used 总是非常的接近,free值很少,也就是说“内存使用率”非常高,哪怕我已经把各种服务都关掉了。但是就算这样,服务器也没有任何问题,再次启动 tomcat服务,做一些小的压测,表现的情况也很正常。那么问题就来了:明明内存情况这么紧张,怎么还能运行这么多服务,甚至做压力测试呢?—> 答案其实很简单,这些看起来used很多的内存中,一大部分是缓存,这就要说到Linux的内存管理机制了。

Linux中的Cache Memory

什么是Cache Memory(缓存内存):

当你读写文件的时候,Linux内核为了提高读写性能与速度,会将文件在内存中进行缓存,这部分内存就是Cache Memory(缓存内存)。即使你的程序运行结束后,Cache Memory也不会自动释放。这就会导致你在Linux系统中程序频繁读写文件后,你会发现可用物理内存会很少。
其实这缓存内存(Cache Memory)在你需要使用内存的时候会自动释放,所以你不必担心没有内存可用。
只有当 free 减去 cached 剩下的这部分内存情况紧张时,才有可能出现应用程序没有足够内存使用的情况。

所以其实刚才top看到的内存使用情况,有一部分是缓存,那个缓存有多少呢?注意看*后有个mem那行后面有个buffers ,swap那行后面有个cached,这两个就是缓存大小。所以如果要计算应用程序真正使用物理内存的情况,应该是used-cached-buffers才对,所以刚才top看到的物理内存使用情况为70048k-64716k=5332k。所以也才没用多少嘛!

另外,如果单纯想要看内存使用情况,用free命令其实更直观:

total       used       free     shared    buffers     cached
Mem:         73728      70940       2788          0          0      64840
-/+ buffers/cache:       6100      67628
Swap:        16384       4500      11884

这些信息的说明大致如下:
其中*行用全局角度描述系统使用的内存状况:
total——总物理内存
used——已使用内存,一般情况这个值会比较大,因为这个值包括了cache+应用程序使用的内存
free——完全未被使用的内存
shared——应用程序共享内存
buffers——缓存,主要用于目录方面,inode值等(ls大目录可看到这个值增加)
cached——缓存,用于已打开的文件
注意-/+ buffers/cache: 6100 67628这行。
前个值表示-buffers/cache—–>不包括缓存,应用程序物理内存使用情况,即 -buffers/cache=used-buffers-cached ,所以此时应用程序才用了6100k内存 。
后个值表示+buffers/cache—–>所有可供应用程序使用的内存大小,free加上缓存值,即+buffers/cache=free+buffers+cached ,所以此时还有67628k 内存可供程序使用。
另外,free命令也可以使用”-m” 参数,这样显示的内存信息是用MB算,而不是KB,大内存情况下,这样更直观。

free -m
-----------
             total       used       free     shared    buffers     cached
Mem:            72         69          2          0          0         63
-/+ buffers/cache:          5         66
Swap:           16          4         11

总结

使用top命令或者free命令看到的内存使用率used中,包含了缓存,如果要查看应用程序真正的内存使用情况,应该是used-cached-buffers ,或者直接看free命令结果的-/+ buffers/cache行信息。

linux系统中内存爆满之后会如何?

在使用python写程序的时候,发现一个可以无限迭代的迭代器,从而可以直接将系统中的内存占满,那么占满之后会发生什么呢?

1、 创建无限迭代,生成列表,如下:

  1. [root@python ~]# python
  2. Python 2.7.11 (default, Mar 20 2016, 14:21:08)
  3. [GCC 4.4.6 20110731 (Red Hat 4.4.6-3)] on linux2
  4. Type “help”, “copyright”, “credits” or “license” for more information.
  5. >>> import itertools
  6. >>> r = map(lambda x :x*x,itertools.count(1))

2、监控使用命令vmstat

[root@python ~]# vmstat 1 -S m  #每隔一秒采集一次信息,单位为Mb

3、系统使用情况如下

procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0      0    851     15     52    0    0     0     0  414   69 34  3 63  0  0	
 1  0      0    610     15     52    0    0     0     0  995    7 83 17  0  0  0	
 1  0      0    458     15     52    0    0     0     0  616   13 70 30  0  0  0	
 1  0      0    345     15     52    0    0     0     0  489    9 70 30  0  0  0	
 1  0      0    187     15     52    0    0     0     0  694   11 77 23  0  0  0	
 3  2     29     50      2     18    0    0   220 41400 1154  190 71 23  0  6  0	
 1  2    238     65      1     13    0    0   580 191392 1232  261 49 25  0 26  0	
 1  2    375     50      0     12    0    0  1720 134264 1360  284 64 25  0 11  0	
 0  3    452     50      0     11    0    0   768 75212  758  150 38 13  0 49  0	
 1  1    528     62      0      9    0    0   192 74668  972  134 52 18  0 30  0	
 1  1    700     50      0      9    0    0     0 167244 1084  150 55 23  0 22  0	
 0  3    767     50      0      9    0    0     0 66068  456   48 17 12  0 71  0	
 1  1    808     60      0      9    0    0  1652 39688  505  191 17  9  0 74  0	
 0  3    952     50      0      7    0    0   504 141228 1078  191 57 20  0 23  0	
 0  2   1068     58      0      7    0    0   288 113176  860  127 40 21  0 40  0	
 2  2   1136     50      0      7    0    0  1584 66656  939  279 46 17  0 37  0	
 0  4   1252     50      0      7    0    0   276 113460  679   90 31 14  0 55  0	
 0  6   1266     53      0      8    0    0   904 13332  267  116  8  4  0 88  0	
 1  0   1339     63      0      7    0    0   216 71196  957  116 43 19  0 38  0	
 0  2   1549     52      0      7    0    0     0 205896 1196  194 49 31  0 20  0	
 1  0   1666     71      0      6    0    0     0 114372  948   57 43 19  0 37  0	
 0  2   1799     51      0      5    0    0     0 130104 1101  214 57 24  0 20  0	
 1  1   1922     61      0      5    0    0     0 119856 1092  132 51 23  0 26  0	
 0  2   2002     50      0      5    0    0     0 77788  662  103 31 17  0 52  0	
 2  0   2093     51      0      5    0    0    32 89104 1054  257 49 25  0 26  0	
 0  2     16    990      0      1    0    0  3824 19836  786 1639 21 17  0 62  0	

从上面可以看出,swap使用的越来越多,free的空间越来越少,到*后达到swap的*大值之后,开始恢复内存空间。

系统的内存如下:

[root@python ~]# free -m
             total       used       free     shared    buffers     cached
Mem:           996         64        932          0          1         10
-/+ buffers/cache:         52        943
Swap:         2015         14       2001

4、 结论

当内存使用太多,没有剩余的内存的时候,操作系统会直接将进程杀死,如下所示:

  1. [root@python ~]# python
  2. Python 2.7.11 (default, Mar 20 2016, 14:21:08)
  3. [GCC 4.4.6 20110731 (Red Hat 4.4.6-3)] on linux2
  4. Type “help”, “copyright”, “credits” or “license” for more information.
  5. >>> import itertools
  6. >>> r = map(lambda x :x*x,itertools.count(1))
  7. Killed

*后一个killed表示杀死进程,开始回收空间。

iOS开发之Realm数据库的简单使用

1.安装

Realm github下载地址

如果使用cocoapods,示例语句如下
platform :ios, '8.0'
target 'Simona_Realm' do
pod 'Realm', '~> 3.0.0-beta.3'
end

2.创建简易模型,继承于RLMObject,存储在数据库的模型都需要继承于RLMObject

  1. Person.h
  2. #import <Realm/Realm.h>
  3. RLM_ARRAY_TYPE(Person);
  4. @interface Person : RLMObject
  5. @property NSString *personId;
  6. @property BOOL status;
  7. @property NSString *sex;
  8. @end
  1. Person.m
  2. #import “Person.h”
  3. @implementation Person
  4. // 设置主键,确保数据唯一性
  5. + (NSString *)primaryKey {
  6. return @”personId”;
  7. }
  8. // 设置属性不为nil
  9. + (NSArray<NSString *> *)requiredProperties {
  10. return @[@”sex”];
  11. }
  12. // 设置忽略属性
  13. + (NSArray<NSString *> *)ignoredProperties {
  14. return @[];
  15. }
  16. // 设置默认值,对于不为null的属性,默认给空
  17. + (NSDictionary *)defaultPropertyValues {
  18. return @{@”sex”:@””};
  19. }
  20. //索引属性,主要用于搜索,根据性别进行搜索
  21. + (NSArray<NSString *> *)indexedProperties {
  22. return @[@”sex”,@”woman”];
  23. }
  24. @end

关于RLMObject
1.Realm忽略了OC的属性特性(如nonatomic, atomic, strong,retain, weak,copy等),所以在声明属性时可不写,,这些特性会一直生效直到被写入数据库。
2.Realm支持以下的类型BOOL, NSInteger, long, double, CGFloat, NSString, NSDate, NSData
3.定义了RLM_ARRAY_TYPE(Person)表示支持RLMArray属性,相当于允许RLMArray<Person>属性的使用,例如:在其他属性里可@property RLMArray<Person *><Person> *personal如此使用,相当于继承关系(本文暂不做赘述)

3.使用

<1.>存储

  1. 模拟存储一万条假数据
  2. RLMRealm *realm = [RLMRealm defaultRealm];
  3. [realm transactionWithBlock:^{
  4. for (int i = 0; i < 10000; i++) {
  5. Person *person = [[Person alloc]init];
  6. person.personId = [NSString stringWithFormat:@“张%d”,i];
  7. person.sex = @“Not scanned”;
  8. person.status = YES;
  9. [realm addObject:person];
  10. }
  11. }];

这里的
[realm transactionWithBlock:^{ }];
等同于
[realm beginWriteTransaction];
[realm commitWriteTransaction];
每次打开关闭数据库时都应执行此操作

<2.>删除

  1. RLMRealm *realm = [RLMRealm defaultRealm];
  2. [realm beginWriteTransaction];
  3. [realm deleteAllObjects];
  4. [realm commitWriteTransaction];

<3.>查询

  1. 1.根据谓词查询
  2. // RLMResults相当于查询到的数组
  3. NSPredicate *pred = [NSPredicate predicateWithFormat:@”status = %ld”, NO];
  4. RLMResults *result = [Person objectsWithPredicate:pred];
  5. Person *per = result[indexPath.row];
  6. 2.根据条件查询
  7. RLMResults *result = [Person objectsWhere:@”status = ‘NO’ AND personId BEGINSWITH ‘张1′”];
  8. Person *per = result[indexPath.row];
  9. 3.查询所有
  10. RLMResults *persons = [Person allObjects];
  11. Person *per = persons[indexPath.row];

<4.>更新

  1. 1.更新数据里某一属性值
  2. NSPredicate *pred = [NSPredicate predicateWithFormat:@“personId = %@”, strScan];
  3. RLMResults *result = [Person objectsWithPredicate:pred];
  4. if (result.count != 0) {
  5. RLMRealm *r = [RLMRealm defaultRealm];
  6. [r beginWriteTransaction];
  7. //直接修改
  8. Person *person = result[0];
  9. person.sex = @“Has been scanned”;
  10. person.status = NO;
  11. [r commitWriteTransaction];
  12. }
  13. 2.创建一个新值并更新数据库,createOrUpdateInRealm:通过主键来更新插入
  14. NSPredicate *pred = [NSPredicate predicateWithFormat:@“personId = %@”, strScan];
  15. RLMResults *result = [Person objectsWithPredicate:pred];
  16. if (result.count != 0) {
  17. Person *person = [[Person alloc]init];
  18. person.personId = @“Simona1”;
  19. person.sex = @“woman”;
  20. person.status = NO;
  21. RLMRealm *r = [RLMRealm defaultRealm];
  22. [r beginWriteTransaction];
  23. [Person createOrUpdateInRealm:r withValue:person];
  24. [r commitWriteTransaction];

 

iOS字符串匹配算法2个字符串相似度(Levenshtein:莱文斯坦距离)

Levenshtein的经典算法,参考http://en.wikipedia.org/wiki/Levenshtein_distance的伪代码实现的,同时参考了一些C++的实现,求字符串相似度。

下面求出结果是0.0~100.0,   表示为0%~100%。

  1. static inline int min(int a, int b) {
  2. return a < b ? a : b;
  3. }
  4. +(float)likePercentByCompareOriginText:(NSString *)originText targetText:(NSString *)targetText{
  5. //length
  6. int n = (int)originText.length;
  7. int m = (int)targetText.length;
  8. if (n == 0 || m == 0) {
  9. return 0.0;
  10. }
  11. //Construct a matrix, need C99 support
  12. int N = n+1;
  13. int **matrix;
  14. matrix = (int **)malloc(sizeof(int *)*N);
  15. int M = m+1;
  16. for (int i = 0; i < N; i++) {
  17. matrix[i] = (int *)malloc(sizeof(int)*M);
  18. }
  19. for (int i = 0; i<N; i++) {
  20. for (int j=0; j<M; j++) {
  21. matrix[i][j]=0;
  22. }
  23. }
  24. for(int i=1; i<=n; i++) {
  25. matrix[i][0]=i;
  26. }
  27. for(int i=1; i<=m; i++) {
  28. matrix[0][i]=i;
  29. }
  30. for(int i=1;i<=n;i++)
  31. {
  32. unichar si = [originText characterAtIndex:i-1];
  33. for(int j=1;j<=m;j++)
  34. {
  35. unichar dj = [targetText characterAtIndex:j-1];
  36. int cost;
  37. if(si==dj){
  38. cost=0;
  39. }
  40. else{
  41. cost=1;
  42. }
  43. const int above = matrix[i-1][j]+1;
  44. const int left = matrix[i][j-1]+1;
  45. const int diag = matrix[i-1][j-1]+cost;
  46. matrix[i][j] = min(above, min(left,diag));
  47. }
  48. }
  49. return 100.0100.0*matrix[n][m]/MAX(m,n);
  50. }

 

iOS开发 — IQKeyboardManager使用

IQKeyboardManager的使用非常简单,只需要在项目使用到的地方之前给IQKeyboardManager的单例设置一些属性就能轻松实现。下面来介绍一下这些属性:

建议在AppDelegate的

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(nullableNSDictionary *)launchOptions{}方法中设置属性。

例如:属性设置(配置信息)

 

IQKeyboardManager *manager = [IQKeyboardManagersharedManager];
manager.enable = YES; // 控制整个功能是否启用。
manager.shouldResignOnTouchOutside =YES; // 控制点击背景是否收起键盘
manager.shouldToolbarUsesTextFieldTintColor =YES; // 控制键盘上的工具条文字颜色是否用户自定义
manager.enableAutoToolbar =YES; // 控制是否显示键盘上的工具条
manager.toolbarManageBehaviour =IQAutoToolbarByTag; // *新版的设置键盘的returnKey的关键字 ,可以点击键盘上的next键,自动跳转到下一个输入框,*后一个输入框点击完成,自动收起键盘。

在需要使用的界面设置

 

IQKeyboardReturnKeyHandler *retuenKeyHandler =        [[IQKeyboardReturnKeyHandleralloc]initWithViewController:self];
retuenKeyHandler.lastTextFieldReturnKeyType =UIReturnKeyDone; // 设置*后一个输入框的ReturnKey关键字
这样设置后就能实现输入框随着键盘的弹出自动上下浮动,点击背景收起键盘了,不用再自己实现,非常方便。

*新版的只需要在这个方法中

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(nullableNSDictionary *)launchOptions{
// 设置键盘监听管理
[[IQKeyboardManager sharedManager] setToolbarManageBehaviour:IQAutoToolbarByPosition];

就可以实现。

如果因为不知名的原因需要在某个页面禁止自动键盘处理事件相应,也很简单。

– (void) viewWillAppear: (BOOL)animated {
//打开键盘事件相应
[IQKeyboardManager sharedManager].enable = NO;
}
– (void) viewWillDisappear: (BOOL)animated {
//关闭键盘事件相应
[IQKeyboardManager sharedManager].enable = YES;

iPhone屏幕尺寸(包含7p)

px与pt区别

字体大小的设置单位,常用的有2种:px、pt。这两个有什么区别呢?

先搞清基本概念:

  • px就是表示pixel,像素,是屏幕上显示数据的*基本的点;
  • pt就是point,是印刷行业常用单位,等于1/72英寸。

px全称为pixel,是一个点,它不是自然界的长度单位,谁能说出一个“点”有多长多大么?可以画的很小,也可以很大。如果点很小,那画面就清晰,我们称它为“分辨率高”,反之,就是“分辨率低”。所以,“点”的大小是会“变”的,也称为“相对长度”。

pt全称为point,但中文不叫“点”,查金山词霸可以看到,确切的说法是一个专用的印刷单位“磅”,大小为1/72英寸。所以它是一个自然界标准的长度单位,也称为“*对长度”。

因此就有这样的说法:

  • pixel是相对大小,
  • point是*对大小。

iPhone各种屏幕分辨率

设备 屏幕尺寸 分辨率(pt) Reader 分辨率(px) 渲染后 PPI
iPhone 3GS 3.5吋 320×480 @1x 320×480 163
iPhone 4/4s 3.5吋 320×480 @2x 640×960 330
iPhone 5/5s/5c 4.0吋 320×568 @2x 640×1136 326
iPhone 6 4.7吋 375×667 @2x 750×1334 326
iPhone 6Plus 5.5吋 414×736 @3x 1242×2208 1080×1920 401
iPhone 6s 4.7吋 375×667 @2x 750×1334 326
iPhone 6sPlus 5.5吋 414×736 @3x 1242×2208 1080×1920 401
iPhone 7 4.7吋 375×667 @2x 750×1334 326
iPhone 7Plus 5.5吋 414×736 @3x 1242×2208 1080×1920 401

%title插图%num

%title插图%num