注意事项
要使用MySQL前提条件必须安装了MySQL,由于目前采用的是Docker的使用,所以提前需要使用Docker安装MySQL,具体操作参见《Docker MySQL》。
基础概念
数据库实例:数据库实例相当于分配,默认节点分为master和slave,属于默认实例default。
master和slave是两个特殊的名称,他们会归纳到default实例中,表现为default.master和default.slave。
数据库节点:每个实例下的item都是一个节点,key是节点名称。
每个节点都会创建一个连接池,连接池的名称是instance.node。
可以通过\Swoft::getPool("instance.node")获取连接池对象
为什么需要引入连接池呢?
对于基于PHP-FPM的传统PHP Web应用,包括且不限于MySQL、Redis、RabbitMq,每次请求到来时都需要为其新建一套独享的连接,这直接带来了一个典型的问题:
连接开销
连接随着HTTP请求的到来而新建,随着请求返回而销毁,大量连接新建销毁是对系统资源的浪费。
连接数量过高
每个请求都需要一套自己的连接,系统连接数和并发数会成一个线性的关系。如果系统并发量达到1W,那就需要建立1W个对应的连接,这对MySQL之类的后端服务来说是一个很大的负荷。
空闲连接
假如接口使用了一个MySQL连接,接口在一开始进行一次SQL查询后,后面的操作都是与SQL无关的,那么这个请求占据的空闲连接完全就是一种资源的浪费。
对于异步系统而言,这个问题变得更加严重。一个请求处理进程要对同一服务进行并发操作,这意味着请求要持有一个以上的同类连接。对系统压力而言,无疑是雪上加霜。所以连接池对基于Swoole的Web框架是必须实现的机制。
数据库配置
Swoft提供properties和env两种方式配置,env会覆盖properties配置。
Swoft提供数据库主从master-slave配置,默认读操作使用从配置slave,写操作使用主配置master,若只配置主则读写都会使用。
如果对数据库主从不是很清晰,可参见《MySQL读写分离主从复制》。
env配置
$ vim .env
# 数据库主节点配置
# 连接池节点名称用于服务发现
DB_NAME=dbMaster
# 连接地址信息
DB_URI=127.0.0.1:3306/test?user=root&password=123456&charset=utf8,127.0.0.1:3306/test?user=root&password=123456&charset=utf8
# 最小活跃连接数
DB_MIN_ACTIVE=5
# 最大活跃连接数
DB_MAX_ACTIVE=10
# 最大等待连接
DB_MAX_WAIT=20
# 最大等待时间
DB_MAX_WAIT_TIME=3
# 连接最大空闲时间
DB_MAX_IDLE_TIME=60
# 连接超时时间
DB_TIMEOUT=2
# 数据库从节点配置
# 连接池节点名称用于服务发现
DB_SLAVE_NAME=dbSlave
# 连接地址信息
DB_SLAVE_URI=127.0.0.1:3306/test?user=root&password=123456&charset=utf8,127.0.0.1:3306/test?user=root&password=123456&charset=utf8
# 最小活跃连接数
DB_SLAVE_MIN_ACTIVE=5
# 最大活跃连接数
DB_SLAVE_MAX_ACTIVE=10
# 最大等待连接
DB_SLAVE_MAX_WAIT=20
# 最大等待时间
DB_SLAVE_MAX_WAIT_TIME=3
# 连接最大空闲时间
DB_SLAVE_MAX_IDLE_TIME=60
# 连接超时时间
DB_SLAVE_TIMEOUT=3
数据库连接地址uri规则:ip:端口/数据库名?user=用户名&password=密码&charset=utf8
properties属性配置
$ vim config/properties/db.php
return [
//数据库主节点配置
'master' => [
//连接池节点名称用于服务发现
'name' => 'master',
//连接地址信息
'uri' => [
'127.0.0.1:3306/test?user=root&password=123456&charset=utf8',
'127.0.0.1:3306/test?user=root&password=123456&charset=utf8',
],
//最小活跃连接数
'minActive' => 8,
//最大活跃连接数
'maxActive' => 8,
//最大等待连接
'maxWait' => 8,
//超时时间
'timeout' => 8,
//连接最大空闲时间
'maxIdleTime' => 60,
//连接最大等待时间
'maxWaitTime' => 3,
],
//数据库从节点配置
'slave' => [
//连接池节点名称用于服务发现
'name' => 'slave',
//连接地址信息
'uri' => [
'127.0.0.1:3306/test?user=root&password=123456&charset=utf8',
'127.0.0.1:3306/test?user=root&password=123456&charset=utf8',
],
//最小活跃连接数
'minActive' => 8,
//最大活跃连接数
'maxActive' => 8,
//最大等待连接
'maxWait' => 8,
//超时时间
'timeout' => 8,
//连接最大空闲时间
'maxIdleTime' => 60,
//连接最大等待时间
'maxWaitTime' => 3,
],
];
实体定义
无论是高级查询还是基础查询都需要表实体,一个表字段和一个表属性是一一映射的关系,对类的操作相当于对表的操作,该类称为一个实体。实体不能作为属性被注入到任何类,因为每个实体对象都有不同的数据记录行。实体对象都是在那里使用就在那里创建。
一个实体类对应一张数据表
实体对象代表了表的一行数据记录
创建数据表
CREATE TABLE `user` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '名称',
`age` tinyint(1) unsigned DEFAULT '0' COMMENT '年龄',
`sex` tinyint(1) unsigned DEFAULT '0' COMMENT '性别',
`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '描述',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户表';
CREATE TABLE `count` (
`uid` int(11) unsigned DEFAULT '0' COMMENT '用户ID',
`fans` int(10) unsigned DEFAULT '0' COMMENT '粉丝数量',
`follows` int(10) unsigned DEFAULT '0' COMMENT '关注数量'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='计数表';
快速生成实体类
使用Swoft提供的内置命令快速生成实体类
php bin/swoft entity:create -d test user,count
查看生成的实体类
$ vim app/Models/Entity/User.php
/**
* This file is part of Swoft.
*
* @link https://swoft.org
* @document https://doc.swoft.org
* @contact group@swoft.org
* @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE
*/
namespace App\Models\Entity;
use Swoft\Db\Bean\Annotation\Id;
use Swoft\Db\Bean\Annotation\Required;
use Swoft\Db\Bean\Annotation\Table;
use Swoft\Db\Bean\Annotation\Column;
use Swoft\Db\Bean\Annotation\Entity;
use Swoft\Db\Model;
use Swoft\Db\Types;
/**
* 用户实体
*
* @Entity()
* @Table(name="user")
* @uses User
* @version 2017年08月23日
* @author stelin
* @copyright Copyright 2010-2016 Swoft software
* @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
*/
class User extends Model
{
/**
* 主键ID
*
* @Id()
* @Column(name="id", type=Types::INT)
* @var null|int
*/
private $id;
/**
* 名称
*
* @Column(name="name", type=Types::STRING, length=20)
* @Required()
* @var null|string
*/
private $name;
/**
* 年龄
*
* @Column(name="age", type=Types::INT)
* @var int
*/
private $age = 0;
/**
* 性别
*
* @Column(name="sex", type="int")
* @var int
*/
private $sex = 0;
/**
* 描述
*
* @Column(name="description", type="string")
* @var string
*/
private $desc = '';
/**
* 非数据库字段,未定义映射关系
*
* @var mixed
*/
private $otherProperty;
/**
* @return int|null
*/
public function getId()
{
return $this->id;
}
/**
* @param int|null $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* @return null|string
*/
public function getName()
{
return $this->name;
}
/**
* @param null|string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* @return int
*/
public function getAge(): int
{
return $this->age;
}
/**
* @param int $age
*/
public function setAge(int $age)
{
$this->age = $age;
}
/**
* @return int
*/
public function getSex(): int
{
return $this->sex;
}
/**
* @param int $sex
*/
public function setSex(int $sex)
{
$this->sex = $sex;
}
/**
* @return string
*/
public function getDesc(): string
{
return $this->desc;
}
/**
* @param string $desc
*/
public function setDesc(string $desc)
{
$this->desc = $desc;
}
/**
* @return mixed
*/
public function getOtherProperty()
{
return $this->otherProperty;
}
/**
* @param mixed $otherProperty
*/
public function setOtherProperty($otherProperty)
{
$this->otherProperty = $otherProperty;
}
}
$ vim app/Models/Count.php
/**
* This file is part of Swoft.
*
* @link https://swoft.org
* @document https://doc.swoft.org
* @contact group@swoft.org
* @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE
*/
namespace App\Models\Entity;
use Swoft\Db\Bean\Annotation\Column;
use Swoft\Db\Bean\Annotation\Entity;
use Swoft\Db\Bean\Annotation\Id;
use Swoft\Db\Bean\Annotation\Table;
use Swoft\Db\Model;
use Swoft\Db\Types;
/**
* 计数表实体
*
* @Entity()
* @Table("count")
* @uses Count
* @version 2017年09月15日
* @author stelin
* @copyright Copyright 2010-2016 swoft software
* @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
*/
class Count extends Model
{
/**
* 用户ID
*
* @Column(name="uid", type=Types::INT)
* @Id()
* @var null|int
*/
private $uid;
/**
* 粉丝数
*
* @Column(name="fans", type=Types::NUMBER)
* @var int
*/
private $fans = 0;
/**
* 关注数
*
* @Column("follows", type=Types::NUMBER)
* @var int
*/
private $follows = 0;
/**
* @return int|null
*/
public function getUid()
{
return $this->uid;
}
/**
* @param int|null $uid
*/
public function setUid($uid)
{
$this->uid = $uid;
}
/**
* @return int
*/
public function getFans(): int
{
return $this->fans;
}
/**
* @param int $fans
*/
public function setFans(int $fans)
{
$this->fans = $fans;
}
/**
* @return mixed
*/
public function getFollows()
{
return $this->follows;
}
/**
* @param mixed $follows
*/
public function setFollows($follows)
{
$this->follows = $follows;
}
}
注解标签
@Entity 标记一个类是一个实体,无需多余参数。
@Table 定义实体映射的数据库表名
@Column(name, type) 类属性默认值为表字段默认值
name 定义类属性映射的表字段,若无则不映射。type 定义字段数据更新时验证类型。所有字段属性必须存在getter和setter方法。
@Id 表明当前类属性对应了数据库表中的主键,必须存在。
错误处理
{
"msg":"Database connection error,error=Connection refused",
"file":"\/var\/www\/swoft\/vendor\/swoft\/db\/src\/Driver\/Mysql\/MysqlConnection.php",
"line":89,
"code":0
}
数据库连接失败,需要检查.env或db.php配置文件中数据库配置选项。
未完待续...