Jupyter Notebook入门指南

京东云开发者
• 阅读 404
作者:京东科技隐私计算产品部 孙晓军

1. Jupyter Notebook介绍



Jupyter Notebook入门指南



图1 Jupter项目整体架构

[https://docs.jupyter.org/en/latest/projects/architecture/content-architecture.html]

Jupyter Notebook是一套基于web的交互式开发环境。用户可以在线开发和分享包含代码和输出的交互式文档,支持实时代码,数学方程,可视化和 markdown等。用途包括:数据清理和转换,数值模拟,统计建模,机器学习等等。

Jupyter Notebook内部通过内核维护状态并运行代码片段,浏览器显示代码片段和其执行的结果。Jupyter Notebook提供了一个用户交互式的开发环境,用户可以通过执行一部分代码片段,并观察执行结果。这种交互式设计,使得Jupyter Notebook非常适合数据科学和机器学习的开发工作。

注意本文的代码和脚本,均基于Jupyter Notebook v6.5.2稳定版本。

2. Jupyter的工作方式



Jupyter Notebook入门指南



图2 Jupter Notebook工作方式

[https://docs.jupyter.org/en/latest/projects/architecture/content-architecture.html]

Jupyter的主要工作单元是Jupyter Server和Kernel。其中Jupyter Server用来提供基于Web的界面和API服务,Kernel用来执行代码片段。浏览器通过Http和Websockets的方式和Jupyter Server进行交互,Jupyter Server和kernel之间,通过ZeroMQ进行数据通信。

Jupyter Server采用经典的MVC模式,使用了tornado作为web服务器,用来提供地址映射和控制器逻辑,使用jinja2来提供模板视图功能。

Jupyter Notebook(v6.5.2)项目的主要模块结构如下:

模块 说明
notebook Notebook功能模块。
terminal 终端模块。为Jupyter提供控制台交互能力。
view 文件可视化模块。比如pdf文件的显示。
tree 工作区目录树
nbconvert 格式转换模块,可以把Jupyter Notebook转换成html,pdf等格式
kernel Jupyter Notebook 内核
services Jupyter Notebook REST API模块
i18n Jupyter Notebook多语言资源

3. 安装Jupyter Notebook

前置条件

Python和pip

不同的Jupyter Notebook对Python有不同的版本要求。我们安装的最新的稳定版本v6.5.2的Jupyter Notebook,要求Python的最低版本为3.6。注意这个Python的版本,不同于内核的Python版本。对于Jupyter内核来说,支持的Python版本和Jupyter Notebook依赖的Python版本没有关系。

在Linux系统下安装Jupyter Notebook

使用pip安装Jupyter notebook非常简单。如果服务器同时拥有Python2和Python3的pip,注意需要使用pip3来替换命令中的pip。

# 更新pip
pip install --upgrade pip
# 安装jupyter
pip install jupyter
# 检查安装的jupyter
jupyter --version
//输出 notebook  : 6.5.2

4. 配置和启动Jupyter

Jupyter提供了大量的启动参数,用来配置Jupyter Server。我们可以在启动Jupyter服务时,通过命令行参数的方式配置当前启动的服务,但更普遍的方式是使用Jupyter的配置文件。

# 生成配置文件
jupyter notebook --generate-config
// 默认生成的配置文件位置:
/root/.jupyter/jupyter_notebook_config.py

# 修改Jupyter配置文件...

# 启动jupyter
jupyter notebook

Jupyter直接使用一个Python文件来配置Jupyter服务,所有的配置项均通过Python代码来完成。常用的配置项及其说明如下:

名称 默认值 说明
c.NotebookApp.allow_root False 为了安全,Jupyter默认不允许使用root用户启动。如果需要以root用户的身份启动Jupyter,需要开启此设定
c.NotebookApp.allow_origin '' 当需要Jupyter内嵌到iframe时,可以设置为“*“来避免跨origin的限制
c.NotebookApp.ip localhost 当需要通过外网地址来访问Jupyter服务时,需要设置一个有效的服务器IP地址。
c.NotebookApp.port 8888 Jupyter server对外服务端口
c.NotebookApp.notebook_dir / Jupyter的工作空间,默认可以访问服务器上当前用户的所有文件系统
c.NotebookApp.open_browser True 启动服务后是否立即通过浏览器打开服务地址
c.NotebookApp.default_url /tree Jupyter服务的默认地址
c.NotebookApp.extra_static_paths [] 扩展静态文件目录
c.NotebookApp.extra_template_paths [] 扩展模板文件目录
c.KernelSpecManager.allowed_kernelspecs set() 默认允许使用所有的kernel
c.NotebookApp.nbserver_extensions {} 允许加载的Jupyter Server扩展

5. 使用Jupyter

5.1. 创建Notebook

启动Jupyter 后,在浏览器内输入 http://服务器地址:端口/,Jupyter会默认重定向到.default_url指定的工作区目录树地址,默认是工作区目录树的界面。

如果在访问的过程中,使用了默认的token作为其认证方式,那么在首次打开时,需要输入Jupyter Notebook的token值,这个值可以在启动Jupyter时的控制台输出中找到,或者使用Jupyter命令来查询

# 查询运行的jupyter notebook
jupyter notebook list
//返回结果中包含了http://x.x.x.x:8899?token=ABC 的信息,其中的ABC就是我们需要的token



Jupyter Notebook入门指南



图3 Jupter Notebook的默认工作区目录树页面

Jupyter Notebook通过Jupyter Server提供基于Web的平台无关的工作方式,这使得跨平台开发和协作,代码分享等能力变得比传统IDE更加容易。

在Jupyter 工作区管理界面,用户可以灵活地以类似文件系统的方式管理工作区的数据。可以创建文件和文件夹,编辑文件和文件夹,可以上传和下载文件。通过选择一个Jupyter内核,可以创建一个Notebook文件。



Jupyter Notebook入门指南



图4 通过Jupyter内核创建一个Notebook

5.2. 使用Notebook

使用Python3内核创建一个Notebook后,我们得到一个xxx.ipynb(IPython Notebook)文件。这个文件是一个json格式的文本文件,其中包含了我们在Notebook中编写的代码和文本内容,也包含了界面上没有显示的元数据信息。通过在工作区目录界面选择一个notebook文件,点击编辑,我们可以查看到ipynb文件的原始内容。



Jupyter Notebook入门指南



图5 ipynb文件的原始内容

我们可以像使用其它IDE类似的方式来使用Notebook,在使用Notebook上,我们主要关注下Jupyter内核和单元格。

内核是执行单元格代码的核心进程,不同的内核,决定了我们在单元格中能够编写哪些语言的代码,以及对应了指定的编程语言的哪个版本等信息。

单元格是整个Notebook的核心组成部分,我们编写的代码和文本,通过一些列Notebook单元格来组成。Notebook提供了Code,Markdown, Raw NBConvert, Heading四种类型的单元格。

•Code单元格。用来编写内核指定语言的程序代码

•Markdown单元格。使用Markdown编辑器的语法来编辑富文本

•Raw NBConvert单元格。原始的文本,不会被当作代码或markdown被解释执行

•Heading单元格。Heading是Mardown的一个子集,对应了Markdown中的标题编写语法

Jupyter Notebook使用了机器学习中检查点的概念,在我们修改Notebook的过程中,Jupyter会自动保存我们的修改,我们也可以通过【文件】->【保存】来手动保存检查点。检查点文件包含了我们编写的Notebook内容,以及执行代码单元格之后的输出。我们可以在工作空间的“.ipynb_checkpoints”文件夹下,找到这些检查点文件。



Jupyter Notebook入门指南



图6 使用Jupyter单元格来编写交互式代码

5.3. 分享Notebook

相较于使用传统的IDE编写的代码,基于Web服务的Jupyter Notebook在代码分享上拥有着天然的优势。

在Jupyter Notebook中,我们可以通过两种不同的方式分享我们创作的nootbook。

  1. 交互式Notebook文档

传统的技术文档或者说明书,通过静态的文本,配合图片和视频,来描述和讲解特定的技术或功能。有了Jupyter Notebook后,我们仍然可以使用Notebook来编写类似传统的技术文档。在此基础上,我们可以加入更生动的代码交互单元格,用户通过查看文档说明,并与文档中提供的代码进行互动,可以更生动地介绍产品中的功能和技术。每个Jupyter Notebook的ipynb文件,都对应了一个独立的访问地址: http://x.x.x.x:8899/notebooks/my_notebook.ipynb ,通过分享此文件的地址,其他用户可以方便地使用包含了富文本和可执行的代码的交互式Notebook文档。

  1. 离线Notebook文档

我们通过逐步执行文档中的所有单元格,得到一个包含了我们编写的说明和代码,以及代码执行的输出结果的完整文档。之后点击【文件】-> 【另存为】,选择一种合适的文件格式。我们可以把文档导出为一份静态文件,通过共享此静态文件,我们实现了Notebook文档的离线分享。

5.4. 魔法函数

Jupyter Notebook提供了一些列魔法函数来增强Jupyter Code单元格的功能,通过魔法函数,我们能够执行javascript脚本,html代码,运行另一个可执行程序等许多额外的功能。

我们可以在Jupyter代码单元格中使用 %lsmagic命令来查看所有的魔法函数,如果要阅读详细的魔法函数的使用说明,可以参考: https://ipython.readthedocs.io/en/stable/interactive/magics.html

魔法函数分为行魔法函数,单元格魔法函数和会话魔法函数。顾名思义,行魔法函数只对当前行起作用,而单元格魔法函数则作用于整个单元格,会话魔法函数则作用于整个会话期间。

一些常用的魔法函数:

指令 说明
%matplotlib 设置matplot绘图的显示模式
%%javascript 单元格内的代码被识别为javascript代码
%%html 单元格内的代码被识别为html代码
%run 执行外部脚本文件
%pwd 获取当前工作的目录位置(非工作空间目录位置)
%writefile 以文件形式保存当前单元格代码
%timeit 获取本行代码的执行时间
%debug 激活调试模式

6. 管理Jupyter

6.1. 多语言

Jupyter Notebook使用i18n目录下的资源来进行多语言翻译。在Jupyter Notebook启动时,会加载i18n目录下的多语言资源。之后根据http请求指定的语言,为响应数据提供对应的多语言翻译。如果没有对应的翻译,则保留原始的多语言标签值(英文)。如果调整了多语言翻译,需要重新启动Jupyter Notebook才能使用新的语言包。

Jupyter Notebook的翻译资源主要分布在三个po文件中:

•nbjs.po - js文件中的多语言数据

•nbui.po - UI界面中的多语言数据

•notebook.po - notebook中的多语言数据

原始的po文件,需要通过pybabel工具,把po文件编译成mo文件,之后部署在$notebook/i18n/${LANG}/LC_MESSAGES/目录下($notebook是notebook的安装目录),才能在Jupyter Notebook中作为多语言的资源包来使用。

# 使用pybabel编译多语言po文件
pybabel compile -D notebook -f -l ${LANG} -i ${LANG}/LC_MESSAGES/notebook.po -o ${LANG}/LC_MESSAGES/notebook.mo
pybabel compile -D nbui -f -l ${LANG} -i ${LANG}/LC_MESSAGES/nbui.po -o ${LANG}/LC_MESSAGES/nbui.mo
pybabel compile -D nbjs -f -l ${LANG} -i ${LANG}/LC_MESSAGES/nbjs.po -o ${LANG}/LC_MESSAGES/nbjs.mo



Jupyter Notebook入门指南



图7 使用了中文语言包后的中文Notebook界面

6.2. 内核管理

内核(kernel)是独立于jupyter服务和界面之外的用来运行Jupyter代码单元格的进程,Jupyter默认提供了ipykernel内核来支持Python开发语言。Jupyter社区提供了jupyterC, IJava,xeus-cling, xeus-sql等众多其它编程语言的内核,用来支持C/C++, Java, SQL等编程语言。

ipykernel默认使用系统环境下的Python来提供服务。我们可以使用ipykernel安装多个Python kernel来提供Python2.x, Python3.x等多个Python内核环境。

安装kernel后,kernel的信息被保存在kernel.json文件中,我们可以在 /usr/local/share/jupyter/kernels 目录,找到Jupyter安装的所有kernel以及对应的kernel.json文件。

kernel可以直接继承自安装kernel的Python指令,也可以使用Python虚拟环境。

# 1.直接继承自Python指令的kernel安装
# 安装ipykernel
pip install ipykernel
# 安装kernel
python -m ipykernel install --name tensorflow2 --display-name "tensorflow2"

# 2. 在Python虚拟环境下的kernel安装
# 激活虚拟环境
source activate myenv
# 安装ipykernel
pip install ipykernel
# 安装kernel
python -m ipykernel install --name myenv --display-name "Python3 (myenv)"

如果需要查看当前的kernel列表,以及删除已经安装的kernel,可以使用如下的Jupyter命令:

# 查看已经安装的kernel列表
jupyter kernelspec list
# 删除列表中指定的kernel
jupyter kernelspec remove kernelname

6.3. REST API

Jupyter提供了REST API接口来和Jupyter server进行交互。借助REST API的能力,我们可以以编程的方式和Jupyter Server进行交互,灵活地管理Jupyter Server。另外REST API为现代化的软件开发提供了一个优秀的能力:自动化。

借助Jupyter Notebook REST API,可以实现文件的上传和下载,检查点管理,会话管理,内核管理,终端管理等一些列管理能力。完整的Jupyter REST API接口列表可以参考: https://jupyter-server.readthedocs.io/en/latest/developers/rest-api.html

要使用REST API,需要在请求中携带认证信息。Jupyter支持直接把token作为query string的方式来认证,也可以使用标准的Http Authorization头信息来完成认证。使用Authorization头来认证的格式如下:

Authrozation: token 527a9f1430ccfed995ebcf15517583a2547c2469bc3c47a6



Jupyter Notebook入门指南



图8 使用Postman来调用Jupyter REST API接口

6.4. 安全管理与多人协作

Jupyter提供了灵活强大的能力,用以支持在线的交互式文档和代码的编写。但Jupyter项目自身没有提供精细化的安全管理体系,用以支持多用户下灵活地使用Jupyter Notebook的功能。对于文件安全,Jupyter依赖于启动服务的linux用户,合理地配置启动Jupyter的用户的权限,才能保证使用Jupyter的用户,不会对系统或项目造成破坏。Jupyter工作空间的设定,仅起到了方便Jupyter使用者管理必要文件的易用性,不能阻挡用户访问和管理工作空间外的文件系统。另外,配合使用Python虚拟环境,可以防止Jupyter Notebook提供的 pip install ,pip uninstall功能,对现有项目环境造成破坏。

在多人协作方面,JupyterHub项目提供了多人协作Jupyter Notebook和Jupyter lab开发的能力。使用JupyterHub,不同职能的用户可以在自己独立的空间内进行Notebook的编写工作,不同用户间也可以方便地分享各自的Notebook。

7. 扩展Jupyter

7.1. 前端扩展

Jupyter Notebook前端扩展(front end extension)是使用Javascript语言编写的异步模块,可以用来绘制Jupyter界面的仪表盘,Notebook,工具栏等,。定义一个前端扩展必须要实现一个load_ipython_extension方法,当前端控件被加载时,Jupyter client会调用load_ipython_extension方法。

Jupyter Notebook前端扩展能力目前还不是一个稳定的版本,不保证代码能够向后兼容。Jupyter的JS API目前也没有官方的文档,需要通过源代码或者实际加载的JS来查看Jupyter前端脚本的成员和方法。

我们实现一个简单的前端扩展脚本,在jupyter前端的工具条中,添加一个自定义工具,当点击自定义工具时,弹出提示信息。

define([
    'base/js/namespace'
], function(
    Jupyter
) {
    function load_ipython_extension() {

        var handler = function () {
            alert('欢迎使用前端扩展!');
        };

        var action = {
            icon: 'fa-comment-o',
            help    : '前端扩展',
            help_index : 'zz',
            handler : handler
        };
        var prefix = 'my_extension';
        var action_name = 'show-alert';

        var full_action_name = Jupyter.actions.register(action, action_name, prefix); // returns 'my_extension:show-alert'
        Jupyter.toolbar.add_buttons_group([full_action_name]);
    }

    return {
        load_ipython_extension: load_ipython_extension
    };
});

完前端扩展代码后,我们把脚本保存到main.js文件,放置在/opt/my_extension目录下。接下来我们使用jupyter nbextension工具来安装和启用前端扩展

# 安装前端扩展
jupyter nbextension install /opt/my_extension
# 启用前端扩展
jupyter nbextension enable my_extension/main
# 禁用前端扩展
jupyter nbextension disable my_extension/main
# 查看前端扩展列表
jupyter nbextension list
# 卸载前端扩展
jupyter nbextension uninstall my_extension



Jupyter Notebook入门指南



图9 在Notebook工具条中加入的前端扩展

7.2. 服务端扩展

Jupyter服务端扩展(server extension)是使用Python语言编写的模块,可以用来处理发送到Jupyter Server的Http请求。使用Jupyter服务端扩展,可以更改现有Jupyter请求的数据和行为,也可以为jupyter Server定义新的服务处理程序。

定义一个服务端扩展模块要实现一个load_jupyter_server_extension方法,其中包含一个类型为notebook.notebookapp.NotebookApp的参数serverapp,serverapp的详细属性和方法可以通过Jupyter Notebook源代码中的notebookapp.py文件来查看。当服务端扩展被加载时,Jupyter Server会调用load_jupyter_server_extension方法。在load_jupyter_server_extension方法中,我们可以通过调用serverapp的web_app属性的add_handlers方法来注册处理程序,用来处理特定的服务端请求。处理程序类需要继承自Jupyter的IPythonHandler类。在处理程序的方法中,可以使用Jupyter提供的@web.authenticated装饰器来为方法增加身份认证保护。

通过服务端扩展,还可以与前端扩展联动,实现一个功能丰富的Jupyter Notebook前端控件。

# 定义一个处理程序
from tornado import (
    gen, web,
)
from notebook.base.handlers import IPythonHandler

class HelloWorldHandler(IPythonHandler):
    @web.authenticated
    @gen.coroutine
    def get(self):
        self.finish(f'Hello, world!')


# 实现load_jupyter_server_extension方法并注册处理程序
def load_jupyter_server_extension(serverapp):
    handlers = [
        ('/myextension/hello', HelloWorldHandler)
    ]
    serverapp.web_app.add_handlers('.*$', handlers)

完成服务端扩展代码后,我们把代码保存为init.py文件,要在Jupyter Notebook中使用处理程序,我们还需要进行服务端扩展的安装和启用。不同于前端扩展,服务端扩展不能直接使用指令来安装,需要我们手动编写安装程序。此外,Jupyter提供了自动启用服务端扩展和前端扩展的方法,需要我们在脚本的根目录提供启用扩展的配置文件。

jupyter-config/
├── jupyter_notebook_config.d/
│   └── my_server_extension.json
└── nbconfig/
    └── notebook.d/
        └── my_front_extension.json
 setup.py

加入了自动启用扩展的配置,我们的服务端扩展目录结构如下:

hello-extension/
├── __init__.py
jupyter-config/
├── jupyter_notebook_config.d/
    └── hello_extension.json

hello_extension.json文件的内容为:

{
    "ServerApp": {
        "jpserver_extensions": {
            "hello_extension": true
        }
    }
}

接下来我们通过安装程序,安装服务端扩展的信息保存在/root/.jupyter/jupyter_notebook_config.json文件中。在安装完成后,我们可以通过jupyter serverextesion工具来股那里服务端扩展

# 启用服务端扩展
jupyter serverextension enable hello_extension
# 禁用服务端扩展
jupyter serverextension disable hello_extension

# 服务端扩展直接卸载的方法,需要我们通过pip uninstall 卸载安装程序,
# 再通过手工修改/root/.jupyter/jupyter_notebook_config.json文件删除扩展信息来完成卸载



Jupyter Notebook入门指南



图10 在浏览器中测试安装的服务端扩展程序

7.3. 界面定制

Jupyter没有提供标准的界面定制的能力,但我们可以手工调整jupyter生成的模板视图文件和样式文件,达到整条调整jupyter notebook的界面的能力。

Jupyter Notebook模板文件的位置为:$notebook/templates,样式和脚本定制推荐的方案是使用/.jupyter/custom/custom.css和/.jupyter/custom/custom.js文件。我们可以直接在此基础上对文件进行修改,还可以通过extra_template_paths和extra_static_paths来引入其它位置的模板和其它静态文件。



Jupyter Notebook入门指南



图11 通过直接调整模板文件加入的界面定制按钮

7.4. 小部件

小部件(Widgets)是Jupyter交互式可视化数据呈现部件。Jupyter Widgets同时包含了访问后端数据和前端呈现的能力,可以用于在Jupyter Notebook上生动地展示服务端的数据和数据变化。

在v6.5.2稳定版本上,我们目前只能使用系统提供的小部件,还不能开发自定义小部件。在Jupyter notebook7.x版本中,开始提供了小部件的自定义开发能力。

# 确保安装了ipywidgets和traitlets
pip install --upgrade traitlets
pip install --upgrade ipywidgets

# 安装和启用小部件
jupyter nbextension install --py widgetsnbextension
jupyter nbextension enable --py widgetsnbextension

在安装和启用了小部件后,我们可以在notebook中直接使用系统提供的小部件。



Jupyter Notebook入门指南



图12 在Notebook中使用小部件

完整的小部件列表和使用方式可以参考: https://ipywidgets.readthedocs.io/en/7.x/examples/Widget%20List.html

8. 总结

Jupyter Notebook以其丰富的功能,简单易用,强大的交互能力和扩展能力,成为数据科学和机器学习开发中的神器。目前,Jupyter Notebook支持超过40种编程语言,被应用于Google Colab, Kubeflow, 华为云,kaggle等多个知名项目中,大量机器学习和数据科学的论文中使用到了Jupyter。Jupyter在数据可视化,提升工作效率,改善用户体验和丰富文档功能方面显现了巨大的威力。除此之外,Jupyter还提供的灵活强大的扩展能力,更是为Jupyter的深层次使用提供了更广阔的想象空间。如果你还没有开始接触Jupyter,那么就从现在开始吧。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
PPDB:今晚老齐直播
【今晚老齐直播】今晚(本周三晚)20:0021:00小白开始“用”飞桨(https://www.oschina.net/action/visit/ad?id1185)由PPDE(飞桨(https://www.oschina.net/action/visit/ad?id1185)开发者专家计划)成员老齐,为深度学习小白指点迷津。
Peter20 Peter20
3年前
mysql中like用法
like的通配符有两种%(百分号):代表零个、一个或者多个字符。\(下划线):代表一个数字或者字符。1\.name以"李"开头wherenamelike'李%'2\.name中包含"云",“云”可以在任何位置wherenamelike'%云%'3\.第二个和第三个字符是0的值wheresalarylike'\00%'4\
Wesley13 Wesley13
3年前
VBox 启动虚拟机失败
在Vbox(5.0.8版本)启动Ubuntu的虚拟机时,遇到错误信息:NtCreateFile(\\Device\\VBoxDrvStub)failed:0xc000000034STATUS\_OBJECT\_NAME\_NOT\_FOUND(0retries) (rc101)Makesurethekern
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Stella981 Stella981
3年前
Jenkins 插件开发之旅:两天内从 idea 到发布(上篇)
本文首发于:Jenkins中文社区(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fjenkinszh.cn)!huashan(https://oscimg.oschina.net/oscnet/f499d5b4f76f20cf0bce2a00af236d10265.jpg)
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_