The post 开源美图 2017 08 01 appeared first on Linuxeden开源社区.
http://ift.tt/2wfv6Ir2017年7月31日星期一
从算法实现到 MiniFlow 实现,打造机器学习的基础架构平台
作者 陈迪豪
本文是大数据杂谈 7 月 21 日社群公开课分享整理,也是第四范式主题月的第三堂公开课内容。
大家好,我是第四范式的陈迪豪,目前负责先知机器学习平台的架构与实现。
今天很高兴和大家分享《打造机器学习的基础架构平台》的话题,主要会介绍机器学习底层原理和工程实现方面的内容,也欢迎大家会后多多交流。
基础架构(Infrastructure)相比于大数据、云计算、深度学习,并不是一个很火的概念,甚至很多程序员就业开始就在用 MySQL、Django、Spring、Hadoop 来开发业务逻辑,而没有真正参与过基础架构项目的开发。在机器学习领域也是类似的,借助开源的 Caffe、TensorFlow 或者 AWS、Google CloudML 就可以实现诸多业务应用,但框架或平台可能因行业的发展而流行或者衰退,而追求高可用、高性能、灵活易用的基础架构却几乎是永恒不变的。
Google 的王咏刚老师在《为什么 AI 工程师要懂一点架构》提到,研究院并不能只懂算法,算法实现不等于问题解决,问题解决不等于现场问题解决,架构知识是工程师进行高效团队协作的共同语言。Google 依靠强大的基础架构能力让 AI 研究领先于业界,工业界的发展也让深度学习、Auto Machine Learning 成为可能,未来将有更多人关注底层的架构与设计。
因此,今天的主题就是介绍机器学习的基础架构,包括以下的几个方面:
- 基础架构的分层设计;
- 机器学习的数值计算;
- TensorFlow 的重新实现;
- 分布式机器学习平台的设计。
第一部分,基础架构的分层设计
大家想象一下,如果我们在 AWS 上使用编写一个 TensorFlow 应用,究竟经过了多少层应用抽象?首先,物理服务器和网络宽带就不必说了,通过 TCP/IP 等协议的抽象,我们直接在 AWS 虚拟机上操作就和本地操作没有区别。其次,操作系统和编程语言的抽象,让我们可以不感知底层内存物理地址和读写磁盘的 System call,而只需要遵循 Python 规范编写代码即可。然后,我们使用了 TensorFlow 计算库,实际上我们只需调用最上层的 Python API,底层是经过了 Protobuf 序列化和 swig 进行跨语言调研,然后通过 gRPC 或者 RDMA 进行通信,而最底层这是调用 Eigen 或者 CUDA 库进行矩阵运算。
因此,为了实现软件间的解耦和抽象,系统架构常常采用分层架构,通过分层来屏蔽底层实现细节,而每一个底层都相当于上层应用的基础架构。
那么我们如何在一个分层的世界中夹缝生存?
有人可能认为,既然有人实现了操作系统和编程语言,那么我们还需要关注底层的实现细节吗?这个问题没有标准答案,不同的人在不同的时期会有不同的感受,下面我举两个例子。
在《为了 1% 情形,牺牲 99% 情形下的性能:蜗牛般的 Python 深拷贝》这篇文章中,作者介绍了 Python 标准库中 copy.deep_copy() 的实现,1% 的情况是指在深拷贝时对象内部有可能存在引用自身的对象,因此需要在拷贝时记录所有拷贝过的对象信息,而 99% 的场景下对象并不会直接应用自身,为了兼容 100% 的情况这个库损失了 6 倍以上的性能。在深入了解 Python 源码后,我们可以通过实现深拷贝算法来解决上述性能问题,从而优化我们的业务逻辑。
另一个例子是阿里的杨军老师在 Strata Data Conference 分享的《Pluto: 一款分布式异构深度学习框架》,里面介绍到基于 TensorFlow 的 control_dependencies 来实现冷热数据在 GPU 显存上的置入置出,从而在用户几乎不感知的情况下极大降低了显存的使用量。了解源码的人可能发现了,TensorFlow 的 Dynamic computation graph,也就是 tensorflow/fold 项目,也是基于 control_dependencies 实现的,能在声明式机器学习框架中实现动态计算图也是不太容易。这两种实现都不存在 TensorFlow 的官方文档中,只有对源码有足够深入的了解才可能在功能和性能上有巨大的突破,因此如果你是企业内 TensorFlow 框架的基础架构维护者,突破 TensorFlow 的 Python API 抽象层是非常有必要的。
大家在应用机器学习时,不知不觉已经使用了很多基础架构的抽象,其中最重要的莫过于机器学习算法本身的实现,接下来我们将突破抽象,深入了解底层的实现原理。
第二部分,机器学习的数值计算
机器学习,本质上是一系列的数值计算,因此 TensorFlow 定位也不是一个深度学习库,而是一个数值计算库。当我们听到了香农熵、贝叶斯、反向传播这些概念时,并不需要担心,这些都是数学,而且可以通过计算机编程实现的。
接触过机器学习的都知道 LR,一般是指逻辑回归(Logistic regression),也可以指线性回归(Linear regression),而前者属于分类算法,后者属于回归算法。两种 LR 都有一些可以调优的超参数,例如训练轮数(Epoch number)、学习率(Learning rate)、优化器(Optimizer)等,通过实现这个算法可以帮忙我们理解其原理和调优技巧。
下面是一个最简单的线性回归 Python 实现,模型是简单的 y = w * x + b。
(点击放大图像)
从这个例子大家可以看到,实现一个机器学习算法并不依赖于 Scikit-learn 或者 TensorFlow 等类库,本质上都是数值运算,不同语言实现会有性能差异而已。细心的朋友可能发现,为什么这里 w 的梯度(Gradient)是 -2 * x * (y – x * x –b),而 b 的梯度这是 -2 * (y – w * x – b),如何保证经过计算后 Loss 下降而准确率上升?这就是数学上保证了,我们定义了 Loss 函数(Mean square error)为 y – w * x – b 的平方,也就是说预测值越接近 y 的话 Loss 越小,目标变成求 Loss 函数在 w 和 b 的任意取值下的最小值,因此对 w 和 b 求偏导后就得到上面两条公式。
如果感兴趣,不妨看一下线性回归下 MSE 求偏导的数学公式证明。
(点击放大图像)
逻辑回归与线性回归类似,当由于是分类问题,因此需要对 w * x + b 的预测结果进行归一化(Normalization),一般使用 Sigmoid 方法,在 Python 中可以通过 1.0 / (1 + numpy.exp(-x)) 这种方式实现。由于预测值不同,Loss 函数的定义也不同,求偏导得到的数值计算公式也不同,感兴趣也可以看看我的公式推导。
(点击放大图像)
(点击放大图像)
大家可以看到最终求得的偏导是非常简单的,用任何编程语言都可以轻易实现。但我们自己的实现未必是最高效的,为什么不直接用 Scikit-learn、MXNet 这些开源库已经实现好的算法呢?
我们对这个算法的理解,其实是在工程上使用它的一个很重要的基础。例如在真实的业务场景下,一个样本的特征可能有百亿甚至千亿维,而通过前面的算法我们了解到,LR 模型的大小和样本特征的维度是相同的,也就是说一个接受百亿维特征的模型,本身参数就有百亿个,如果使用标准的双精度浮点数保存模型参数,那么百亿维的模型参数部分至少要超过 40G,那么千亿维的特征更是单机所无法加载的。
因此,虽然 Scikit-learn 通过 native 接口实现了高性能的 LR 算法,但只能满足在单机上训练,而 MXNet 由于原生没有支持 SpareTensor,对于超高维度的稀疏数据训练效率是非常低的,TensorFlow 本身支持 SpareTensor 也支持模型并行,可以支持百亿维特征的模型训练,但没有针对 LR 优化效率也不是很高。在这种场景下,第四范式基于 Parameter server 实现了支持模型并行和数据并行的超高维度、高性能机器学习库,在此基础上的大规模 LR、GBDT 等算法训练效率才能满足工程上的需求。
机器学习还有很多有意思的算法,例如决策树、SVM、神经网络、朴素贝叶斯等等,只需要部分数学理论基础就可以轻易在工程上实现,由于篇幅关系这里就不在赘述了。前面我们介绍的其实是机器学习中的命令式(Imperative)编程接口,我们把求偏导的公式提前推导出来,然后像其他编程脚本一样根据代码那顺序执行,而我们知道 TensorFlow 提供的是一种声明式(Declarative)的编程接口,通过描述计算图的方式来延后和优化执行过程,接下来我们就介绍这方面的内容。
第三部分,TensorFlow 的重新实现
首先大家可能有疑问,我们需要需要重新实现 TensorFlow?TensorFlow 灵活的编程接口、基于 Eigen 和 CUDA 的高性能计算、支持分布式和 Hadoop HDFS 集成,这些都是个人甚至企业很难完全追赶实现的,而且即使需要命令式编程接口我们也可以使用 MXNet,并没有强需求需要一个新的 TensorFlow 框架。
事实上,我个人在学习 TensorFlow 过程中,通过实现一个 TensorFlow-like 的项目,不仅惊叹与其源码和接口的设计精巧,也加深了对声明式编程、DAG 实现、自动求偏导、反向传播等概念的理解。甚至在 Benchmark 测试中发现,纯 Python 实现的项目在线性回归模型训练中比 TensorFlow 快 22 倍,当然这是在特定场景下压测得到的结果,主要原因是 TensorFlow 中存在 Python 与 C++ 跨语言的切换开销。
这个项目就是 MiniFlow,一个实现了链式法则、自动求导、支持命令式编程和声明式编程的数值计算库,并且兼容 TensorFlow Python API。感兴趣可以在 这个地址 参与开发,下面是两者 API 对比图。
(点击放大图像)
了解 TensorFlow 和 MXNet(或者 NNVM)源码的朋友可能知道,两者都抽象了 Op、Graph、Placeholer、Variable 等概念,通过 DAG 的方式描述模型的计算流图,因此我们也需要实现类似的功能接口。
与前面的 LR 代码不同,基于 Graph 的模型允许用户自定义 Loss 函数,也就是用户可以使用传统的 Mean square error,也可以自定义一个任意的数学公式作为 Loss 函数,这要求框架本身能够实现自动求导的功能,而不是我们根据 Loss 函数预先实现了导数的计算方式。
那么用户可以定义的最小操作,也就是 Op,需要平台实现基本的算子,例如 ConstantOp、AddOp、MultipleOp 等,而且用户实现自定义算子时可以加入自动求导的流程中,并不影响框架本身的训练流程。参考 TensorFlow 的 Python 源码,下面我们定义了 Op 的基类,所有的 Op 都应该实现 forward() 和 grad() 以便于模型训练时自动求导,而且通过重载 Python 操作符可以为开发者提供更便利的使用接口。
(点击放大图像)
那么对于常量(ConstantOp)和变量(VariableOp),他们的正向运算就是得到的是本身的值,而求导时常量的导数为 0,求偏导的变量导数为 1,其他变量也为 0,具体代码如下。
(点击放大图像)
其实更重要的是,我们需要实现加(AddOp)、减(MinusOp)、乘(MultipleOp)、除(DivideOp)、平方(PowerOp)等算子的正向运算和反向运算逻辑,然后根据链式法则,任何复杂的数学公式求导都可以简化成这些基本算子的求导。
例如加法和减法,我们知道两个数加法的导数等于导数的加法,因此根据此数学原理,我们可以很容易实现 AddOp,而 MinusOp 实现类似就不赘述了。
(点击放大图像)
而乘法和除法相对复杂,显然两个数乘法的导数不等于导数的乘法,例如 x 和 x 的平方,先导数后相乘得到 2x,先相乘后导数得到 3 倍 x 的平方。因此这是需要使用乘数法则,基本公式是,而代码实现如下。
(点击放大图像)
除法和平方的求导方式也是类似的,因为数学上已经证明,所以只需要编码实现基本的正向和反向运算即可。由于篇幅有限,这里不再细致介绍 MiniFlow 的源码实现了,感兴趣可以通过上面的 Github 链接找到完整的源码实现,下面再提供使用相同 API 接口实现的模型性能测试结果,对于小批量数据处理、需要频繁切换 Python/C++ 运行环境的场景下 MiniFlow 会有更好的性能表现。
(点击放大图像)
前面介绍了机器学习算法和深度学习类库的实现,并不是所有人都有能力去重写或者优化这部分基础架构的,很多时候我们都只是这些算法的使用者,但从另一个角度,我们就需要维护一个高可用的计算平台来做机器学习的训练和预测,下面将从这方面介绍如何打造分布式机器学习平台。
第四部分,分布式机器学习平台的设计
随着大数据和云计算的发展,实现一个高可用、分布式的机器学习平台成为一个基本需求。无论是 Caffe、TensorFlow,还是我们自研的高性能机器学习库,都只是解决数值计算、算法实现以及模型训练的问题,对于任务的隔离、调度、Failover 都需要上层平台实现。
那么设计一个针对机器学习全流程的基础架构平台,需要涵盖哪些功能呢?
首先,必须实现资源隔离。在一个共享底层计算资源的集群中,用户提交的训练任务不应该受到其他任务的影响,尽可能保证 CPU、内存、GPU 等资源隔离。如果使用 Hadoop 或 Spark 集群,默认就会在任务进程上挂载 cgroups,保证 CPU 和内存的隔离,而随着 Docker 等容器技术的成熟,我们也可以使用 Kubernetes、Mesos 等项目来启动和管理用户实现的模型训练任务。
其次,实现资源调度和共享。随着通用计算的 GPU 流行,目前支持 GPU 调度的编排工具也越来越多,而部分企业内还存在着 GPU 专卡专用的情况,无法实现资源的动态调度和共享,这必然导致计算资源的严重浪费。在设计机器学习平台时,需要尽可能考虑通用的集群共享场景,例如同时支持模型训练、模型存储以及模型服务等功能,可以对标的典例就是 Google Borg 系统。
然后,平台需要有灵活的兼容性。目前机器学习业务发展迅速,针对不同场景的机器学习框架也越来越多,灵活的平台架构可以兼容几乎所有主流的应用框架,避免基础架构因为业务的发展而频繁变化。目前 Docker 是一种非常合适的容器格式规范,通过编写 Dockerfile 就可以描述框架的运行环境和系统依赖,在此基础上我们可以在平台上实现了 TensorFlow、MXNet、Theano、CNTK、Torch、Caffe、Keras、Scikit-learn、XGBoost、PaddlePaddle、Gym、Neon、Chainer、PyTorch、Deeplearning4j、Lasagne、Dsstne、H2O、GraphLab 以及 MiniFlow 等框架的集成。
最后,需要实现机器学习场景下的 API 服务。针对机器学习的模型开发、模型训练和模型服务三个主要流程,我们可以定义提交训练任务、创建开发环境、启动模型服务、提交离线预测任务等 API,用熟悉的编程语言来实现 Web service 接口。要实现一个 Google-like 的云深度学习平台,大家可以参考下面这三个步骤。
(点击放大图像)
当然,要实现一个涵盖数据引入、数据处理、特征工程以及模型评估功能的机器学习平台,我们还需要集成 HDFS、Spark、Hive 等大数据处理工具,实现类似 Azkaban、Oozie 的工作流管理工具,在易用性、低门槛方面做更多的工作。
总结
最后总结一下,机器学习的基础架构包含了机器学习算法、机器学习类库以及机器学习平台等多个层次的内容。根据业务的需求,我们可以选择特定的领域进行深入研究和二次开发,利用轮子和根据需求改造轮子同样重要。
在机器学习与人工智能非常流行的今天,希望大家也可以重视底层基础架构,算法研究员可以 理解更多工程的设计与实现,而研发工程师可以了解更多的算法原理与优化,在合适的基础架构平台上让机器学习发挥更大的效益,真正应用的实际场景中。
今天分享的内容到这里了,非常感谢大家
答疑环节
Q 1:老师您好! 我的问题是 基础架构在具体落地方面 有什么建议?比如在云上部署和虚拟化容器技术的使用?
陈迪豪: 基础架构其实包含多层次的内容,如果在云端部署,可以考虑使用 AWS 或者 Google CloudML 等基础服务,也可以基于 Kubernetes、TensorFlow 等开源项目这内部搭建机器学习平台,参考前面图片提到的三个步骤,只需要实现简单的 API 服务和容器调度任务即可。
Q 2:老师好,接触(机器学习)之前需要深入学习 spark 吗?
陈迪豪: 机器学习算法本身并依赖 Spark,我们可以自己实现基本的算法,也可以使用 Scikit-learn、TensorFlow 等开源库。当然在大部分业务场景中,我们还是需要 Hadoop、Spark 等大数据框架进行数据处理、特征抽取等功能,因此掌握一定的大数据处理能力也是很有价值的。
Q 3:我有个问题,怎样做到线下模型效果评估的自动化?
陈迪豪: 这是个好问题,在线下我们一般会对测试数据集计算 AUC 等离线指标,来预估模型的效果,自动化方面我们有一套自学习的流程,大家也可以使用 Crontab 或者 Azkaban 等任务管理工具,对于新的测试集进行模型评估,这是纯工程的问题了可以结合已有的服务架构来实现。
Q 4:文中介绍的机器学习基础架构平台的搭建思路,是否就是先知平台的架构思路?先知平台是 SaaS 式的服务平台,如果我是想搭建公司内部的机器学习架构平台,思路也是跟文中描述一样还是说有什么差异?
陈迪豪: 先知平台的底层也是分布式、高可用的基础架构,也是这个思路,不过在易用性、低门槛方面做了更多的工作。目前先知提供 SaaS 公有云版本可以很方便得注册使用,如果是企业内部搭建,先知平台也有企业版私有云可以单独部署,如果需要自己维护和搭建机器学习平台,可以考虑基于 Kubernetes 的容器调度集群,可以通过管理 CPU、GPU 等异构资源,通过制作 Docker 镜像的方式来支持各种机器学习框架的使用,和前面提到的思路类似。
Q 5:对于一般的公司数据规模没那么大,是不是一个高配的机器 +4 个 GPU 卡,安装一个 tensorflow 就可以了?
陈迪豪: 对于规模比较小的公司,可以直接使用单机多卡的方式,包括 BAT 等大企业有的部门也是直接使用裸机的,但裸机带来的问题是没有资源管理和任务调度,使用者之间需要互相约定使用时间和使用的资源,在一定程度上造成资源浪费,如果规模大了建议使用统一的集群调度方式,可以保证任务间的资源隔离和正常执行。
Q 6:正如老师在群里上课提的,在工程上,在真实的业务场景下,一个样本的特征是百亿甚至千亿,在线下的时候,训练好一个模型,部署在服务器端。那么,在线服务阶段,也就是在线服务的预测阶段的时候,由于发起预测请求的样本特征也比较大,那么请问在实际工程中这些数据是怎么存储的?
陈迪豪: 在一个百亿甚至是千亿维的模型上,我们一般通过分 shard 的方式来切分,模型本身可以通过多个 Parameter server 实例来保存,而客户端发送预测请求时,由于特征是非常稀疏的,实际有值的数据非常少,因此并不需要把大部分零值数据也请求到服务端,具体格式大家也可以参考下 TensorFlow 中 SparseTensor 的实现。
Q 7:请问先知目前平台的算法是基于开源算法封装还是自己实现的呢?
陈迪豪: 目前先知平台主要针对真实的应用场景,自主研发的大规模 LR、GBDT、HE-TreeNet、LFC、SVM、Feature-go 等算法都集成到先知平台上,当然平台也支持 Spark MLlib、TensorFlow 等开源框架的算法实现。
作者介绍
陈迪豪, 第四范式先知平台架构师,曾在小米科技和 UnitedStack 担任基础架构研发工程师。活跃于 OpenStack、Kubernetes、TensorFlow 等开源社区,实现了 Cloud Machine Learning 云深度学习平台,Github 账号 。
转自 http://ift.tt/2tSXcsm
The post 从算法实现到 MiniFlow 实现,打造机器学习的基础架构平台 appeared first on Linuxeden开源社区.
http://ift.tt/2vgYNvo测试人员的 GitHub

作者 Ben Linders ,译者 薛命灯
当与开发人员谈起版本控制时,你一般会听到他们说,Git 是一种工作流工具,而 GitHub 是一个存放代码和个人简历的地方。而对于测试人员或业务分析人员来说,Git 是启动构建和产生缺陷的神秘之源。测试人员也应该来使用 GitHub,不管是出于个人目的还是项目的需要,并成为现有项目的贡献者。
来自 Excelon 的总经理 Matt Heusser 和来自 Laurel & Wolf 的测试工程师 Chris Kenst 在 2017 Spring 在线测试大会 上呈现了有关 成为 GitHub 贡献者 的演讲。
Heusser 和 Kenst 以如何创建 GitHub 账号作为开场。因为他们的演讲视频可以重复播放,所以观众可以重复观看视频内容,并在观看过程中暂停视频,然后照着视频中的命令进行操作。
在演讲结束后,InfoQ 采访了 Heusser 和 Kenst,谈论如何在代码之外使用 GitHub、测试人员如何从 GitHub 中得获益,以及如何鼓励测试人员使用 GitHub 和其他开源工具。
InfoQ:你们提到说,GitHub 不仅仅可以用于存放代码,还有其他很多用处。你们能举一些例子吗?
Matt Heusser: 当然。任何一个测试相关的资源——测试计划、会议纪要、简明指南都可以放到版本控制系统里。而规范文件(假设它们是 HTML 或 MS Word 格式的)可以放到 GitHub 上。
Chris Kenst: 探索性测试章程(exploratory charter)、清单、速查手册、数据,甚至是 Reddit 风格的问答(Ask Me Anything,AMA)。我们还能通过 GitHub 页面来创建网站(测试人员应该给自己创建在线资料),并在上面撰写和发布书本作品。只有你想不到的,没有你做不到的!
示例可以激发创造力,所以我举了一些例子。下面是我创建过的测试资源,它们都很有用。第一个是关于如何使用 Elisabeth Hendrickson 所倡导的格式来编写探索性测试章程的描述和示例。第三个是一个字符串清单,你可以把它们用在基于字符串的输入字段上,有助于你找出系统的错误,很有意思!
有很多例子说明如何通过 GitHub 页面来创建一个网站(对于测试人员来说,这是一个很好的选择)。第一个地址是 GitHub 产品经理 Ben Balter 的个人主页和博客。第二个地址是一个非常简单的个人主页,上面汇聚了很多个链接,它们分别链接到不同的社交媒体网站。GitHub 有个很有意思的地方,如果你喜欢什么,你就为它拉取一个分支,然后做一些修改,把它变成你自己的!第三个地址是一个由社区驱动的开源网站,包含了软件测试大会和研讨会的信息。如果你想成为 GitHub 的贡献者,可以看看这个: 成为测试大会的贡献者 。
InfoQ:如何使用 GitHub 来处理非代码对象?可以在这些对象上使用 GitHub 所有的功能吗?
Kenst: 所有的项目享受同等待遇。在创建仓库后,你可以使用 GitHub 所有的主要功能,包括版本控制、问题管理、Wiki,等等。即使是非代码对象,你也可以提交拉取请求并作出增量性的变更。
Heusser: 是的。除了二进制文件的合并和冲突处理,其他的都是一样的。不过,类似从两个分支中选择最新版本的操作对于 MS Word 文档来说是行不通的。
InfoQ:测试人员能够从 GitHub 中获得哪些好处?
Heusser: 首先,你可以了解到开发人员的工作流,然后使用他们喜欢的方式与他们进行沟通。这样会减少测试人员与开发人员之间的摩擦。其次,你离构建资源和代码更近了。如果你看得懂代码,你就可以在代码级别找出一些错误。这样会提升你阅读代码的能力,或许还能参与到代码审查过程当中。Chris 一定会说,你也可以使用 GitHub 来组织自己的工作流——审批推送请求、记录缺陷,等等。另外,拥有一个 GitHub 账号并成为一个贡献者可以让你更容易找到工作,而且对于客户来说风险更小。
Kenst:没错。不管是盈利组织还是非盈利组织(比如 Selenium 项目),他们都在使用 GitHub 来托管他们的代码。
首先,这意味着软件人才会越来越多地使用 GitHub。代码变更是通过拉取请求来提交的,测试人员和开发人员可以看到代码的变更情况,在合并到生产环境之前,可以分别对它们进行测试。在系统的各个层面进行自动化测试给团队带来了优势。就像 Matt 说的那样,测试人员也可以参与到代码审查过程当中,并通过 GitHub Issues 来提交缺陷。
其次,这意味着组织和招聘者会越来越多地通过 GitHub 账号来物色候选人。如果有了 GitHub 上能够显示技术能力的东西,为什么还要去看简历呢?尽管这些东西可能会包含一些无关紧要的项目或非日常工作相关的资料,但它们都比单纯的简历要来得有价值。
InfoQ:你们在大会上演示了如何注册一个 GitHub 账号和启动一个项目,然后做了相应的解释。你们为什么要采取这样的方式呢?
Kenst: 我们演示的主要目的是要确保观众能够真正地了解如何创建一个 GitHub 仓库并成为一个贡献者。基于这个原因和时间上的限制,我们决定先做演示,然后再说明一些背景信息和额外的例子。
Heusser: 在开始的时候,演示花了很长的时间,而额外的一些补充花的时间更长,我们担心会超时。不过我们还是紧凑地完成了任务。
InfoQ:人们应该如何鼓励测试人员使用像 GitHub 这样的开源工具?你们有什么建议吗?
Heusser: 这个问题问得很好。说实话,我听到很多领导或教练说一些“放手去做”之类的话,他们还质疑“为什么测试人员用不了新工具”。我建议让测试人员结成对,让他们使用新工具来完成工作,不管是使用 GitHub 还是 Selenium,或者其他的什么。你可能会觉得这比你想象得要困难一些,需要一些时间和帮助,又或者你觉得这个很容易,结对有助于减小阻力。不管是哪一种情况,你都赢了!
这里有一些给实践者的建议:启动一个项目,学习一些编码招式(katas)。对于编程语言来说,通过 Google 搜索到的建议与深度的专业知识之间是有很大区别的。一些技巧经过反复练习就会变成专业技能。从给我的 katas 仓库 拉取分支开始,然后使用 katagenerator.rb 创建你自己的招式。其中有一个转化器是最简单的,它将罗马数字转换成小数。做一些修改,然后提交,你就知道该如何向一个项目提交代码了。
将来如果你需要其他软件,却因预算问题而烦恼时,可以考虑寻找开源的项目。例如,Selenium 可以用来替换 UFT。找到这个项目,看看能不能加入其中,或者仅仅是报告一些缺陷。报告缺陷是 Git 的另一个工作流,学习起来非常简单,而且开发者会因此而感激你!
Kenst: 在软件测试工具方面,过去十年的最大进展来自开源社区。Selenium 成为 Web 测试的标准(并即将成为 W3C 标准),Appium 似乎也会成为移动自动化测试的标准。我们所使用的那些优秀的浏览器和浏览器工具都得益于开源社区(如 Chrome)。作为最流行的版本控制系统之一的 Git 也是开源的。
为了保持与时俱进,测试人员需要关注这些项目(以及其他相关的项目),而这些项目都在 GitHub 上。
查看英文原文 : GitHub for Testers
转自 http://ift.tt/2w00MSV
The post 测试人员的 GitHub appeared first on Linuxeden开源社区.
http://ift.tt/2uPdMfFTor 项目联合创始人称,暗网并不真正存在
在拉斯维加斯举行的 DEF CON 会议上,Tor 项目联合创始人 Roger Dingledine 试图纠正有关匿名网络的错误观念。他指出,只有 3% 的 Tor 用户访问 Tor 隐藏服务,绝大部分用户是出于完全合法的目的通过 Tor 访问公共网站。这些网民只是想隐藏身份,他们不是什么黑社会恶棍。
他告诉出席 DEF CON 会议的与会者,暗网并不真正存在,它们只是少许网页罢了。Tor 用户访问最多的网站是 Facebook,有超过一百万人通过 Tor 登录 Facebook。一百万,这只是 Facebook 十亿用户群中的一小部分,但对于像 Tor 这样的项目来说却非常重要。
他还解释了 Tor 浏览器继续使用 Firefox 的原因,称 Chrome 的代理绕过仍然引起担忧。
Roger Dingledine 还表示目前项目最需要的是 Windows 开发者,他说大多数 Tor 项目的员工都是 Linux 用户,但 Tor 项目的用户大多数是 Windows 平台上的用户,所以他们需要熟悉微软操作系统的开发者。
来自:The Register
转自 http://ift.tt/2hilBFI
The post Tor 项目联合创始人称,暗网并不真正存在 appeared first on Linuxeden开源社区.
http://ift.tt/2f3giJQGhost 1.2.0 发布,现代在线内容专业发布平台
Ghost 1.2.0 已发布,Ghost 是一个简单而强大的开源内容发布平台, 用于构建和运行现代在线出版物,旨在改变改变在线新闻的现状。
Ghost 的编辑环境基于 Markdown ,所有已发布的内容都会存放在一个易于搜索的存档中,可供快速查找。且使用简单,添加 logo、创建导航菜单和注入分析代码通过几次点击就可以完成。
更新内容:
- Primary tag feature
- Fix empty zip when downloading symlinked theme
- Fix transfer ownership so setup can’t be run multiple times
- Improved SEO meta titles for paginated archives
- Bump core ember dependencies
下载地址:
转自 http://ift.tt/2hitPO3
The post Ghost 1.2.0 发布,现代在线内容专业发布平台 appeared first on Linuxeden开源社区.
http://ift.tt/2f3gbOqHighcharts 5.0.14 发布,纯 Javascript 图表库
Highcharts 5.0.14 已发布。Highcharts 是一个基于 SVG 的 JavaScript 图表库,使开发人员可以轻松地在其网页中设置交互式图表,开源,非商业使用免费 。
更新内容:
- Fixed #7014, a regression causing JS error when writing a new chart to a node that was originally detached.
- Fixed JS error on inverted, bosted charts.
下载地址:
转自 http://ift.tt/2uecg35
The post Highcharts 5.0.14 发布,纯 Javascript 图表库 appeared first on Linuxeden开源社区.
http://ift.tt/2tY72NGMythTV 29.0 发布,开源数字视频录像机
MythTV 29.0 已发布,MythTV 是一个数字录像机和家庭媒体中心。
新功能:
- 后台现在监听所有地址
- 添加一个新的 MythTV 启动页面
主要变化
- mythtv-setup 现在使用 MythUI
- 支持 IPV6 link-local 地址
- 改进 Bluray 覆盖处理
- 同步 FFmpeg 到 3.2
先决条件更改
- 增加:VBOX 最小固件版本为 2.50
- 删除:对 libmp3lame 的依赖
下载地址:
转自 http://ift.tt/2f2zcAw
The post MythTV 29.0 发布,开源数字视频录像机 appeared first on Linuxeden开源社区.
http://ift.tt/2hivRxGRails 5.0.5 发布,开源网络应用框架
Rails 5.0.5 已发布,Ruby on Rails 是一个用于开发数据库驱动的网络应用程序的完整框架。Rails 基于 MVC(模型- 视图- 控制器)设计模式。从视图中的 Ajax 应用,到控制器中的访问请求和反馈,到封装数据库的模型,Rails 为你提供一个纯 Ruby 的开发环境。发布网站时,你只需要一个数据库和一个网络服务器即可。
自 5.0.4 以来发生的变化
要查看各个 gem 的更改,请阅读更改日志:
- Action Cable CHANGELOG
- Action Mailer CHANGELOG
- Action Pack CHANGELOG
- Action View CHANGELOG
- Active Job CHANGELOG
- Active Model CHANGELOG
- Active Record CHANGELOG
- Active Support CHANGELOG
- Railties CHANGELOG
要查看完整的更改列表,请查看 所有提交 。
转自 http://ift.tt/2hivEKU
The post Rails 5.0.5 发布,开源网络应用框架 appeared first on Linuxeden开源社区.
http://ift.tt/2f2zdo4每日文章精选 2017 07 31

维基解密再曝三款 CIA 工具:原被用于攻击 Mac/Linux
如何在主流 JavaScript 框架中做选择?横向对比 Vue, React, AngularJS, and Angular2
The post 每日文章精选 2017 07 31 appeared first on Linuxeden开源社区.
http://ift.tt/2hg7jWoMySQL 死锁与日志二三事
最近线上 MySQL 接连发生了几起数据异常,都是在凌晨爆发,由于业务场景属于典型的数据仓库型应用,白天压力较小无法复现。甚至有些异常还比较诡异,最后 root cause 分析颇费周折。那实际业务当中咱们如何能快速的定位线上 MySQL 问题,修复异常呢?下文我会根据两个实际 case,分享下相关的经验与方法。
1、Case1:部分数据更新失败
某天渠道同学反馈某报表极个别渠道数据为 0,大部分渠道数据正常。这个数据是由一个统计程序每天凌晨例行更新的,按理来说,要么全部正常,要么全部失败,那会是什么原因导致极个别数据异常呢?
首先我们能想到的自然是根据统计任务日志来看了,但是看了统计程序打印的日志没有发现诸如 SQL update 失败的异常描述,那当时的数据库究竟发生了什么呢?在查看 MySQL-server 日志之前,习惯性的看了下数据库状态:
1
|
SHOW ENGINE INNODB STATUS\G
|
恰好看到了凌晨这个 update 发生了死锁:
*** (2) TRANSACTION:
TRANSACTION 215208474, ACTIVE 0 sec fetching rows
mysql tables in use 3, locked 3
6 lock struct(s), heap size 1136, 7 row lock(s)
MySQL thread id 27844825, OS thread handle 140109890225920, query id 412503669 10.135.6.41 zeye Searching rows for update
update t_channel_final_datas set nr_register=24,nr_add_goods=32,nr_order_normal=0,nr_pay_normal=0,nr_order_special=0,nr_pay_special=0,n_add_user_num=11 where count_date=’2017-07-16′ and channel_id=’114′ and channel_type=’10’ and terminal=’116′
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 464 page no 5459 n bits 392 index index_countdate_type_terminal of table `db_zz_flow`.`t_channel_final_datas` trx id 215208474 lock_mode X locks rec but not gap
Record lock, heap no 304 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 10; hex 323031372d30372d3136; asc 2017-07-16;;
1: len 1; hex 30; asc 0;;
2: len 4; hex 80000010; asc ;;
3: len 4; hex 8009055e; asc ^;;
…
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 464 page no 4743 n bits 264 index PRIMARY of table `db_zz_flow`.`t_channel_final_datas` trx id 215208474 lock_mode X locks rec but not gap waiting
Record lock, heap no 168 PHYSICAL RECORD: n_fields 32; compact format; info bits 0
0: len 4; hex 80090569; asc i;;
1: len 6; hex 00000cd3b9d0; asc ;;
…
*** WE ROLL BACK TRANSACTION (1)
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
34
35
36
37
38
39
40
41
42
|
————————————
LATEST DETECTED DEADLOCK
————————————
2017–07–17 04:09:01 0x7f6de03c8700
*** (1) TRANSACTION:
TRANSACTION 215208479, ACTIVE 0 sec fetching rows
mysql tables in use 3, locked 3
LOCK WAIT 5 lock struct(s), heap size 1136, 3 row lock(s)
MySQL thread id 27844824, OS thread handle 140092183037696, query id 412503674 10.126.95.84 zeye Searching rows for update
update t_channel_final_datas set nr_register=133,nr_add_goods=29,nr_order_normal=11,nr_pay_normal=8,nr_order_special=0,nr_pay_special=0,n_add_user_num=16 where count_date=‘2017-07-16’ and channel_id=’16’ and channel_type=’10’ and terminal=’26’
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 464 page no 5459 n bits 392 index index_countdate_type_terminal of table `db_zz_flow`.`t_channel_final_datas` trx id 215208479 lock_mode X locks rec but not gap waiting
Record lock, heap no 304 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 10; hex 323031372d30372d3136; asc 2017–07–16;;
1: len 1; hex 30; asc 0;;
2: len 4; hex 80000010; asc ;;
3: len 4; hex 8009055e; asc ^;;
*** (2) TRANSACTION:
TRANSACTION 215208474, ACTIVE 0 sec fetching rows
mysql tables in use 3, locked 3
6 lock struct(s), heap size 1136, 7 row lock(s)
MySQL thread id 27844825, OS thread handle 140109890225920, query id 412503669 10.135.6.41 zeye Searching rows for update
update t_channel_final_datas set nr_register=24,nr_add_goods=32,nr_order_normal=0,nr_pay_normal=0,nr_order_special=0,nr_pay_special=0,n_add_user_num=11 where count_date=‘2017-07-16’ and channel_id=‘114’ and channel_type=’10’ and terminal=‘116’
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 464 page no 5459 n bits 392 index index_countdate_type_terminal of table `db_zz_flow`.`t_channel_final_datas` trx id 215208474 lock_mode X locks rec but not gap
Record lock, heap no 304 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 10; hex 323031372d30372d3136; asc 2017–07–16;;
1: len 1; hex 30; asc 0;;
2: len 4; hex 80000010; asc ;;
3: len 4; hex 8009055e; asc ^;;
...
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 464 page no 4743 n bits 264 index PRIMARY of table `db_zz_flow`.`t_channel_final_datas` trx id 215208474 lock_mode X locks rec but not gap waiting
Record lock, heap no 168 PHYSICAL RECORD: n_fields 32; compact format; info bits 0
0: len 4; hex 80090569; asc i;;
1: len 6; hex 00000cd3b9d0; asc ;;
...
*** WE ROLL BACK TRANSACTION (1)
|
篇幅所限,上下文我这里省略了很多,从这段日志里可以看到,TRANSACTION 1 和 TRANSACTION 2 分别持有一定数量的行锁,然后又等待对方的锁,最后 MySQL 检测到 deadlock ,然后选择回滚了 TRANSACTION 1:Innodb 目前处理死锁的方法是将持有最少行级排他锁的事务进行回滚。
那这里就有 3 个问题了:
(1)innodb 行锁不是只锁一行?
因为这张表是 innodb 引擎的,InnoDB 支持行锁和表锁。而 InnoDB 行锁是通过给索引上的索引项加锁来实现的,这一点 MySQL 与 Oracle 不同,后者是通过在数据块中对相应数据行加锁来实现的。InnoDB 这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB 才使用行级锁,否则,InnoDB 将使用表锁,会把所有扫描过的行都锁定!在实际应用中,要特别注意 InnoDB 行锁的这一特性,不然的话,可能导致大量的锁冲突,从而影响并发性能。由于 MySQL 的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB 会给符合条件的已有数据记录的索引项加锁;另外间隙锁也会锁多行,InnoDB 除了通过范围条件加锁时使用间隙锁外,如果使用相等条件请求给一个不存在的记录加锁,InnoDB 也会使用间隙锁!
话都说到这了,那就看下咱们业务表的索引情况:
1
2
3
4
5
6
7
8
9
10
11
|
show INDEX from `db_zz_flow`.`t_channel_final_datas`;
+———————————–+———————+———————————————–+————————+——————–+—————+————+———————+—————–+————————–+
| Table | Non_unique | Key_name | Seq_in_index | Column_namt | Packed | Null | Index_type | Comment | Index_comment |
|———————————–+———————+———————————————–+————————+——————–+—————+————+———————+—————–+————————–|
| t_channel_final_datas | 0 | PRIMARY | 1 | id > | <null> | | BTREE | | |
| t_channel_final_datas | 1 | index_countdate_type_terminal | 1 | count_date> | <null> | YES | BTREE | | |
| t_channel_final_datas | 1 | index_countdate_type_terminal | 2 | channel_ty> | <null> | YES | BTREE | | |
| t_channel_final_datas | 1 | index_countdate_type_terminal | 3 | terminal > | <null> | YES | BTREE | | |
| t_channel_final_datas | 1 | index_countdate_channelid | 1 | count_date> | <null> | YES | BTREE | | |
| t_channel_final_datas | 1 | index_countdate_channelid | 2 | channel_id> | <null> | YES | BTREE | | |
+———————————–+———————+———————————————–+————————+——————–+—————+————+———————+—————–+————————–+
|
可以看到这张表的索引极不合理:有 3 个索引,但是 update 却没有完全的用上索引,导致 update 没有精确的用上索引,需要锁定多行范围数据,从而引发死锁。
知道原理后,咱们再精心构建一个四字段的组合索引即可让 update 精准的走 innodb 索引,实际上,我们更新索引后,这个死锁问题即得到了解决。
注:innodb 不仅会打印出事务和事务持有和等待的锁,而且还有记录本身,不幸的是,它可能超过 innodb 为输出结果预留的长度(只能打印 1M 的内容且只能保留最近一次的死锁信息),如果你无法看到完整的输出,此时可以在任意库下创建 innodb_monitor 或 innodb_lock_monitor 表,这样 innodb status 信息会完整且每 15s 一次被记录到错误日志中。如:create table innodb_monitor(a int)engine=innodb;,不需要记录到错误日志中时就删掉这个表即可。
(2)回滚的话,为什么只有部分 update 语句失败,而不是整个事务里的所有 update 都失败?
这是因为咱们的 innodb 默认是自动提交的:
1
2
3
4
5
6
7
|
show variables like ‘autocommit’;
+————————–+————–+
| Variable_name | Value |
|————————–+————–|
| autocommit | ON |
+————————–+————–+
|
在多个 update 或 insert 语句情况下,每执行完一条 SQL,innodb 就立即 commit 一次以持久化变更,同时释放锁,这也正是本例中死锁回滚事务后只有极个别语句失败的原因。
需要注意的是,通常还有另外一种情况也可能导致部分语句回滚,需要格外留意。在 innodb 里有个参数叫:innodb_rollback_on_timeout
1
2
3
4
5
6
7
|
show VARIABLES LIKE ‘innodb_rollback_on_timeout’
+——————————————+————–+
| Variable_name | Value |
|——————————————+————–|
| innodb_rollback_on_timeout | OFF |
+——————————————+————–+
|
官方手册里这样描述:
In MySQL 5.1, InnoDB rolls back only the last statement on a transaction timeout by default. If –innodb_rollback_on_timeout is specified, a transaction timeout causes InnoDB to abort and roll back the entire transaction (the same behavior as in MySQL 4.1). This variable was added in MySQL 5.1.15.
解释:这个参数关闭或不存在的话遇到超时只回滚事务最后一个 Query,打开的话事务遇到超时就回滚整个事务。
(3)怎样降低 innodb 死锁几率?
死锁在行锁及事务场景下很难完全消除,但可以通过表设计和 SQL 调整等措施减少锁冲突和死锁,包括:
- 尽量使用较低的隔离级别,比如如果发生了间隙锁,你可以把会话或者事务的事务隔离级别更改为 RC(read committed) 级别来避免,但此时需要把 binlog_format 设置成 row 或者 mixed 格式
- 精心设计索引,并尽量使用索引访问数据,使加锁更精确,从而减少锁冲突的机会;
- 选择合理的事务大小,小事务发生锁冲突的几率也更小;
- 给记录集显示加锁时,最好一次性请求足够级别的锁。比如要修改数据的话,最好直接申请排他锁,而不是先申请共享锁,修改时再请求排他锁,这样容易产生死锁;
- 不同的程序访问一组表时,应尽量约定以相同的顺序访问各表,对一个表而言,尽可能以固定的顺序存取表中的行。这样可以大大减少死锁的机会;
- 尽量用相等条件访问数据,这样可以避免间隙锁对并发插入的影响;
- 不要申请超过实际需要的锁级别;除非必须,查询时不要显示加锁;
- 对于一些特定的事务,可以使用表锁来提高处理速度或减少死锁的可能。
2、Case2:诡异的 Lock wait timeout
连续几天凌晨 6 点和早上 8 点 都分别有一个任务失败,load data local infile 的时候报 Lock wait timeout exceeded try restarting transaction innodb 的 Java SQL 异常,和平台的同学沟通得知,这是我们自己的业务数据库的 Lock 时间太短或者锁冲突的问题。但是回头一想不应该啊?这不一直好好的吗?而且基本都是单表单任务,不存在多人冲突。
甭管谁的问题,那咱们还是先看自己的数据库有没有问题:
1
2
3
4
5
6
7
|
show variables like ‘innodb_lock_wait_timeout’;
+—————————————+————–+
| Variable_name | Value |
|—————————————+————–|
| innodb_lock_wait_timeout | 50 |
+—————————————+————–+
|
默认 lock 超时时间 50s,这个时间真心不短了,估计调了也没用,事实上确实死马当活马医的试了下没用。。。
而且这次 SHOW ENGINE INNODB STATUSG 也没出现任何死锁信息,然后又将目光转向 MySQL-server 日志,希望能从日志里看一看那个时刻前后数据究竟在做什么操作。这里先简单的介绍下 MySQL 日志文件系统的组成:
(a) error 日志:记录启动、运行或停止 mysqld 时出现的问题,默认开启。
(b) general 日志:通用查询日志,记录所有语句和指令,开启数据库会有 5% 左右性能损失。
(c) binlog 日志:二进制格式,记录所有更改数据的语句,主要用于 slave 复制和数据恢复。
(d) slow 日志:记录所有执行时间超过 long_query_time 秒的查询或不使用索引的查询,默认关闭。
(e) Innodb 日志:innodb redo log、undo log,用于恢复数据和撤销操作。
从上面的介绍可以看到,目前这个问题的日志可能在 d 和 b 中,看了下 d 中没有,那就只能开启 b 了,但 b 对数据库的性能有一定损耗,由于是全量日志,量非常巨大,所以开启一定要谨慎:
— 全局 session 级别开启:
set global general_log=1
— 如果需要对当前 session 生效需要:
set general_log=1
— set 指令设置的动态参数在 MySQL 重启后失效,如果需要永久生效需要在 /etc/my.cnf 中配置静态变量/参数。
— 如果不知道 my.cnf 位置,可以根据 mysql -? | grep “.cnf” 查询
order of preference, my.cnf, $MYSQL_TCP_PORT,
/etc/my.cnf /etc/mysql/my.cnf /usr/etc/my.cnf ~/.my.cnf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
— general_log 日志默认关闭,开启会影响数据库 5% 左右性能:
show variables like ‘general%’;
+—————————+————————————————–+
| Variable_name | Value |
|—————————+————————————————–|
| general_log | OFF |
| general_log_file | /opt/data/mysql/tjtx–103–26.log |
+—————————+————————————————–+
— 全局 session 级别开启:
set global general_log=1
— 如果需要对当前 session 生效需要:
set general_log=1
— set 指令设置的动态参数在 MySQL 重启后失效,如果需要永久生效需要在 /etc/my.cnf 中配置静态变量 / 参数。
— 如果不知道 my.cnf 位置,可以根据 mysql –? | grep “.cnf” 查询
order of preference, my.cnf, $MYSQL_TCP_PORT,
/etc/my.cnf /etc/mysql/my.cnf /usr/etc/my.cnf ~/.my.cnf
|
我这里只是每天在出问题的前后半小时开启下全量日志,结果没有发现任何 MySQL-client 请求到我们的业务数据库!该日志格式如下,记录了所有的连接与命令:
1
2
3
4
5
6
7
8
9
10
|
/usr/sbin/mysqld, Version: 5.7.12–log (MySQL Community Server (GPL)). started with:
Tcp port: 3306 Unix socket: /opt/data/mysql/mysql.sock
Time Id Command Argument
2017–07–20T21:45:01.880828Z28556028 Quit
2017–07–20T21:45:02.708621Z28401469 Query SELECT 1
2017–07–20T21:45:02.736734Z28556029 Connect ooxx@127.0.0.1 on db_zz_system using TCP/IP
2017–07–20T21:45:02.737242Z28556029 Query /* mysql-connector-java-5.1.6 ( Revision: ${svn.Revision} ) */SHOW VARIABLES WHERE Variable_name =‘language’ OR Variable_name = ‘net_write_timeout’ OR Variable_name = ‘interactive_timeout’ OR Variable_name = ‘wait_timeout’ OR Variable_name = ‘character_set_client’ OR Variable_name = ‘character_set_connection’ OR Variable_name = ‘character_set’ OR Variable_name = ‘character_set_server’ OR Variable_name = ‘tx_isolation’ OR Variable_name = ‘transaction_isolation’ OR Variable_name = ‘character_set_results’ OR Variable_name = ‘timezone’ OR Variable_name = ‘time_zone’ OR Variable_name = ‘system_time_zone’ OR Variable_name = ‘lower_case_table_names’ OR Variable_name = ‘max_allowed_packet’ OR Variable_name = ‘net_buffer_length’ OR Variable_name = ‘sql_mode’ OR Variable_name = ‘query_cache_type’ OR Variable_name = ‘query_cache_size’ OR Variable_name = ‘init_connect’
2017–07–20T21:45:02.738868Z28556029 Query SHOW COLLATION
2017–07–20T21:45:02.739941Z28556029 Query SET character_set_results = NULL
2017–07–20T21:45:02.740275Z28556029 Query SET autocommit=1
|
那问题基本确定了,客户端请求都没到我们这边就抛出了上述的异常,和平台方再三沟通确认下,最后平台方查证是因为在执行插入前他们需要先从 SQL task 表取出 SQL 和更新 task 状态,结果这张表由于在整点存在大量 insert 和 update 并发,导致部分 SQL 等待 lock 超时了。。。
3、MySQL 日志分析脚本
由于凌晨是数据仓库的业务高峰,很多问题都是在这个时候爆发,一些诡异的问题往往是过了这个村就没这个店了,白天无法复现。如何能捕获我们关心的日志,便于快速的定位问题,这个是重中之重,这里我写了个小脚本,crontab 部署,可以选择时间范围开启,每分钟采样一次日志,需要说明的是 general log 没事别轻易开启,否则对数据库性能损耗较大。
date -Iseconds
echo ‘>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> top -bn1|head’
top -bn1|head
echo
echo ‘>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SHOW ENGINE INNODB STATUS\G ‘
mysql -uroot -pooxx -h127.0.0.1 -e ‘SHOW ENGINE INNODB STATUS\G’
echo
echo ‘>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> show open tables where in_use>0;’
mysql -uroot -pooxx -h127.0.0.1 -e ‘show open tables where in_use>0;’
echo
echo ‘>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> show full processlist;’
mysql -uroot -pooxx -h127.0.0.1 -e ‘show full processlist;’
echo
echo ‘>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SELECT * FROM `information_schema`.`innodb_trx` ORDER BY `trx_started`; ‘
mysql -uroot -pooxx -h127.0.0.1 -e ‘SELECT * FROM `information_schema`.`innodb_trx` ORDER BY `trx_started`;’
echo
echo ‘>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SELECT * FROM `information_schema`.`innodb_locks`;’
mysql -uroot -pooxx -h127.0.0.1 -e ‘SELECT * FROM `information_schema`.`innodb_locks`;’
echo
echo “>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> show status like ‘%lock%’;”
mysql -uroot -pooxx -h127.0.0.1 -e “show status like ‘%lock%’;”
echo
echo ‘>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> show global status like “table_locks%”;’
mysql -uroot -pooxx -h127.0.0.1 -e ‘show global status like “table_locks%”;’
echo
echo “>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> too long omit…”
mysql -uroot -pooxx -h127.0.0.1 -e “select r.trx_isolation_level, r.trx_id waiting_trx_id, r.trx_mysql_thread_id waiting_trx_thread, r.trx_state waiting_trx_state, lr.lock_mode waiting_trx_lock_mode, lr.lock_type waiting_trx_lock_type, lr.lock_table waiting_trx_lock_table, lr.lock_index waiting_trx_lock_index, r.trx_query waiting_trx_query, b.trx_id blocking_trx_id, b.trx_mysql_thread_id blocking_trx_thread, b.trx_state blocking_trx_state, lb.lock_mode blocking_trx_lock_mode, lb.lock_type blocking_trx_lock_type, lb.lock_table blocking_trx_lock_table, lb.lock_index blocking_trx_lock_index, b.trx_query blocking_query from information_schema.innodb_lock_waits w inner join information_schema.innodb_trx b on b.trx_id=w.blocking_trx_id inner join information_schema.innodb_trx r on r.trx_id=w.requesting_trx_id inner join information_schema.innodb_locks lb on lb.lock_trx_id=w.blocking_trx_id inner join information_schema.innodb_locks lr on lr.lock_trx_id=w.requesting_trx_id\G”
echo
echo “>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> too long omit…”
mysql -uroot -pooxx -h127.0.0.1 -e “SELECT r.trx_id waiting_trx_id, r.trx_mysql_thread_id waiting_thread, r.trx_query waiting_query, b.trx_id blocking_trx_id, b.trx_mysql_thread_id blocking_thread, b.trx_query blocking_query FROM information_schema.innodb_lock_waits w INNER JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id INNER JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id\G”
echo
date -Iseconds
echo ‘>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>==================================================================================’
echo
file_name=mysql_perf.log.`date -I`
if [[ `date +%-H%-M` == 1059 ]]
then
cd /opt/ooxx/script/logs
chmod 777 /home/work/ooxx/$file_name
find /home/work/ooxx -name ‘mysql_perf.log.*’ -mtime +7 -delete
cd /opt/data/mysql
cp tjtx-ooxx-slow.log /home/work/ooxx/tjtx-ooxx-slow.log.`date -I`
chmod 777 /home/work/ooxx/tjtx-ooxx-slow.log.`date -I`
find /home/work/ooxx -name ‘tjtx-ooxx-slow.log.*’ -mtime +7 -delete
cp mysqld.log /home/work/ooxx/mysqld.log.`date -I`
chmod 777 /home/work/ooxx/mysqld.log.`date -I`
find /home/work/ooxx -name ‘mysqld.log.*’ -mtime +7 -delete
fi
################
# 开启 general_log 全量明细日志会降低数据库 5% 性能
#if [[ “`date +%H%M`” == “0545” ]]
#then
# echo “`date +%H%M` ——- set global general_log=1;”
# mysql -uroot -pooxx -h127.0.0.1 -e ‘set global general_log=1;’
#elif [[ “`date +%H%M`” == “0630” ]]
#then
# echo “`date +%H%M` ——- set global general_log=0;”
# mysql -uroot -pooxx -h127.0.0.1 -e ‘set global general_log=0;’
#elif [[ “`date +%H%M`” == “0745” ]]
#then
# echo “`date +%H%M` ——- set global general_log=1;”
# mysql -uroot -pooxx -h127.0.0.1 -e ‘set global general_log=1;’
#elif [[ “`date +%H%M`” == “0830” ]]
#then
# echo “`date +%H%M` ——- set global general_log=0;”
# mysql -uroot -pooxx -h127.0.0.1 -e ‘set global general_log=0;’
#elif [[ “`date +%H%M`” == “0001” ]]
#then
# echo “`date +%H%M` ——- set global general_log=1;”
# mysql -uroot -pooxx -h127.0.0.1 -e ‘set global general_log=1;’
#elif [[ “`date +%H%M`” == “0002” ]]
#then
# echo “`date +%H%M` ——- set global general_log=0;”
# mysql -uroot -pooxx -h127.0.0.1 -e ‘set global general_log=0;’
#fi
#[[ 10#`date +%H%M` -lt 10#0550 || 10#`date +%H%M` -gt 10#0830 ]] && echo “`date +%H%M` ——- set global general_log=0;” && mysql -uroot -pooxx -h127.0.0.1 -e ‘set global general_log=0;’
# mysql -uroot -pooxx -h127.0.0.1 -e ‘show open tables where in_use>0;show full processlist;SELECT * FROM `information_schema`.`innodb_trx` ORDER BY `trx_started`;SELECT * FROM `information_schema`.`innodb_locks`;SHOW ENGINE INNODB STATUS\G’
# –show variables like ‘%tx_isolation%’;
# –SELECT @@GLOBAL.tx_isolation, @@tx_isolation, @@session.tx_isolation;
# — SET GLOBAL tx_isolation = ‘READ-COMMITTED’;
#
# –show variables like ‘%timeout%’;
# –show variables like ‘innodb_lock_wait_timeout’;
# — SET GLOBAL innodb_lock_wait_timeout=60
#
# –show variables like ‘long_query_time’;
# — SET global long_query_time=3;
# — show variables like ‘innodb_rollback_on_timeout’;
# — show VARIABLES like ‘%max_allowed_packet%’;
# — set global max_allowed_packet = 100*1024*1024;
# 自动提交
# — show variables like ‘autocommit’;
# 慢查询
# — show variables like ‘%slow_query_log%’;
# set global 只对当前数据库生效,如果 MySQL 重启后则会失效。如果要永久生效,就必须修改配置文件 my.cnf(其它系统变量也是如此)。
# set global slow_query_log=1;
# — show variables like ‘long_query_time%’;
# set global long_query_time=4;
# show global variables like ‘long_query_time’;
# select sleep(5);
# — show variables like ‘log_queries_not_using_indexes’;
# set global log_queries_not_using_indexes=1;
# — show variables like ‘log_slow_admin_statements’;
# — show global status like ‘%Slow_queries%’;
# http://ift.tt/1PxfmU5
# — show variables like “%time_zone%”;
#set global time_zone = ‘+8:00’;
#开启 general_log 日志
# — show variables like ‘general%’;
#可以在 my.cnf 里添加,1 开启(0 关闭), 当然了, 这样要重启才能生效, 有点多余了
#general-log = 1
#log = /log/mysql_query.log 路径
#也可以设置变量那样更改,1 开启(0 关闭), 即时生效, 不用重启, 首选当然是这样的了
# set global general_log=1
#这个日志对于操作频繁的库, 产生的数据量会很快增长, 出于对硬盘的保护, 可以设置其他存放路径
#set global general_log_file=/tmp/general_log.log
#mysql 记录客户端 IP:init_connect,有 super 权限的用户是不记录的,
# create table t1 ( cur_user varchar(100), n_user varchar(100),in_time timestamp default current_timestamp()) ;
# set global init_connect=’insert into test.t1 (cur_user,n_user) values (current_user(),user())’;
# SHOW CREATE TABLE mysql.general_log\G ,开启 general_log 日志也行:
# http://ift.tt/2tQ4OLW
#SELECT REVERSE(SUBSTRING_INDEX(REVERSE(USER()),’@’,1)) as ip;
#SELECT SUBSTRING(USER(), LOCATE(‘@’, USER())+1) as ip;
#select SUBSTRING_INDEX(host,’:’,1) as ‘ip’ from information_schema.processlist WHERE ID=connection_id();
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
|
# crontab 部署方式:
# */1 0-10 * * * cd /opt/ooxx/script && bash mysql_perf.sh >> logs/mysql_perf.log.`date -I` 2>&1
date –Iseconds
echo ‘>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> top -bn1|head’
top –bn1|head
echo
echo ‘>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SHOW ENGINE INNODB STATUS\G ‘
mysql –uroot –pooxx –h127.0.0.1 –e ‘SHOW ENGINE INNODB STATUS\G’
echo
echo ‘>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> show open tables where in_use>0;’
mysql –uroot –pooxx –h127.0.0.1 –e ‘show open tables where in_use>0;’
echo
echo ‘>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> show full processlist;’
mysql –uroot –pooxx –h127.0.0.1 –e ‘show full processlist;’
echo
echo ‘>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SELECT * FROM `information_schema`.`innodb_trx` ORDER BY `trx_started`; ‘
mysql –uroot –pooxx –h127.0.0.1 –e ‘SELECT * FROM `information_schema`.`innodb_trx` ORDER BY `trx_started`;’
echo
echo ‘>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SELECT * FROM `information_schema`.`innodb_locks`;’
mysql –uroot –pooxx –h127.0.0.1 –e ‘SELECT * FROM `information_schema`.`innodb_locks`;’
echo
echo “>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> show status like ‘%lock%’;”
mysql –uroot –pooxx –h127.0.0.1 –e “show status like ‘%lock%’;”
echo
echo ‘>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> show global status like “table_locks%”;’
mysql –uroot –pooxx –h127.0.0.1 –e ‘show global status like “table_locks%”;’
echo
echo “>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> too long omit…”
mysql –uroot –pooxx –h127.0.0.1 –e “select r.trx_isolation_level, r.trx_id waiting_trx_id, r.trx_mysql_thread_id waiting_trx_thread, r.trx_state waiting_trx_state, lr.lock_mode waiting_trx_lock_mode, lr.lock_type waiting_trx_lock_type, lr.lock_table waiting_trx_lock_table, lr.lock_index waiting_trx_lock_index, r.trx_query waiting_trx_query, b.trx_id blocking_trx_id, b.trx_mysql_thread_id blocking_trx_thread, b.trx_state blocking_trx_state, lb.lock_mode blocking_trx_lock_mode, lb.lock_type blocking_trx_lock_type, lb.lock_table blocking_trx_lock_table, lb.lock_index blocking_trx_lock_index, b.trx_query blocking_query from information_schema.innodb_lock_waits w inner join information_schema.innodb_trx b on b.trx_id=w.blocking_trx_id inner join information_schema.innodb_trx r on r.trx_id=w.requesting_trx_id inner join information_schema.innodb_locks lb on lb.lock_trx_id=w.blocking_trx_id inner join information_schema.innodb_locks lr on lr.lock_trx_id=w.requesting_trx_id\G”
echo
echo “>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> too long omit…”
mysql –uroot –pooxx –h127.0.0.1 –e “SELECT r.trx_id waiting_trx_id, r.trx_mysql_thread_id waiting_thread, r.trx_query waiting_query, b.trx_id blocking_trx_id, b.trx_mysql_thread_id blocking_thread, b.trx_query blocking_query FROM information_schema.innodb_lock_waits w INNER JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id INNER JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id\G”
echo
date –Iseconds
echo ‘>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>==================================================================================’
echo
file_name=mysql_perf.log.`date –I`
if [[ `date +%–H%–M` == 1059 ]]
then
cd /opt/ooxx/script/logs
chmod 777 /home/work/ooxx/$file_name
find /home/work/ooxx –name ‘mysql_perf.log.*’ –mtime +7 –delete
cd /opt/data/mysql
cp tjtx–ooxx–slow.log /home/work/ooxx/tjtx–ooxx–slow.log.`date –I`
chmod 777 /home/work/ooxx/tjtx–ooxx–slow.log.`date –I`
find /home/work/ooxx –name ‘tjtx-ooxx-slow.log.*’ –mtime +7 –delete
cp mysqld.log /home/work/ooxx/mysqld.log.`date –I`
chmod 777 /home/work/ooxx/mysqld.log.`date –I`
find /home/work/ooxx –name ‘mysqld.log.*’ –mtime +7 –delete
fi
################
# 开启 general_log 全量明细日志会降低数据库 5% 性能
#if [[ “`date +%H%M`” == “0545” ]]
#then
# echo “`date +%H%M` ——- set global general_log=1;”
# mysql -uroot -pooxx -h127.0.0.1 -e ‘set global general_log=1;’
#elif [[ “`date +%H%M`” == “0630” ]]
#then
# echo “`date +%H%M` ——- set global general_log=0;”
# mysql -uroot -pooxx -h127.0.0.1 -e ‘set global general_log=0;’
#elif [[ “`date +%H%M`” == “0745” ]]
#then
# echo “`date +%H%M` ——- set global general_log=1;”
# mysql -uroot -pooxx -h127.0.0.1 -e ‘set global general_log=1;’
#elif [[ “`date +%H%M`” == “0830” ]]
#then
# echo “`date +%H%M` ——- set global general_log=0;”
# mysql -uroot -pooxx -h127.0.0.1 -e ‘set global general_log=0;’
#elif [[ “`date +%H%M`” == “0001” ]]
#then
# echo “`date +%H%M` ——- set global general_log=1;”
# mysql -uroot -pooxx -h127.0.0.1 -e ‘set global general_log=1;’
#elif [[ “`date +%H%M`” == “0002” ]]
#then
# echo “`date +%H%M` ——- set global general_log=0;”
# mysql -uroot -pooxx -h127.0.0.1 -e ‘set global general_log=0;’
#fi
#[[ 10#`date +%H%M` -lt 10#0550 || 10#`date +%H%M` -gt 10#0830 ]] && echo “`date +%H%M` ——- set global general_log=0;” && mysql -uroot -pooxx -h127.0.0.1 -e ‘set global general_log=0;’
# mysql -uroot -pooxx -h127.0.0.1 -e ‘show open tables where in_use>0;show full processlist;SELECT * FROM `information_schema`.`innodb_trx` ORDER BY `trx_started`;SELECT * FROM `information_schema`.`innodb_locks`;SHOW ENGINE INNODB STATUS\G’
# –show variables like ‘%tx_isolation%’;
# –SELECT @@GLOBAL.tx_isolation, @@tx_isolation, @@session.tx_isolation;
# — SET GLOBAL tx_isolation = ‘READ-COMMITTED’;
#
# –show variables like ‘%timeout%’;
# –show variables like ‘innodb_lock_wait_timeout’;
# — SET GLOBAL innodb_lock_wait_timeout=60
#
# –show variables like ‘long_query_time’;
# — SET global long_query_time=3;
# — show variables like ‘innodb_rollback_on_timeout’;
# — show VARIABLES like ‘%max_allowed_packet%’;
# — set global max_allowed_packet = 100*1024*1024;
# 自动提交
# — show variables like ‘autocommit’;
# 慢查询
# — show variables like ‘%slow_query_log%’;
# set global 只对当前数据库生效,如果 MySQL 重启后则会失效。如果要永久生效,就必须修改配置文件 my.cnf(其它系统变量也是如此)。
# set global slow_query_log=1;
# — show variables like ‘long_query_time%’;
# set global long_query_time=4;
# show global variables like ‘long_query_time’;
# select sleep(5);
# — show variables like ‘log_queries_not_using_indexes’;
# set global log_queries_not_using_indexes=1;
# — show variables like ‘log_slow_admin_statements’;
# — show global status like ‘%Slow_queries%’;
# http://ift.tt/1PxfmU5
# — show variables like “%time_zone%”;
#set global time_zone = ‘+8:00’;
#开启 general_log 日志
# — show variables like ‘general%’;
#可以在 my.cnf 里添加,1 开启(0 关闭), 当然了, 这样要重启才能生效, 有点多余了
#general-log = 1
#log = /log/mysql_query.log 路径
#也可以设置变量那样更改,1 开启(0 关闭), 即时生效, 不用重启, 首选当然是这样的了
# set global general_log=1
#这个日志对于操作频繁的库, 产生的数据量会很快增长, 出于对硬盘的保护, 可以设置其他存放路径
#set global general_log_file=/tmp/general_log.log
#mysql 记录客户端 IP:init_connect,有 super 权限的用户是不记录的,
# create table t1 ( cur_user varchar(100), n_user varchar(100),in_time timestamp default current_timestamp()) ;
# set global init_connect=’insert into test.t1 (cur_user,n_user) values (current_user(),user())’;
# SHOW CREATE TABLE mysql.general_log\G ,开启 general_log 日志也行:
# http://ift.tt/2tQ4OLW
#SELECT REVERSE(SUBSTRING_INDEX(REVERSE(USER()),’@’,1)) as ip;
#SELECT SUBSTRING(USER(), LOCATE(‘@’, USER())+1) as ip;
#select SUBSTRING_INDEX(host,’:’,1) as ‘ip’ from information_schema.processlist WHERE ID=connection_id();
|
Refer:
[1] MySQL 批量 SQL 插入性能优化
[2] MySQL 四种事务隔离级的说明
[3] innodb_rollback_on_timeout 参数对锁的影响
[4] 日均万条数据丢失,一个隐式骚操作导致的奇葩事故!
[5] mysql 之 show engine innodb status 解读(转)
[6] Mysql 锁机制笔记
[7] MySQL 索引原理及慢查询优化
[8] [译文]MySQL 发生死锁肿么办?
[9] MySQL 的 timeout 那点事
[10] mysql 死锁 (deadlock) 分析及解决(索引 间隙锁 sql 顺序)
转自 http://ift.tt/2vdcnA6
The post MySQL 死锁与日志二三事 appeared first on Linuxeden开源社区.
http://ift.tt/2wdJEs6