# MySQL数据库说明
# 开启
云虚拟主机在创建时自动开启一个MySQL数据库,因此您不需要单独开启数据库服务。
# 查询数据库的连接信息
进入主机的管理中心,选择左侧“数据库管理”将进入数据库的管理页面,如图所示:
数据库连接信息:
选项 | 值 |
---|---|
主机地址 | w.rdc.sae.sina.com.cn |
端口 | 3306 |
数据库名称 | app_XXX(XXX时虚拟主机的名称,例如wafwiki) |
数据库用户名 | 点击上图 数据库用户名 后的 显示查询 |
数据库密码 | 点击上图 数据库密码 后的 显示查询 |
# 使用phpMyAdmin管理数据库
云虚拟主机的数据库只能使用 phpMyAdmin
管理,不能使用本地的MySQL客户端管理。
从应用的管理中心,选择左侧“数据库管理”将进入数据库的管理页面,如图所示:
点击“phpMyAdmin”可以进入phpMyAdmin管理面板,如图所示:
# 数据库权限
当前数据库开放的权限列表为:
select, insert, update, delete, create table, alter table, drop table, index
可以执行以上的操作。
# 导入数据库
云虚拟主机在管理中心提供了数据库的导入功能,请使用这个功能导入数据库。
# 不要从phpMyAdmin导入数据
从phpMyAdmin导入提示
因为phpMyAdmin的运行环境有运行时长和内存的限制,因此不要从phpMyAdmin导入。
# 从云应用SAE的应用数据库导入
云虚拟主机支持从云应用SAE的数据库导入:
- 登陆云虚拟主机的管理平台(https://scs.sinacloud.com (opens new window))
- 进入主机的管理面板(点击主机列表的详情 进入)
- 选择左侧导航条数据库管理
- 点击 数据导入导出 功能的 “数据导入”标签,点击“+新建导入任务”
- 数据源选择“共享型数据库”
- 选择需要导入的应用
- 选择导入“整个数据库”或者“数据表”
- 添加任务完成,在任务列表处查询导入的状态
# 从外部数据库导入
云虚拟主机支持从外部数据库直接导入数据,外部的数据库需要从公网能直接连接。
- 登陆云虚拟主机的管理平台(https://scs.sinacloud.com (opens new window))
- 进入主机的管理面板(点击主机列表的详情 进入)
- 选择左侧导航条数据库管理
- 点击 数据导入导出 功能的 “数据导入”标签,点击“+新建导入任务”
- 数据源选择“外部数据库”
- 添加任务完成,在任务列表处查询导入的状态
# 从文件链接导入
从文件链接(SQL或者SQL的压缩文件)添加导入任务,文件支持sql、zip、rar、tar.gz。
提示
- 请确保填写的地址可以从公网访问,否则会导入失败
- 压缩文件需要是SQL文件的压缩包,压缩包中只支持单个的SQL文件
- 登陆云虚拟主机的管理平台(https://scs.sinacloud.com (opens new window))
- 进入主机的管理面板(点击主机列表的详情 进入)
- 选择左侧导航条数据库管理
- 点击 数据导入导出 功能的 “数据导入”标签,点击“+新建导入任务”
- 数据源选择“文件链接”
- 添加任务完成,在任务列表处查询导入的状态
# 从SQL文件导入
云虚拟主机支持从SQL文件导入数据,可以在线上传SQL文件导入,除SQL文件外,也可以上传zip、rar、tar.gz格式的SQL压缩文件。
说明
仅支持单个SQL文件的压缩文件,如果有多个SQL文件需要上传,请分多次添加导入的任务。
- 登陆云虚拟主机的管理平台(https://scs.sinacloud.com (opens new window))
- 进入主机的管理面板(点击主机列表的详情 进入)
- 选择左侧导航条数据库管理
- 点击 数据导入导出 功能的 “数据导入”标签,点击“+新建导入任务”
- 数据源选择“SQL文件”
- 添加任务完成,在任务列表处查询导入的状态
# 数据库导出
# 添加导出任务
云虚拟主机支持在线添加数据库导出的任务。
- 登陆云虚拟主机的管理平台(https://scs.sinacloud.com (opens new window))
- 进入主机的管理面板(点击主机列表的详情 进入)
- 选择左侧导航条数据库管理
- 点击 数据导入导出 功能的 “数据导出”标签,点击“+新建导出任务”
- 选择导出的类型,整个数据库或者选择部分数据表
- 添加任务完成,在任务列表处查询导出的状态,导出任务执行完成后,从“其他”的“点击下载”可以下载导出的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 (opens new window))限制,提升账户等级(http://www.sinacloud.com/ucenter/account.html (opens new window))可以增加配额。
注意
- 当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的记录已经没有了。