• Docker技术入门与实战 第二版-学习笔记-7-数据管理(volume)


    Docker 数据管理

    为什么要进行数据管理呢?因为当我们在使用container时,可能会在里面创建一些数据或文件,但是当我们停掉或删除这个容器时,这些数据或文件也会同样被删除,这是我们并不想看见的事情,所以我们要进行数据管理,实现持久化存储

    在容器中管理数据主要有两种方式:

    • 数据卷(Data volumes)
    • 数据卷容器(Data volume containers)

    一.如果使用的是-v参数的方法指定挂载的数据卷

    其实本质都是在运行docker run命令时,使用-v参数将主机的某个目录作为容器的数据卷。使用-v挂载数据卷时有两种情况:

    • 挂载的是本机的某个指定目录到容器的某个目录上(链接的方式)
    • 使用的是docker自己管理的数据卷volume(存放在/var/lib/docker/volumes下)

    1.第一种情况比如:

    docker run -it -v $(pwd)/hostDirectory:/containerDirectory ubuntu;14.04

    这种情况挂载的就是本机的某个目录到了容器的某个目录上

    这种情况下需要注意下面的几点内容:

    • 本地的目录必须是全路径,即以/或者~/开头的路径,如~/.bash_history:/.bash_history,否则会被当成第二种情况被处理
    • 如果指定的本地目录在本地不存在时,docker会在本地自动创建该目录
    • 如果指定的容器目录在容器中不存在时,docker会在容器中自动创建该目录
    • 如果指定的容器目录中已经有内容的话,这些内容都会被本地目录中的内容覆盖掉

    2.第二种情况就是比如下面的一个例子:

    userdeMacBook-Pro:~ user$ docker run -d -P --name web -v /webapp training/webapp python app.py

    其并没有指定要连接本地的什么目录,所以docker就会自动创建一个匿名的volume,并将其挂载到容器中的/webapp目录上,从其返回的挂载信息可见:

             "Mounts": [
                {
                    "Type": "volume",
                    "Name": "2e8085436ec33315c8c034a87097df301e9ce1c04a9a9f5ce007344c6cef3105",
                    "Source": "/var/lib/docker/volumes/2e8085436ec33315c8c034a87097df301e9ce1c04a9a9f5ce007344c6cef3105/_data",
                    "Destination": "/webapp",
                    "Driver": "local",
                    "Mode": "",
                    "RW": true,
                    "Propagation": ""
                }
            ],

    可见其匿名生成了一个2e8085436ec33315c8c034a87097df301e9ce1c04a9a9f5ce007344c6cef3105/_data目录,将其挂载到了容器的/webapp目录上

    除了docker自动帮我们创建volume外,我们还可以自己创建并查看相应的信息:

    userdeMBP:~ user$ docker volume create myVolume
    myVolume
    userdeMBP:~ user$ docker volume ls
    DRIVER              VOLUME NAME
    local               6dcce8fa7b5bed65b8bdf8cb91603cfa4e993439ec20e921a6742808b6478637
    local               49bfc006ae2ebc343cdf59c0af674d7e16f70466f2e9a186ac34e61deb0cb027
    local               71a809c56c488bc15078d7b715ddda461eef698643bec14fe53237e66c12eb62
    local               933a418456d65bafb6f77989ea120a2da8dd62c7efccd2828d2e716e3111c530
    local               myVolume
    userdeMBP:~ user$ docker volume inspect myVolume
    [
        {
            "CreatedAt": "2018-12-18T09:23:22Z",
            "Driver": "local",
            "Labels": {},
            "Mountpoint": "/var/lib/docker/volumes/myVolume/_data",
            "Name": "myVolume",
            "Options": {},
            "Scope": "local"
        }
    ]

    然后就能够使用这个数据卷,将其挂载到相应的容器中:

    docker run -it -v myVolume:/mydata ubuntu:14.04

    需要注意的内容是:

    如果volume是空的,容器目录非空,那么docker会将容器目录里面的内容拷贝到volume中

    如果volume中已经有内容,不管容器中有没有内容,都会将容器中的内容覆盖

    二.使用的是Dockerfile

    1.

    当然,除了使用-v参数外,还能够在Dockerfile中使用VOLUME挂载数据卷

    #Dockerfile
    VOLUME /mydata

    这样docker也会创建一个匿名的volume,然后将其挂载到相应容器的/mydata目录上,这样,如果/mydata中是有数据的,将会拷贝到匿名volume中

    它的效果和docker run -it -v /mydata imageName是一样的

    因为Dockerfile的方法每一次运行时都会自动生成一个匿名的volume,如果你想要在不同的容器之间共享数据的话,还是要使用上面自己创建一个myVolume,然后将其挂载的方法

    docker run -it -v myVolume:/mydata ubuntu:14.04

    2.

    ⚠️需要注意的一点:

    在Dockerfile的VOLUME指令后面是不能对该volume进行任何更改的,比如:

    FROM debian:wheezy
    RUN useradd foo
    VOLUME /data
    RUN touch /data/x  //生成新文件夹
    RUN chown -R foo:foo /data  //更改权限

    上面执行的结果与你预期是不符的,我们本来希望touch命令在镜像的文件系统上运行,但是实际上它是在一个临时容器的Volume上运行,所以应该做的是:

    FROM debian:wheezy
    RUN useradd foo
    RUN mkdir /data && touch /data/x
    RUN chown -R foo:foo /data
    VOLUME /data

    3.当我们的镜像是通过Dockerfile生成的,如果在Dockerfile中使用的本地目录下的代码需要更改时,为了不用每次更改都要重新运行build去生成一个新的镜像,方法就是将将代码所在的相应目录挂在到数据卷上,比如:

    COPY . /mydata  #把当前的目录拷贝到 skeleton
    WORKDIR /mydata

    解决办法就是运行镜像时,写成:

    docker run -d -p 80:5000 -v $(pwd):/mydata ubuntu:14.04

    使用-v $(pwd):/mydata,这样$(pwd)中的数据就会与容器/mydata中的数据同步,这样修改时,会自动刷新数据

    下面是比较详细的讲解

    1) 数据卷

    数据卷是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:

    • 数据卷可以在容器之间共享和重用
    • 对数据卷的修改会立马生效
    • 对数据卷的更新,不会影响镜像
    • 数据卷默认会一直存在,即使容器被删除

    ⚠️数据卷的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会隐藏掉,能显示看的是挂载的数据卷。

    创建一个数据卷

    在用 docker run命令的时候,使用 -v 标记来创建一个数据卷并挂载到容器里。

    在一次 run 中多次使用可以挂载多个数据卷。

    userdeMacBook-Pro:~ user$ docker run -d -P --name web -v /webapp training/webapp python app.py
    Unable to find image 'training/webapp:latest' locally
    latest: Pulling from training/webapp
    e190868d63f8: Pull complete 
    909cd34c6fd7: Pull complete 
    0b9bfabab7c1: Pull complete 
    a3ed95caeb02: Pull complete 
    10bbbc0fc0ff: Pull complete 
    fca59b508e9f: Pull complete 
    e7ae2541b15b: Pull complete 
    9dd97ef58ce9: Pull complete 
    a4c1b0cb7af7: Pull complete 
    Digest: sha256:06e9c1983bd6d5db5fba376ccd63bfa529e8d02f23d5079b8f74a616308fb11d
    Status: Downloaded newer image for training/webapp:latest
    1ad71e9d38081c38dcd355fd7a438cd9f94503e8bc28e99a48c0738b51e7efef

    注意:也可以在 Dockerfile 中使用 VOLUME来添加一个或者多个新的卷到由该镜 像创建的任意容器。

    删除数据卷

    数据卷是被设计用来持久化数据的,它的生命周期独立于容器,Docker不会在容器 被删除后自动删除数据卷,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的数据卷。

    如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v 这个命令。

    挂载一个主机目录作为数据卷

    使用 -v 标记也可以指定挂载一个本地主机的目录到容器中去

    docker run -d -P --name web -v /src/webapp:/opt/webapp training/webapp python app.py

    上面的命令加载主机的 /src/webapp目录到容器的 /opt/webapp目录。

    这个功能在进行测试的时候十分方便,比如用户可以放置一些程序到本地目录/src/webapp中,来查看容器是否正常工作。

    本地目录的路径必须是绝对路径,如果目录不存在 Docker 会自动为你创建它。

    ⚠️Dockerfile 中不支持这种用法,这是因为 Dockerfile 是为了移植和分享用 的。然而,不同操作系统的路径格式不一样,所以目前还不能支持。

    Docker 挂载数据卷的默认权限是读写,用户也可以通过 :ro 指定为只读。

    docker run -d -P --name web -v /src/webapp:/opt/webapp:ro training/webapp python app.py

    查看数据卷的具体信息

    查看的是上面运行的docker run -d -P --name web -v /webapp training/webapp python app.py命令生成的volume

    userdeMacBook-Pro:~ user$ docker inspect web
    [
        {
            "Id": "1ad71e9d38081c38dcd355fd7a438cd9f94503e8bc28e99a48c0738b51e7efef",
            "Created": "2018-12-15T09:34:23.600548467Z",
            "Path": "python",
            "Args": [
                "app.py"
            ],
    
            ...
    
             "Mounts": [
                {
                    "Type": "volume",
                    "Name": "2e8085436ec33315c8c034a87097df301e9ce1c04a9a9f5ce007344c6cef3105",
                    "Source": "/var/lib/docker/volumes/2e8085436ec33315c8c034a87097df301e9ce1c04a9a9f5ce007344c6cef3105/_data",
                    "Destination": "/webapp",
                    "Driver": "local",
                    "Mode": "",
                    "RW": true,
                    "Propagation": ""
                }
            ],
    
            ...

    在输出的内容中找到其中和数据卷相关的部分,可以看到所有的数据卷都是创建在主机的 /var/lib/docker/volumes/下面的

    挂载一个本地主机文件作为数据卷——不推荐

    -v 标记也可以从主机挂载单个文件到容器中:

    docker run --rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash
    这样就可以记录在容器输入过的命令了
    

    ⚠️如果直接挂载一个文件,很多文件编辑工具,包括 vi 或者sed --in-place ,可能会造成文件 inode 的改变,从 Docker 1.1 .0起,这会导致报错误信息。

    所以最简单的办法就直接挂载文件的父目录。

    2) 数据卷容器——实现持续更新数据在容器间共享

    数据卷容器,其实就是一个正常的容器,专门用来提供数据卷供其它容器挂载的。

    首先,创建一个名为 dbdata 的数据卷容器:

    docker run -d -v /dbdata --name dbdata training/postgres echo Data-only container for postgres

    然后其他的容器就能够使用--volumes-from来挂载dbdata容器中的数据卷/dbdata :

    $docker run -d --volumes-from dbdata --name db1 training/postgres
    $docker run -d --volumes-from dbdata --name db2 training/postgres

    一个容器可以使用超过一个的 --volumes-from参数来指定从多个容器挂载不同的数据卷

    也可以挂载其他已经挂载了数据卷容器的容器来实现级联挂载数据卷:

    docker run -d --name db3 --volumes-from db1 training/postgres

    ⚠️:使用--volumes-from参数所挂载数据卷的容器自己不需要保持在运行状态

    如果删除了挂载的容器(包括 dbdatadb1 db2),数据卷并不会被自动删除。

    所以如果想要删除一个数据卷,必须在删除最后一个还挂载着它的容器时使用docker rm -v命令来指定同时要删除的数据卷

    3)利用数据卷容器来备份、恢复、迁移数据卷

    备份

    首先使用 --volumes-from标记来创建一个加载 dbdata 容器卷的容器,并从主机挂载当前目录到容器的 /backup 目录,如:

    docker run --volumes-from dbdata -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata

    容器启动后,使用了 tar命令来将 /dbdata 卷备份为容器中的/backup/backup.tar 文件,同时也就会在主机当前目录下生成名为 backup.tar的文件。

    恢复

    如果要恢复数据到一个容器,首先创建一个带有空数据卷的容器 dbdata2

    docker run -v /dbdata --name dbdata2 ubuntu /bin/bash

    然后创建另一个容器busybox,挂载 dbdata2 容器卷中的数据卷,也指定将数据卷中的内容挂载到容器中的/backup下,这样该容器的/backup下就有了backup.tar文件,然后使用untar解压备份文件/backup/backup.tar到挂载的容器卷中:

    docker run --volumes-from dbdata2 -v $(pwd):/backup busybox tar xvf /backup/backup.tar

    为了查看/验证恢复的数据,可以再启动一个容器busybox挂载同样的容器卷,然后运行ls /dbdata来查看:

    docker run --volumes-from dbdata2 busybox /bin/ls /dbdata

     4)学习docker volume命令的使用

    1> 首先查看该命令的使用方式

    userdeMBP:~ user$ docker volume --help  //查看该命令的使用方式
    
    Usage:    docker volume COMMAND
    
    Manage volumes //管理数据卷
    
    Commands:
      create      Create a volume ,创建一个数据卷
      inspect     Display detailed information on one or more volumes 展示一个或多个数据卷的详细信息
      ls          List volumes 列举出所有现有的数据卷
      prune       Remove all unused local volumes 移除所有现在并没有在使用的数据卷
      rm          Remove one or more volumes 移除指定的一个或多个数据卷
    
    Run 'docker volume COMMAND --help' for more information on a command.

    2> 列举查看本地现有的volume

    userdeMBP:~ user$ docker volume ls //查看之前生成的数据卷,都是匿名数据卷
    DRIVER              VOLUME NAME
    local               2e8085436ec33315c8c034a87097df301e9ce1c04a9a9f5ce007344c6cef3105
    local               4f059cc319550b606b40d1198866fcc8822267a9d6631c8f44a24619d4fe312e
    local               6dcce8fa7b5bed65b8bdf8cb91603cfa4e993439ec20e921a6742808b6478637
    local               49bfc006ae2ebc343cdf59c0af674d7e16f70466f2e9a186ac34e61deb0cb027
    local               71a809c56c488bc15078d7b715ddda461eef698643bec14fe53237e66c12eb62
    local               933a418456d65bafb6f77989ea120a2da8dd62c7efccd2828d2e716e3111c530
    local               a49c6e5e7497f3d1bfcb11159a5144030e03fd7cba21e3bd6ac9b12cc6e8ea76

    3> 详细查看其中某一个volume的信息:

    userdeMBP:~ user$ docker volume inspect 2e8085436ec33315c8c034a87097df301e9ce1c04a9a9f5ce007344c6cef3105 //详细查看其中某一个数据卷的信息
    [
        {
            "CreatedAt": "2018-12-15T09:34:23Z",
            "Driver": "local",
            "Labels": null,
            "Mountpoint": "/var/lib/docker/volumes/2e8085436ec33315c8c034a87097df301e9ce1c04a9a9f5ce007344c6cef3105/_data",
            "Name": "2e8085436ec33315c8c034a87097df301e9ce1c04a9a9f5ce007344c6cef3105",
            "Options": null,
            "Scope": "local"
        }
    ]

    4> 将其中不在使用的本地数据卷给删除

    userdeMBP:~ user$ docker volume prune   //将其中不在使用的本地数据卷给删除 
    WARNING! This will remove all local volumes not used by at least one container.
    Are you sure you want to continue? [y/N] y
    Deleted Volumes:  //这是被删除掉的数据卷
    a49c6e5e7497f3d1bfcb11159a5144030e03fd7cba21e3bd6ac9b12cc6e8ea76
    2e8085436ec33315c8c034a87097df301e9ce1c04a9a9f5ce007344c6cef3105
    4f059cc319550b606b40d1198866fcc8822267a9d6631c8f44a24619d4fe312e
    
    Total reclaimed space: 77.23kB
    userdeMBP:~ user$ docker volume ls //这些是还剩下的数据卷
    DRIVER              VOLUME NAME
    local               6dcce8fa7b5bed65b8bdf8cb91603cfa4e993439ec20e921a6742808b6478637
    local               49bfc006ae2ebc343cdf59c0af674d7e16f70466f2e9a186ac34e61deb0cb027
    local               71a809c56c488bc15078d7b715ddda461eef698643bec14fe53237e66c12eb62
    local               933a418456d65bafb6f77989ea120a2da8dd62c7efccd2828d2e716e3111c530

    5>自己手动创建一个非匿名的volume

    userdeMBP:~ user$ docker volume create myVolume 然后可以创建一个非匿名的数据卷myVolume
    myVolume
    userdeMBP:~ user$ docker volume ls
    DRIVER              VOLUME NAME
    local               6dcce8fa7b5bed65b8bdf8cb91603cfa4e993439ec20e921a6742808b6478637
    local               49bfc006ae2ebc343cdf59c0af674d7e16f70466f2e9a186ac34e61deb0cb027
    local               71a809c56c488bc15078d7b715ddda461eef698643bec14fe53237e66c12eb62
    local               933a418456d65bafb6f77989ea120a2da8dd62c7efccd2828d2e716e3111c530
    local               myVolume
    userdeMBP:~ user$ docker volume inspect myVolume
    [
        {
            "CreatedAt": "2018-12-18T09:23:22Z",
            "Driver": "local",
            "Labels": {},
            "Mountpoint": "/var/lib/docker/volumes/myVolume/_data",
            "Name": "myVolume",
            "Options": {},
            "Scope": "local"
        }
    ]

    6)删除容器

    userdeMBP:~ user$ docker volume inspect 6dcce8fa7b5bed65b8bdf8cb91603cfa4e993439ec20e921a6742808b6478637
    [
        {
            "CreatedAt": "2018-12-15T07:28:50Z",
            "Driver": "local",
            "Labels": null,
            "Mountpoint": "/var/lib/docker/volumes/6dcce8fa7b5bed65b8bdf8cb91603cfa4e993439ec20e921a6742808b6478637/_data",
            "Name": "6dcce8fa7b5bed65b8bdf8cb91603cfa4e993439ec20e921a6742808b6478637",
            "Options": null,
            "Scope": "local"
        }
    ]
    userdeMBP:~ user$ docker volume rm 6dcce8fa7b5bed65b8bdf8cb91603cfa4e993439ec20e921a6742808b6478637 
    Error response from daemon: remove 6dcce8fa7b5bed65b8bdf8cb91603cfa4e993439ec20e921a6742808b6478637: volume is in use - [b4a512f0230fa598e7e40171e0597ea6db0740105449314a1a3767dcaa4a0edb]
    userdeMBP:~ user$ docker inspect b4a512f0230fa598e7e40171e0597ea6db0740105449314a1a3767dcaa4a0edb //该容器正在运行,所以其挂载的数据卷是不能被删除的
    [
        {
            "Id": "b4a512f0230fa598e7e40171e0597ea6db0740105449314a1a3767dcaa4a0edb",
            "Created": "2018-12-15T07:28:26.800359991Z",
            "Path": "/entrypoint.sh",
            "Args": [
                "/etc/docker/registry/config.yml"
            ],
            "State": {
                "Status": "running",
                "Running": true,
                "Paused": false,
                "Restarting": false,
                "OOMKilled": false,
                "Dead": false,
                "Pid": 3006,
                "ExitCode": 0,
                "Error": "",
                ...
    
    userdeMBP:~ user$ docker ps 
    CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
    2084e92eea8c        ubuntu:14.04        "/bin/bash"              7 hours ago         Up 7 hours                                   boring_jackson
    b4a512f0230f        registry            "/entrypoint.sh /etc…"   3 days ago          Up 8 hours          0.0.0.0:5000->5000/tcp   registry
    
    userdeMBP:~ user$ docker volume rm myVolume //然后删除一个没有使用的数据卷就成功了
    myVolume

    5)现在我们进行一个例子小实验:

    1> 首先创建一个mysql1容器,生成mysql数据卷挂载在该容器的/var/lib/mysql目录上

    userdeMBP:~ user$ docker run -d -v mysql:/var/lib/mysql --name mysql1 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
    Unable to find image 'mysql:latest' locally
    latest: Pulling from library/mysql
    a5a6f2f73cd8: Already exists 
    936836019e67: Pull complete 
    283fa4c95fb4: Pull complete 
    1f212fb371f9: Pull complete 
    e2ae0d063e89: Pull complete 
    5ed0ae805b65: Pull complete 
    0283dc49ef4e: Pull complete 
    a7e1170b4fdb: Pull complete 
    88918a9e4742: Pull complete 
    241282fa67c2: Pull complete 
    b0fecf619210: Pull complete 
    bebf9f901dcc: Pull complete 
    Digest: sha256:b7f7479f0a2e7a3f4ce008329572f3497075dc000d8b89bac3134b0fb0288de8
    Status: Downloaded newer image for mysql:latest
    71f78959d2a823e55a723cde747ee833f52138bf04f0f8ec33189ea902bb2356

    2>查看生成的mysql数据卷

    userdeMBP:~ user$ docker volume ls //现在可见生成了一个mysql数据卷
    DRIVER              VOLUME NAME
    local               6dcce8fa7b5bed65b8bdf8cb91603cfa4e993439ec20e921a6742808b6478637
    local               49bfc006ae2ebc343cdf59c0af674d7e16f70466f2e9a186ac34e61deb0cb027
    local               71a809c56c488bc15078d7b715ddda461eef698643bec14fe53237e66c12eb62
    local               933a418456d65bafb6f77989ea120a2da8dd62c7efccd2828d2e716e3111c530
    local               mysql
    userdeMBP:~ user$ docker volume inspect mysql //查看该数据卷的详细内容
    [
        {
            "CreatedAt": "2018-12-18T10:21:16Z",
            "Driver": "local",
            "Labels": null,
            "Mountpoint": "/var/lib/docker/volumes/mysql/_data",
            "Name": "mysql",
            "Options": null,
            "Scope": "local"
        }
    ]

    3> 然后对朝该数据卷中写入数据

    userdeMBP:~ user$ docker exec -it mysql1 /bin/bash
    root@71f78959d2a8:/# mysql -u root
    Welcome to the MySQL monitor.  Commands end with ; or g.
    Your MySQL connection id is 8
    Server version: 8.0.13 MySQL Community Server - GPL
    
    Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
    
    Oracle is a registered trademark of Oracle Corporation and/or its
    affiliates. Other names may be trademarks of their respective
    owners.
    
    Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.
    
    mysql> show databases;
    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | mysql              |
    | performance_schema |
    | sys                |
    +--------------------+
    4 rows in set (0.00 sec)
    
    mysql> create database docker; //生成一个新的数据库
    Query OK, 1 row affected (0.08 sec)
    
    mysql> show databases;
    +--------------------+
    | Database           |
    +--------------------+
    | docker             |
    | information_schema |
    | mysql              |
    | performance_schema |
    | sys                |
    +--------------------+
    5 rows in set (0.01 sec)
    
    mysql> exit
    Bye
    root@71f78959d2a8:/# exit
    exit
    userdeMBP:~ user$ docker rm -f mysql1 //然后移除mysql1这个容器
    mysql1

    4> 然后重新打开一个mysql2容器,挂载之前生成的mysql数据卷,以验证数据是否成功共享:

    userdeMBP:~ user$ docker run -d -v mysql:/var/lib/mysql --name mysql2 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
    6f242e6fc8341f2b809f2d54bef294e718658a3db8c595dfc84955b5b4b0f7a8
    
    userdeMBP:~ user$ docker exec -it mysql2 /bin/bash
    root@6f242e6fc834:/# mysql -u root
    Welcome to the MySQL monitor.  Commands end with ; or g.
    Your MySQL connection id is 8
    Server version: 8.0.13 MySQL Community Server - GPL
    
    Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
    
    Oracle is a registered trademark of Oracle Corporation and/or its
    affiliates. Other names may be trademarks of their respective
    owners.
    
    Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.
    
    mysql> show databases; //然后查看数据库,是可以看见之前在容器mysql1上生成的docker数据库的,可见数据成功共享了
    +--------------------+
    | Database           |
    +--------------------+
    | docker             |
    | information_schema |
    | mysql              |
    | performance_schema |
    | sys                |
    +--------------------+
    5 rows in set (0.01 sec)
    
    mysql> 
  • 相关阅读:
    习题2.4 递增的整数序列链表的插入(15 分)浙大版《数据结构(第2版)》题目集
    杭电 KazaQ's Socks
    B. Beautiful Paintings
    java之接口
    java之模板方法设计模式
    java之抽象类
    java之final关键字
    java之匿名内部类
    jvaa之初始化块
    java之单例设计模式
  • 原文地址:https://www.cnblogs.com/wanghui-garcia/p/10124159.html
Copyright © 2020-2023  润新知