# MySQL数据库说明

# 开启

云虚拟主机在创建时自动开启一个MySQL数据库,因此您不需要单独开启数据库服务。

# 查询数据库的连接信息

进入主机的管理中心,选择左侧“数据库管理”将进入数据库的管理页面,如图所示:

数据库连接信息

数据库连接信息:

选项
主机地址 w.rdc.sae.sina.com.cn
端口 3306
数据库名称 app_XXX(XXX时虚拟主机的名称,例如wafwiki)
数据库用户名 点击上图 数据库用户名 后的 显示查询
数据库密码 点击上图 数据库密码 后的 显示查询

# 使用phpMyAdmin管理数据库

云虚拟主机的数据库只能使用 phpMyAdmin 管理,不能使用本地的MySQL客户端管理。

从应用的管理中心,选择左侧“数据库管理”将进入数据库的管理页面,如图所示:

数据库连接信息

点击“phpMyAdmin”可以进入phpMyAdmin管理面板,如图所示:

进入phpMyAdmin管理面板

# 数据库权限

当前数据库开放的权限列表为:

select, insert, update, delete, create table, alter table, drop table, index

可以执行以上的操作。

# 导入数据库

云虚拟主机在管理中心提供了数据库的导入功能,请使用这个功能导入数据库。

# 不要从phpMyAdmin导入数据

从phpMyAdmin导入提示

因为phpMyAdmin的运行环境有运行时长和内存的限制,因此不要从phpMyAdmin导入。

# 从云应用SAE的应用数据库导入

云虚拟主机支持从云应用SAE的数据库导入:

  1. 登陆云虚拟主机的管理平台(https://scs.sinacloud.com
  2. 进入主机的管理面板(点击主机列表的详情 进入) 进入虚拟主机的管理中心
  3. 选择左侧导航条数据库管理 选择数据库管理导航
  4. 点击 数据导入导出 功能的 “数据导入”标签,点击“+新建导入任务” 新增导入任务
  5. 数据源选择“共享型数据库” 选择共享型数据库
  6. 选择需要导入的应用 选择需要导入的应用名
  7. 选择导入“整个数据库”或者“数据表” 选择是导入数据库还是数据表
  8. 添加任务完成,在任务列表处查询导入的状态 查询导入的任务状态

# 从外部数据库导入

云虚拟主机支持从外部数据库直接导入数据,外部的数据库需要从公网能直接连接。

  1. 登陆云虚拟主机的管理平台(https://scs.sinacloud.com
  2. 进入主机的管理面板(点击主机列表的详情 进入) 进入虚拟主机的管理中心
  3. 选择左侧导航条数据库管理 选择数据库管理导航
  4. 点击 数据导入导出 功能的 “数据导入”标签,点击“+新建导入任务” 新增导入任务
  5. 数据源选择“外部数据库” 选择外部数据库
  6. 添加任务完成,在任务列表处查询导入的状态 查询导入的任务状态

# 从文件链接导入

从文件链接(SQL或者SQL的压缩文件)添加导入任务,文件支持sql、zip、rar、tar.gz。

提示

  • 请确保填写的地址可以从公网访问,否则会导入失败
  • 压缩文件需要是SQL文件的压缩包,压缩包中只支持单个的SQL文件
  1. 登陆云虚拟主机的管理平台(https://scs.sinacloud.com
  2. 进入主机的管理面板(点击主机列表的详情 进入) 进入虚拟主机的管理中心
  3. 选择左侧导航条数据库管理 选择数据库管理导航
  4. 点击 数据导入导出 功能的 “数据导入”标签,点击“+新建导入任务” 新增导入任务
  5. 数据源选择“文件链接” 从外部链接导入
  6. 添加任务完成,在任务列表处查询导入的状态 查询导入的任务状态

# 从SQL文件导入

云虚拟主机支持从SQL文件导入数据,可以在线上传SQL文件导入,除SQL文件外,也可以上传zip、rar、tar.gz格式的SQL压缩文件。

说明

仅支持单个SQL文件的压缩文件,如果有多个SQL文件需要上传,请分多次添加导入的任务。

  1. 登陆云虚拟主机的管理平台(https://scs.sinacloud.com
  2. 进入主机的管理面板(点击主机列表的详情 进入) 进入虚拟主机的管理中心
  3. 选择左侧导航条数据库管理 选择数据库管理导航
  4. 点击 数据导入导出 功能的 “数据导入”标签,点击“+新建导入任务” 新增导入任务
  5. 数据源选择“SQL文件” 选择SQL文件
  6. 添加任务完成,在任务列表处查询导入的状态 查询导入的任务状态

# 数据库导出

# 添加导出任务

云虚拟主机支持在线添加数据库导出的任务。

  1. 登陆云虚拟主机的管理平台(https://scs.sinacloud.com
  2. 进入主机的管理面板(点击主机列表的详情 进入) 进入虚拟主机的管理中心
  3. 选择左侧导航条数据库管理 选择数据库管理导航
  4. 点击 数据导入导出 功能的 “数据导出”标签,点击“+新建导出任务” 新增导出任务
  5. 选择导出的类型,整个数据库或者选择部分数据表 选择数据导出的类型,数据库或者数据表
  6. 添加任务完成,在任务列表处查询导出的状态,导出任务执行完成后,从“其他”的“点击下载”可以下载导出的SQL文件。 查看导出任务的列表

# 数据库的限制

# 物理限制

MySQL 数据库服务通过 RDC(我们为共享数据库服务开发的高性能 MySQL 代理)和 MySQL 本身的认证机制来实现数据库和数据库之间的隔离。所有的数据库连接会首先连接 RDC,在 RDC 认证通过之后,由 RDC 将连接代理给实际的后端数据库。

RDC 的功能主要如下:

  • 通过其强隔绝性为数据库提供更高的安全性,保障您的数据安全
  • 通过预判用户执行的 SQL 语句,提前拦截可能损伤系统的 SQL 语句

RDC 会对所有的 SQL 语句执行以下预判,如果超过了其限制,RDC 会直接拦截这个 SQL 查询,返回查询错误,您可以根据 MySQL 错误码判断该请求是否是被 RDC 拦截以及拦截的原因。

预判条件 相关错误信息 限额
单表的最大行数 Table has too many rows 10,000,000 行
库的最大表数量 Database has too many tables 512个
不支持的存储引擎类型 Not support table type memory, temporary
不支持的内置函数 Not support function sleep, benchmark
最大外排序的行数 Filesort on too many rows 100,000 行
最大无索引的操作行数 Select on too many rows without index 300,000 行
查询的最大操作行数 Select on too many rows 1,000,000 行
更新的最大操作行数 Update on too many rows 1,000,000 行
创建索引时允许的表的最大行数 Create index on big table 500,000 行
修改表结构时允许的表的最大行数 Alter table on big table 500,000 行

说明

RDC 的预判和拦截是针对 Web 应用的使用场景来优化的,如果您的数据库表结构、索引、SQL 语句等合理的话是不会触发这些拦截的,相反,如果出现这些拦截,您就需要根据对应的错误码以及其优化建议对你的数据库或者 SQL 语句去做相应的优化

以下是被 RDC 拦截返回的错误码说明以及建议:

错误码 错误信息 说明 建议
13000 Not support multi statements 不支持一个字符串多条 SQL 语句
13001 Select on too many rows 查询的表记录超过了限制 优化 SQL 语句,减少扫描行
13002 Update on too many rows 更新的表记录超过了限制 优化 SQL 语句,减少扫描行
13003 Delete on too many rows 删除的表记录超过了限制 减少 SQL 语句,减少扫描行
13004 Create index on big table 在一个过大的表上创建索引 执行分表操作
13005 Alter table on big table 在一个过大的表上改变表结构 优化业务逻辑,分表
13006 Operations take too much time cost 超过 SQL 并发执行时间和 优化 SQL 语句,减少SQL的执行时间
13007 Filesort on too many rows SQL 导致高时间复杂度的外排序 优化 SQL 语句,合理加索引
13008 Table has too many rows 单表行数超过规定上限 分表以降低表内的记录数
13009 Database has too many tables 用户当前表数目已达到规定上限 删除无用的表,降低表的数量
13010 Not support table type 试图创建不支持的表类型 了解支持的表类型
13011 Not support table optimization 试图执行 optimize table 语句 去掉该语句
13012 Not support function 试图执行禁用函数 不执行该函数
13013 Scanned too many databases when querying INFORMATION_SCHEMA 查询 INFORMATION_SCHEMA 时导致过多的跨库扫描 查询时 INFORMATION_SCHEMA 时显式指明库和表
13014 Too complicated sql case uncacheable 过于复杂的语句导致不可被 cache 降低语句复杂度
13016 Not support show databases 不支持 show databases 不要调用 show databases
13017 Select on too many rows without index 查询时过于复杂且不带索引 请使用索引查询
13018 Impossible where that may caused by sql injection 出现永远不可能的条件语句 有可能是被 SQL 注入导致 检查该语句是否有安全隐患
13019 Invalid operate information_schema 对 information_schema 操作不当 避免对 information_schema 的操作
13044 Backends connection error 连接时出现未知错误 稍后重试,连续失败时,请向官方反馈
13045 Backends connection timeout 连接时超时 稍后重试,连续失败时,请向官方反馈
13046 No available backends 没有可用的后端 请向官方反馈
13047 Be banned (maybe out of quota) 因为慢查询过多导致被禁用 优化SQL

请注意

扫描行数过多,这里的考虑因素有表结构、表行数、带没带索引、有没有 limit、有没有 join 等,请根据具体情况具体分析,从phpMyAdmin执行explain SQL语句可以查询语句的执行计划。

# 分钟配额限制

云虚拟主机的MySQL服务的流入流量、流出流量、请求次数受您的账户等级(http://www.sinacloud.com/index/price.html#level)限制,提升账户等级(http://www.sinacloud.com/ucenter/account.html)可以增加配额。

注意

  • 当MySQL服务的分钟使用量达到配额的70%时,系统会发给您发送邮件和站内信报警,请及时关注;
  • 超出配额后,系统会自动禁用MySQL服务几分钟,会自动解禁,如果短期内频繁超配,则禁用的时间会逐渐增加。

# PHP SDK

以下PHP SDK是新浪云提供的数据库操作封装,提供了

  • 批量查询数据
  • 查询某一行数据
  • 查询某个遍历
  • 执行SQL查询
  • 过滤参数

功能。

# SDK源码

<?php

/**
 * Sch Mysql Class
 *
 * <code>
 * <?php
 * $mysql = new SchMysql();
 *
 * $sql = "SELECT * FROM `user` LIMIT 10";
 * $data = $mysql->getData( $sql );
 * $name = strip_tags( $_REQUEST['name'] );
 * $age = intval( $_REQUEST['age'] );
 * $sql = "INSERT  INTO `user` ( `name` , `age` , `regtime` ) VALUES ( '"  . $mysql->escape( $name ) . "' , '" . intval( $age ) . "' , NOW() ) ";
 * $mysql->runSql( $sql );
 * if( $mysql->errno() != 0 )
 * {
 *    die( "Error:" . $mysql->errmsg() );
 * }
 *
 * $mysql->closeDb();
 * ?>
 * </code>
 *
 *
 */
class SchMysql
{
    private $host;
    private $port;
    private $username;
    private $password;
    private $db_name;
    private $charset;

    /**
     * 构造函数
     * SchMysql constructor.
     * @param $host
     * @param $port
     * @param $username
     * @param $password
     * @param $db_name
     * @param string $charset
     */
    function __construct($host, $port, $username, $password, $db_name, $charset = 'utf8')
    {
        $this->host = $host;
        $this->port = $port;
        $this->username = $username;
        $this->password = $password;
        $this->db_name = $db_name;
        $this->charset = $charset;
    }

    /**
     * 重新设置用户名和密码
     * @param $username
     * @param $password
     */
    public function setAuth($username, $password)
    {
        $this->username = $username;
        $this->password = $password;
    }


    /**
     * 设置主机和端口
     * @param $host
     * @param $port
     */
    public function setHostAndPort($host, $port)
    {
        $this->port = intval($port);
        $this->host = $host;

    }

    /**
     * 设置数据库名称
     * @param $db_name
     */
    public function setDbName($db_name)
    {
        $this->db_name = $db_name;
    }


    /**
     * 设置当前连接的字符集 , 必须在发起连接之前进行设置
     *
     * @param string $charset 字符集,如GBK,GB2312,UTF8
     * @return void
     */
    public function setCharset($charset)
    {
        return $this->set_charset($charset);
    }

    /**
     * 同setCharset,向前兼容
     *
     * @param string $charset
     * @return void
     * @ignore
     */
    public function set_charset($charset)
    {
        $this->charset = $charset;
    }

    /**
     * 运行Sql语句,不返回结果集
     *
     * @param string $sql
     * @return mysqli_result|bool
     */
    public function runSql($sql)
    {
        return $this->run_sql($sql);
    }

    /**
     * 同runSql,向前兼容
     *
     * @param string $sql
     * @return bool
     * @ignore
     */
    public function run_sql($sql)
    {
        $this->last_sql = $sql;
        $db_link = $this->db_write();
        if ($db_link === false) {
            return false;
        }
        $ret = mysqli_query($db_link, $sql);
        $this->save_error($db_link);
        return $ret;
    }

    /**
     * 运行Sql,以多维数组方式返回结果集
     *
     * @param string $sql
     * @return array 成功返回数组,失败时返回false
     */
    public function getData($sql)
    {
        return $this->get_data($sql);
    }

    /**
     * 同getData,向前兼容
     *
     * @ignore
     */
    public function get_data($sql)
    {
        $this->last_sql = $sql;
        $data = Array();
        $i = 0;
        $db_link = $this->do_replication ? $this->db_read() : $this->db_write();
        if ($db_link === false) {
            return false;
        }
        $result = mysqli_query($db_link, $sql);

        $this->save_error($db_link);

        if (is_bool($result)) {
            return $result;
        } else {
            while ($Array = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
                $data[$i++] = $Array;
            }
        }

        mysqli_free_result($result);

        if (count($data) > 0)
            return $data;
        else
            return NULL;
    }

    /**
     * 运行Sql,以数组方式返回结果集第一条记录
     *
     * @param string $sql
     * @return array 成功返回数组,失败时返回false
     */
    public function getLine($sql)
    {
        return $this->get_line($sql);
    }

    /**
     * 同getLine,向前兼容
     *
     * @param string $sql
     * @return array
     * @ignore
     */
    public function get_line($sql)
    {
        $data = $this->get_data($sql);
        if ($data) {
            return @reset($data);
        } else {
            return false;
        }
    }

    /**
     * 运行Sql,返回结果集第一条记录的第一个字段值
     *
     * @param string $sql
     * @return mixed 成功时返回一个值,失败时返回false
     */
    public function getVar($sql)
    {
        return $this->get_var($sql);
    }

    /**
     * 同getVar,向前兼容
     *
     * @param string $sql
     * @return array
     * @ignore
     */
    public function get_var($sql)
    {
        $data = $this->get_line($sql);
        if ($data) {
            return $data[@reset(@array_keys($data))];
        } else {
            return false;
        }
    }

    /**
     * 同mysqli_affected_rows函数
     *
     * @return int 成功返回行数,失败时返回-1
     */
    public function affectedRows()
    {
        $result = isset($this->db_write) ? mysqli_affected_rows($this->db_write) : -1;
        return $result;
    }

    /**
     * 同mysqli_insert_id函数
     *
     * @return int 成功返回last_id,失败时返回false
     */
    public function lastId()
    {
        return $this->last_id();
    }

    /**
     * 同lastId,向前兼容
     *
     * @return int
     * @ignore
     */
    public function last_id()
    {
        $result = mysqli_insert_id($this->db_write(false));
        return $result;
    }

    /**
     * 关闭数据库连接
     *
     * @return void
     */
    public function closeDb()
    {
        return $this->close_db();
    }

    /**
     * 同closeDb,向前兼容
     *
     * @return void
     * @ignore
     */
    public function close_db()
    {
        if (isset($this->db_read))
            @mysqli_close($this->db_read);

        if (isset($this->db_write))
            @mysqli_close($this->db_write);

    }

    /**
     *  同mysqli_real_escape_string
     *
     * @param string $str
     * @return string
     */
    public function escape($str)
    {
        if (isset($this->db_read)) {
            $db = $this->db_read;
        } elseif (isset($this->db_write)) {
            $db = $this->db_write;
        } else {
            $db = $this->db_read();
        }

        return mysqli_real_escape_string($db, $str);
    }

    /**
     * 返回错误码
     *
     *
     * @return int
     */
    public function errno()
    {
        return $this->errno;
    }

    /**
     * 返回错误信息
     *
     * @return string
     */
    public function error()
    {
        return $this->error;
    }

    /**
     * 返回错误信息,error的别名
     *
     * @return string
     */
    public function errmsg()
    {
        return $this->error();
    }

    /**
     * @ignore
     */
    private function connect()
    {
        if ($this->port == 0) {
            $this->error = 13048;
            $this->errno = 'Not Initialized';
            return false;
        }

        $db = mysqli_init();
        mysqli_options($db, MYSQLI_OPT_CONNECT_TIMEOUT, 5);

        if (!mysqli_real_connect($db, $this->host, $this->username, $this->password, $this->db_name, $this->port)) {
            $this->error = mysqli_connect_error();
            $this->errno = mysqli_connect_errno();
            return false;
        }

        mysqli_set_charset($db, $this->charset);

        return $db;
    }

    /**
     * @ignore
     */
    private function db_read()
    {
        if (isset($this->db_read) && mysqli_ping($this->db_read)) {
            return $this->db_read;
        } else {
            if (!$this->do_replication) return $this->db_write();
            else {
                $this->db_read = $this->connect(false);
                return $this->db_read;
            }
        }
    }

    /**
     * @ignore
     */
    private function db_write($reconnect = true)
    {
        if (isset($this->db_write) && ($reconnect == false || mysqli_ping($this->db_write))) {
            return $this->db_write;
        } else {
            $this->db_write = $this->connect(true);
            return $this->db_write;
        }
    }

    /**
     * @ignore
     */
    private function save_error($dblink)
    {
        $this->error = mysqli_error($dblink);
        $this->errno = mysqli_errno($dblink);
    }

    private $error;
    private $errno;
    private $last_sql;
}

# 源码下载

点击下载

# 创建数据表(示例)

先创建一个数据表sch_example,包含四个字段:

字段 类型 说明
id int 自增键
name varchar 名称
value varchar
timeline timestamp 当前写入时间
<?php
// schmysql.class.php的源码在上面可以下载
require('./schmysql.class.php');

$db_instance = new SchMysql('w.rdc.sae.sina.com.cn', 3306, '你的AccessKey', '你的SccessSey', 'app_你的应用名');

$sql = 'CREATE TABLE IF NOT EXISTS `sch_example` (
          `id` int(11) NOT NULL AUTO_INCREMENT,
          `name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
          `value` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
          `timeline` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
          PRIMARY KEY (`id`),
          KEY `id` (`id`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1';

$ret = $db_instance->run_sql($sql);

var_dump($ret);

if (!$ret) {
    var_dump($db_instance->errno(), $db_instance->errmsg());
}

返回值:

bool(true) 

phpMyAdmin查询到的数据库结构:

创建完的数据库表结构

# 写入几条记录(示例)

示例将演示如果使用SDK写入数据。

<?php
// schmysql.class.php的源码在上面可以下载
require('./schmysql.class.php');

$db_instance = new SchMysql('w.rdc.sae.sina.com.cn', 3306, '你的AccessKey', '你的SccessSey', 'app_你的应用名');

$sql = "INSERT INTO `sch_example`(name,value)values ('%s', '%s')";

for ($i = 0; $i < 5; $i++) {
    $sql_insert = sprintf($sql, '名称'.$i, '值'.$i);
    $ret = $db_instance->run_sql($sql_insert);
    if (!$ret) {
        var_dump($db_instance->errno(), $db_instance->errmsg());
        break;
    }
}

执行完数据结构:

执行完写入操作后的数据库记录列表

# 查询多条数据(示例)

示例将演示查询id小于等于2的记录列表。

<?php
// schmysql.class.php的源码在上面可以下载
require('./schmysql.class.php');

$db_instance = new SchMysql('w.rdc.sae.sina.com.cn', 3306, '你的AccessKey', '你的SccessSey', 'app_你的应用名');
$sql = sprintf("SELECT * FROM `sch_example` where id<=%s", 2);

$data = $db_instance->get_data($sql);

var_dump($data);

返回值

查询将返回一个二维数组,数组的每一个值是一行数据库记录。

array(2) {
  [0]=>
  array(4) {
    ["id"]=>
    string(1) "1"
    ["name"]=>
    string(7) "名称0"
    ["value"]=>
    string(4) "值0"
    ["timeline"]=>
    string(19) "2018-09-10 17:51:39"
  }
  [1]=>
  array(4) {
    ["id"]=>
    string(1) "2"
    ["name"]=>
    string(7) "名称1"
    ["value"]=>
    string(4) "值1"
    ["timeline"]=>
    string(19) "2018-09-10 17:51:39"
  }
}

# 查询单条数据(示例)

示例将演示查询id为1的单条数据。

<?php
// schmysql.class.php的源码在上面可以下载
require('./schmysql.class.php');
$db_instance = new SchMysql('w.rdc.sae.sina.com.cn', 3306, '你的AccessKey', '你的SccessSey', 'app_你的应用名');
$sql = sprintf("SELECT * FROM `sch_example` where id=%s", 1);
$data = $db_instance->get_line($sql);
var_dump($data);

返回值

查询将返回一维数组,请注意和批量查询差异。

array(4) {
  ["id"]=>
  string(1) "1"
  ["name"]=>
  string(7) "名称0"
  ["value"]=>
  string(4) "值0"
  ["timeline"]=>
  string(19) "2018-09-10 17:51:39"
}

# 查询某个字段的值(示例)

示例将演示查询id为1的数据的name字段的值。

<?php
// schmysql.class.php的源码在上面可以下载
require('./schmysql.class.php');
$db_instance = new SchMysql('w.rdc.sae.sina.com.cn', 3306, '你的AccessKey', '你的SccessSey', 'app_你的应用名');
$sql = sprintf("SELECT `name` FROM `sch_example` where id=%s", 1);
$data = $db_instance->get_var($sql);
var_dump($data);

返回值

将返回字符串。

string(7) "名称0" 

# 删除数据(示例)

示例将演示删除id为1和2的数据。

<?php
// schmysql.class.php的源码在上面可以下载
require('./schmysql.class.php');
$db_instance = new SchMysql('w.rdc.sae.sina.com.cn', 3306, '你的AccessKey', '你的SccessSey', 'app_你的应用名');
$sql = sprintf("DELETE FROM `sch_example` WHERE id <= %d", 2);
$ret = $db_instance->run_sql($sql);

if ($ret) {
    // 影响的行数
    var_dump($db_instance->affectedRows());
}

返回值

int(2)

这里的数字2表示影响的行数。

删除后从phpMyAdmin查询数据就可以发现,1和2的记录已经没有了。

执行完删除后的数据列表