tar -xzvf mysql-5.7.21-linux-glibc2.12-x86_64.tar.gz -C /usr/local/
mv mysql-5.7.21-linux-glibc2.12-x86_64 mysql
mkdir data
groupadd mysql
useradd -r -s /sbin/nologin -g mysql mysql -d /usr/local/mysql
chown -R mysql.mysql /usr/local/mysql
(若初始化时报错 error while loading shared libraries: libaio.so.1: cannot open shared object file: No such file or directory,出现该问题首先检查该链接库文件有没有安装使用 命令进行核查
rpm -qa|grep libaio
运行该命令后发现系统中无该链接库文件
使用命令,yum install libaio-devel.x86_64
安装成功后,继续运行数据库的初始化命令,提示成功。)
./mysqld --initialize --user=mysql --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data
A temporary password is generated for root@localhost: 初始密码
vim /etc/my.cnf
修改为:
[mysqld]
basedir=/usr/local/mysql
datadir=/usr/local/mysql/data
socket=/tmp/mysql.sock
user=mysql
port=3306
character_set_server=utf8
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
[mysqld_safe]
log-error=/usr/local/mysql/data/error.log
pid-file=/usr/local/mysql/data/mysqld.pid
tmpdir=/tmp
将mysql目录下的support-files目录中的mysql.server文件复制到路径/etc/init.d/mysqld
cp support-files/mysql.server /etc/init.d/mysqld
vim /etc/init.d/mysqld
完善=后面的内容。其余不动
basedir=/usr/local/mysql
datadir=/usr/local/mysql/data
mysqld_file_path=/usr/local/mysql/data/mysqld_pid
firewall-cmd --zone=public --add-port=3306/tcp --permanent
firewall-cmd --reload
查看所有数据库:show databases;
使用mysql数据库:use mysql;
查看mysql数据库下的所有表名:show tables; 可以看到mysql数据库下有一个user表
执行:
select host from user where user='root';
update user set host = '%' where user ='root';
提交与刷新数据库:flush privileges;
service mysqld start
15.登录mysql(在mysql的bin目录下进行)l
mysql -u root -p
set password for 'root'@localhost=password('新密码');
SOL(Structured Query Language):结构化查询语言。其实就是定义了操作所有关系型数据库的一种规则。
通用语法规则
SQL语句可以单行或多行书写,以分号结尾
可使用空格和缩进来增强语句的可读性
MySQL数据库的SQL语句不区分大小写,关键字建议使用大写
单行注释:–注释内容 #注释内容(MySQL特有)
多行注释: /* 注释内容 */
SQL分类
SHOW DATABASES;
SHOW CREATE DATABASE 数据库名称;
CREATE DATABASE 数据库名称;
CREATE DATABASE IF NOT EXISTS 数据库名称;
CREATE DATABASE 数据库名称 CHARACTERSET 字符集名称;
ALTER DATABASE 数据库名称 CHARACTER SET 字符集名称;
DROP DATABASE 数据库名称;
DROP DATABASE IF EXISTS 数据库名称;
USE 数据库名称;
SELECT DATABASE();
SHOW TABLES;
DESC 表名;
SHOW TABLE STATUS FROM 库名 LIKE '表名';
CREATE TABLE 表名(
列名 数据类型 约束,
列名 数据类型 约束,
...
列名 数据类型 约束,
);
例:
-- 创建一个product商品表(商品编号、商品名称、商品价格、商品库存、上架时间)
CREATE TABLE product(
id INT,
NAME VARCHAR(20),
price DOUBLE,
stock INT,
insert_time DATE
);
int:整数类型
double:小数类型
data:日期类型。包含年月日,格式yyyy-MM-dd
datatime:日期类型。包含年月日时分秒,格式yyyy-MM-dd HH:mm:ss
timestamp:时间戳类型。包含年月日时分秒,格式yyyy-MM-dd HH:mm:ss
*如果不给该列赋值、或赋值为null,则默认使用当前系统时间自动赋值
varchar(长度):字符串类型
ALTER TABLE 表名 RENAME TO 新表名;
例:
-- 给product表重命名为product2
ALTER TABLE product RENAME TO product2;
ALTER TABLE 表名 CHARACTERSET 字符集名称;
例:
-- 修改product2数据表字符集为gbk
ALTER TABLE product2 CHARACTER SET gbk;
ALTER TABLE 表名 ADD 列名 数据类型;
例:
-- 给product2添加一列color
ALTER TABLE product2 ADD color VARCHAR(10);
ALTER TABLE 表名 MODIFY 列名 新数据类型;
例:
-- 将color数据类型修改为int
ALTER TABLE product2 MODIFY color INT;
ALTER TABLE 表名 CHANGE 列名 新列名 新数据类型;
例:
-- 将color修改为address
ALTER TABLE product2 CHANGE color address VARCHAR(200);
ALTER TABLE 表名 DROP 列名;
例:
-- 删除address列
ALTER TABLE product2 DROP address;
DROP TABLE 表名;
例:
-- 删除product2表
DROP TABLE product2;
DROP TABLE IF EXISTS 表名;
例:
-- 删除product2表(判断,如果存在则删除)
DROP TABLE IF EXISTS product2;
INSERT INFO 表名(列名1,列名2,...)VALUES(值1,值2,...);
例:
-- 向product表中添加指定列数据
INSERT INTO product (id,NAME,price) VALUES (2,'电脑',3999.99);
INSERT INFO 表名 VALUES(值1,值2,...);
例:
-- 默认给全部列添加数据
INSERT INTO product VALUES (3,'冰箱',1500,35,'2021-10-01');
INSERT INFO 表名 (列名1,列名2,...) VALUES (值1,值2,...),(值1,值2,...),...;
INSERT INFO 表名 VALUES(值1,值2,...),(值1,值2,...),...;
例:
-- 批量添加数据
INSERT INTO product VALUES (4,'电视机',2000,20,'2025-01-01'),(5,'空调',3000,4,'2021-01-01');
列名和值的数量以及数据类型要对应,除了数字类型,其他数据类型的数据都需要加引号(单引双引都行,推荐单引)。
UPDATE 表名 SET 列名1=值1,列名2=值2,...[WHERE 条件];
例:
-- 修改手机的价格为3500
UPDATE product SET price=3500 WHERE NAME='手机';
-- 修改电脑的价格为1800、库存为36
UPDATE product SET price=1800,stock=36 WHERE NAME='电脑';
修改语句中必须加条件,如果不加条件,则会将所有数据都修改。
DELETE FROM 表名 [WHERE 条件];
例:
-- 删除product表中的空调信息
DELETE FROM product WHERE NAME='空调';
-- 删除product表中库存为10的商品信息
DELETE FROM product WHERE stock=10;
删除语句中必须加条件,如果不加条件,则会将所有数据都删除。
SELECT
字段列表
FROM
表名列表
WHERE
条件列表
GROUP BY
分组字段
HAVING
分组后的过滤条件
ORDER BY
排序
LIMIT
分页
SELECT * FROM 表名;
例:
-- 查询product表所有数据
SELECT * FROM product;
SELECT 列名1,列名2,... FROM 表名;
例:
-- 查询名称、价格、品牌
SELECT NAME,price,brand FROM product;
SELECT DISTINCT 列名1,列名2,... FROM 表名;
例:
-- 查询品牌、去除重复
SELECT DISTINCT brand FROM product;
SELECT 列名1 运算符(+ - * /) 列名2 FROM 表名;
如果某一列为null,可以进行替换
ifnull(表达式1,表达式2)
表达式1:想替换的列
表达式2:想替换的值
例:
-- 查询商品库存,库存数量在原有的基础上加10
SELECT NAME,stock+10 FROM product;
-- 查询商品名称和库存,库存数量在原有基础上加10。进行null值判断
SELECT NAME,IFNULL(stock,0)+10 FROM product;
SELECT 列名 AS 别名 FROM 表名;
例:
-- 查询商品名称和库存,库存数量在原有基础上加10。进行null值判断。起别名为getSum
SELECT NAME,IFNULL(stock,0)+10 AS getSum FROM product;
-- AS可以省略
SELECT NAME,IFNULL(stock,0)+10 getSum FROM product;
| 符号 | 功能 |
|---|---|
| > | 大于 |
| < | 小于 |
| >= | 大于等于 |
| <= | 小于等于 |
| = | 等于 |
| <>或!= | 不等于 |
| BETWEEN…AND… | 在某个范围之内(都包含) |
| IN(…) | 多选一 |
| LIKE 占位符 | 模糊查询 _单个任意字符 %多个任意字符 |
| IS NULL | 是NULL |
| IS NOT NULL | 不是NULL |
| AND或&& | 并且 |
| OR或|| | 或者 |
| NOT或! | 非,不是 |
例:
-- 查询库存大于20的商品信息
SELECT * FROM product WHERE stock > 20;
-- 查询品牌为华为的商品信息
SELECT * FROM product WHERE brand='华为';
-- 查询金额在4000~6000之间的商品信息
SELECT * FROM product WHERE price >=4000 AND price <= 6000;
SELECT * FROM product WHERE price BETWEEN 4000 AND 6000;
-- 查询库存为14/30/23的商品信息
SELECT * FROM product WHERE stock=14 OR stock=30 OR stock=23;
SELECT * FROM product WHERE stock IN(14,30,23)
-- 查询库存为null的商品信息
SELECT * FROM product WHERE stock IS NULL;
-- 查询库存不为null的商品信息
SELECT * FROM product WHERE stock IS NOT NULL;
-- 查询名称以小米为开头的商品信息
SELECT * FROM product WHERE NAME LIKE '小米%';
-- 查询名称第二个字是为的商品信息
SELECT * FROM product WHERE NAME LIKE '_为%';
-- 查询名称为四个字符的商品信息
SELECT * FROM product WHERE NAME LIKE '____';
-- 查询名称中包含电脑的信息
SELECT * FROM product WHERE NAME LIKE '%电脑%'
聚合函数的介绍
将一列数据作为一个整体,进行纵向的计算
聚合函数的分类
| 函数名 | 功能 |
|---|---|
| count(列名) | 统计数量(一般选用不为null的列) |
| max(列名) | 最大值 |
| min(列名) | 最小值 |
| sum(列名) | 求和 |
| avg(列名) | 平均值 |
SELECT 函数名(列名) FROM 表名 [WHERE 条件]
例:
-- 计算product表中总记录条数
SELECT COUNT(*) FROM product;
-- 获取最高价格
SELECT MAX(price) FROM product;
-- 获取最低库存
SELECT MIN(stock) FROM product;
-- 获取总库存数量
SELECT SUM(stock) FROM product;
-- 获取品牌为苹果的总库存数量
SELECT SUM(stock) FROM product WHERE brand='苹果';
-- 获取品牌为小米的平均商品价格
SELECT AVG(price) FROM product WHERE brand='小米';
SELECT 列名列表 FROM [WHERE 条件] ORDER BY 列名 排序方式,列名 排序方式,...;
排序方式:ASC-升序,DESC-降序
如果有多个排序条件,只有当前边的条件值一样时,才会判断第二条件
例:
-- 按照库存升序排序
SELECT * FROM product ORDER BY stock ASC;
-- 查询名称中包含数据的商品信息。按照金额降序排序
SELECT * FROM product WHERE NAME LIKE '%手机%' ORDER BY price DESC;
-- 按照金额升序排序,如果金额相同,按照库存降序排列
SELECT * FROM product ORDER BY price ASC,stock DESC;
SELECT 列名列表
FROM 表名
[WHERE 条件]
GROUP BY 分组列名
[HAVING 分组后的条件过滤]
[ORDER BY 排序列名 排序方式];
-- 按照品牌分组,获取每组商品的总金额
SELECT brand,SUM(price) FROM product GROUP BY brand;
-- 对金额大于4000元的商品,按照品牌分组,获取每组商品的总金额
SELECT brand,SUM(price) FROM product WHERE price > 4000 GROUP BY brand;
-- 对金额大于4000元的商品,按照品牌分组,获取每组商品的总金额,只显示总金额大于7000元的
SELECT brand,SUM(price) getSum FROM product WHERE price > 4000 GROUP BY brand HAVING getSum > 7000;
-- 对金额大于4000元的商品,按照品牌分组,获取每组商品的总金额,只显示总金额大于7000元的、并按总金额的降序排列
SELECT brand,SUM(price) getSum FROM product
WHERE price > 4000
GROUP BY brand
HAVING getSum > 7000
ORDER BY getSum DESC;
SELECT 列名列表 FROM 表名
[WHERE 条件]
[GROUP BY 分组列名]
[HAVING 分组后的条件过滤]
[ORDER BY 排序列名 排序方式]
LIMIT 当前页数,每页显示的条数;
例:
-- 每页显示三条数据
-- 第一页 当前页数=(1-1)*3
SELECT * FROM product LIMIT 0,3;
-- 第二页 当前页数=(2-1)*3
SELECT * FROM product LIMIT 3,3;
-- 第三页 当前页数=(3-1)*3
SELECT * FROM product LIMIT 6,3;
什么是约束
对表中的数据进行限定,保证数据的正确性、有效性、完整性!
约束的分类
| 约束 | 作用 |
|---|---|
| PRIMARY KEY | 主键约束 |
| PRIMARY KEY AUTO_INCREMENT | 主键自增 |
| UNIQUE | 唯一约束 |
| NOT NULL | 非空约束 |
| FOREIGN KEY | 外键约束 |
| FOREIGN KEY ON UPDATE CASCADE | 外键级联更新 |
| FOREIGN KEY ON DELETE CASCADE | 外键级联删除 |
主键约束的特点
主键约束默认包含非空和唯一两个功能
一张表只能有一个 主键
主键一般用于表中数据的唯一标识
建表时添加主键约束
CREATE TABLE 表名(
列名 数据类型 PRIMARY KEY,
...
列名 数据类型 约束
)
ALTER TABLE 表名 DROP PRIMARY KEY;
ALTER TABLE MODIFY 列名 数据类型 PRIMARY KEY;
例:
-- 创建学生表(编号、姓名、年龄)
CREATE TABLE student(
id INT PRIMARY KEY,
NAME VARCHAR(30),
age INT
);
-- 查询学生表的详细信息
DESC student;
-- 添加数据
INSERT INTO student VALUES (1,'张三',23);
INSERT INTO student VALUES (2,'李四',24);
-- 删除主键
ALTER TABLE student DROP PRIMARY KEY;
-- 建表后单独添加主键约束
ALTER TABLE student MODIFY id INT PRIMARY KEY;
CREATE TABLE 表名(
列名 数据类型 PRIMARY KEY AUTO_INCREMENT
...
列名 数据类型 约束
);
ALTER TABLE 表名 MODIFY 列名 数据类型;
ALTER TABLE 表名 MODIFY 列名 数据类型 AUTO_INCREMENT;
MySQL中的自增约束,必须配合键的约束一起使用!
例:
-- 创建学生表(编号、姓名、年龄) 编号设为主键自增
CREATE TABLE student(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(30),
age INT
);
-- 查询学生表的详细信息
DESC student;
-- 添加数据
INSERT INTO student VALUES (NULL,'张三',23),(NULL,'李四',24);
-- 删除自增约束
ALTER TABLE student MODIFY id INT;
-- 建表后单独添加自增约束
ALTER TABLE student MODIFY id INT AUTO_INCREMENT;
CREATE TABLE 表名(
列名 数据类型 UNIQUE,
...
列名 数据类型 约束
);
ALTER TABLE 表名 DROP INDEX 列名;
ALTER TABLE 表名 MODIFY 列名 数据类型 UNIQUE;
例:
-- 创建学生表(编号、姓名、年龄) 编号设为主键自增,年龄设为唯一
CREATE TABLE student(
id INT PRIMARY KEY auto_increment,
NAME VARCHAR(30),
age INT UNIQUE
);
-- 查询学生表的详细信息
DESC student;
-- 添加数据
INSERT INTO student VALUES (NULL,'张三',23);
INSERT INTO student VALUES (NULL,'李四',23);
-- 删除唯一约束
ALTER TABLE student DROP INDEX age;
-- 建表后单独添加唯一约束
ALTER TABLE student MODIFY age INT UNIQUE;
CREATE TABLE 表名(
列名 数据类型 NOT NULL,
...
列名 数据类型 约束
)
ALTER TABLE 表名 MODIFY 列名 数据类型;
例:
ALTER TABLE 表名 MODIFY 列名 数据类型 NOT NULL;
-- 创建学生表(编号、姓名、年龄) 编号设为主键自增,姓名设为非空,年龄设为唯一
CREATE TABLE student(
id INT PRIMARY KEY auto_increment,
NAME VARCHAR(30) NOT NULL,
age INT UNIQUE
);
-- 查询学生表的详细信息
DESC student;
-- 添加数据
INSERT INTO student VALUES (NULL,'王志凯',23);
-- 删除非空约束
ALTER TABLE student MODIFY NAME VARCHAR(30);
INSERT INTO student VALUES (NULL,NULL,24);
-- 建表后单独添加非空约束
ALTER TABLE student MODIFY NAME VARCHAR(30) NOT NULL;
为什么要有外键约束?
当表与表之间的数据有相关联性的时候,如果没有相关的数据约束,则无法保证数据的准确性!
外键约束的作用
让表与表之间产生关联关系,从而保证数据的准确性!
建表时添加外键约束
CREATE TABLE 表名(
列名 数据类型 约束,
...
CONSTRAINT 外键名 FOREIGN KEY (本表外键列名) REFERENCES 主表名 (主表主键列名)
)
ALTER TABLE 表名 DROP FOREIGN KEY 外键名;
ALTER TABLE 表名 ADD
CONSTRAINT 外键名 FOREIGN KEY (本表外键列名) REFERENCES 主表名(主键列名);
CREATE DATABASE db2;
USE db2;
CREATE TABLE USER(
id INT PRIMARY KEY auto_increment,
NAME VARCHAR(20) NOT NULL
);
INSERT INTO USER VALUES (NULL,'张三'),(NULL,'李四');
CREATE TABLE orderlist(
id INT PRIMARY KEY auto_increment,
number VARCHAR(20) NOT NULL,
uid INT,
CONSTRAINT ou_fk1 FOREIGN KEY (uid) REFERENCES USER(id)
);
INSERT INTO orderlist VALUES (NULL,'hm001',1),(NULL,'hm002',1),
(NULL,'hm003',2),(NULL,'hm004',2);
-- 添加一个订单,但是没有真实用户,添加失败
INSERT INTO orderlist VALUES (NULL,'hm005',3);
-- 删除李四用户
DELETE FROM USER WHERE NAME=‘李四’;
-- 删除外键约束
ALTER TABLE orderlist DROP FOREIGN KEY ou_fk1;
-- 添加外键约束
ALTER TABLE orderlist CONSTRAINT ou_fk1 FOREIGN KEY (uid) REFERENCES USER(id);
ALTER TABLE 表名 ADD
CONSTRAINT 外键名 FOREIGN KEY (本表外键列名) REFERENCES 主表名(主键列名)
ON UPDATE CASCADE;
ALTER TABLE 表名 ADD
CONSTRAINT 外键名 FOREIGN KEY (本表外键列名) REFERENCES 主表名(主键列名)
ON DELETE CASCADE;
ALTER TABLE 表名 ADD
CONSTRAINT 外键名 FOREIGN KEY (本表外键列名) REFERENCES 主表名(主键列名)
ON UPDATE CASCADE ON DELETE CASCADE;
说白了就是多张数据表,而表与表之间是可以有一定的关联关系,这种关联关系通过外键约束实现.
多表的分类
一对一
一对多
多对多
适用场景
人和身份证。一个人只有一个身份证,一个身份证只能对应一个人
建表原则
在任意一个表建立外键,去关联另外一个表的主健。
USE db3;
CREATE TABLE person(
id INT PRIMARY KEY auto_increment,
NAME VARCHAR(20)
);
INSERT INTO person VALUES (NULL,'张三'),(NULL,'李四');
CREATE TABLE card(
id INT PRIMARY KEY auto_increment,
number VARCHAR(20) UNIQUE NOT NULL,
pid INT UNIQUE,
CONSTRAINT cp_fk1 FOREIGN KEY (pid) REFERENCES person(id)
);
INSERT INTO card VALUES (NULL,'12345',1),(NULL,'56789',2);
适用场景
用户和订单。一个用户可以有多个订单。
商品分类和商品。一个分类下可以有多个商品。
建表原则
在多的一方,建立外健约束,来关联一的一方主健
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20)
);
INSERT INTO USER VALUES (NULL,'张三'),(NULL,'李四');
CREATE TABLE orderlist(
id INT PRIMARY KEY AUTO_INCREMENT,
number VARCHAR(20),
uid INT,
CONSTRAINT ou_fk1 FOREIGN KEY (uid) REFERENCES USER(id)
);
INSERT INTO orderlist VALUES (NULL,'hm001',1),(NULL,'hm002',1),(NULL,'hm003',2),(NULL,'hm004',2);
CREATE TABLE category(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10)
);
INSERT INTO category VALUES (NULL,'手机数码'),(NULL,'电脑办公');
适用场景
学生和课程。一个学生可以选择多个课程,一个课程也可以被多个学生选择。
建表原则
需要借助第三张中间表,中间表至少包含两个列。这两个列作为中间表的外键,分别关联两张表的主键。
CREATE TABLE student(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20)
);
INSERT INTO student VALUES (NULL,'张三'),(NULL,'李四');
CREATE TABLE course(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10)
);
INSERT INTO course VALUES (NULL,'语文'),(NULL,'数学');
CREATE TABLE stu_course(
id INT PRIMARY KEY AUTO_INCREMENT,
sid INT,
cid INT,
CONSTRAINT sc_fk1 FOREIGN KEY (sid) REFERENCES student(id),
CONSTRAINT sc_fk2 FOREIGN KEY (cid) REFERENCES course(id)
);
INSERT INTO stu_course VALUES (NULL,1,1),(NULL,1,2),(NULL,2,1),(NULL,2,2);
内连接查询的是两张表有交集的部分数据(有主外键关联的数据)。
查询语法
SELECT 列名 FROM 表名1 [INNER] JOIN 表名2 ON 条件;
SELECT 列名 FROM 表名1,表名2 WHERE 条件;
例:
-- 查询用户信息和对应的订单信息
SELECT * FROM USER INNER JOIN orderlist ON orderlist.uid = user.id;
-- 查询用户信息和对应的订单信息,起别名
SELECT * FROM USER u INNER JOIN orderlist o ON o.uid = u.id;
-- 查询用户姓名,年龄,订单编号
SELECT
u.name, -- 用户姓名
u.age, -- 用户年龄
o.number -- 订单编号
FROM
USER u -- 用户表
INNER JOIN
orderlist o -- 订单表
ON
o.uid=u.id;
SELECT
u.name, -- 用户姓名
u.age, -- 用户年龄
o.number -- 订单编号
FROM
USER u,
orderlist o
WHERE
o.uid=u.id;
### 7.外连接查询
+ 左外连接
+ 查询原理
查询左表的全部数据,和左右两张表有交集部分的数据。
+ 查询语法
```sql
SELECT 列名 FROM 表名1 LEFT [OUTER] JOIN 表名2 ON 条件;
右外连接
查询原理
查询右表的全部数据,和左右两张表有交集部分的数据。
查询语法
SELECT 列名 FROM 表名1 RIGHT [OUTER] JOIN 表名2 ON 条件;
例:
-- 查询所有用户信息,以及用户对应的订单信息
SELECT
u.*,
o.number
FROM
USER u
LEFT OUTER JOIN
orderlist o
ON
o.uid=u.id;
-- 查询所有订单信息,以及订单所属的用户信息
SELECT
o.*,
u.name
FROM
USER u
RIGHT OUTER JOIN
orderlist o
ON
o.uid= u.id;
查询语句中嵌套了查询语句,我们就将嵌套的查询称为子查询。
结果是单行单列的
查询作用
可以将查询的结果作为另一条语句的查询条件,使用运算符判断。= > > =< <=等。
查询语法
SELECT 列名 FROM 表名 WHERE 列名=(SELECT 列名 FROM 表名[WHERE条件]);
结果是多行单列的
查询作用
可以作为条件,使用运算符IN或NOT IN进行判断。
查询语法
SELECT 列名 FROM 表名 WHERE 列名 [NOT]IN (SELECT 列名 FROM 表名 [WHERE 条件]);
结果是多行多列的
查询作用
查询的结果可以作为一张虚拟表参与查询。
查询语法
SELECT 列名 FROM 表名 [别名],(SELECT 列名 FROM 表名 [WHERE 条件]) [别名] [WHERE 条件];
例:
-- 查询年龄最高的用户姓名
SELECT MAX(age) FROM USER;
SELECT NAME,age FROM USER WHERE age=(SELECT MAX(age) FROM USER);
-- 查询张三和李四的订单信息
```sql
SELECT * FROM orderlist WHERE uid IN (1,2);
SELECT id FROM USER WHERE NAME IN ('张三','李四');
SELECT * FROM orderlist WHERE uid IN (SELECT id FROM USER WHERE NAME IN ('张三','李四'));
-- 查询订单表中id大于4的订单信息和所属用户信息
SELECT * FROM orderlist WHERE id > 4;
SELECT
u.name,
o.number
FROM
USER u,
(SELECT * FROM orderlist WHERE id > 4) o
WHERE
o.uid=u.id;
自关联查询概念
在同一张表中数据有关联性,我们可以把这张表当成多个表来查询。
CREATE VIEW 视图名称 [(列名列表)] AS 查询语句;
SELECT * FROM 视图名称;
UPDATE 视图名称 SET 列名=值 WHERE 条件;
ALTER VIEW 视图名称 (列名列表) AS 查询语句;
DROP VIEW [IF EXISTS] 视图名称;
备份
登录到MySQL服务器(不用登录MySQL),输入:
mysqldump -u root -p 数据库名称 > 文件保存路径
恢复
1.登录MySQL数据库(登录到MySQL)。
2.删除已备份的数据库。
3.重新创建名称相同的数据库。
4.使用该数据库。
5.导入文件执行: source 备份文件全路径。
例:
备份:
mysqldump -u root -p db5 > /root/db5.sql
恢复:
create database db5;
use da5;
source /root/db5.sql;
略
-- 修改结束分隔符
DELIMITER $
-- 创建存储过程
CREATE PROCEDURE 存储过程名称(参数列表)
BEGIN
SQL语句列表
END$
-- 修改结束分隔符
DELIMITER ;
CALL 存储过程名称(实际参数)
例:
-- 创建stu_grou() 存储过程,封装 分组查询总成绩,并按照总成绩升序排序的功能
DELIMITER $
CREATE PROCEDURE stu_group()
BEGIN
SELECT gender,SUM(score) getSum FROM student GROUP BY gender ORDER BY getSum ASC;
END$
DELIMITER ;
-- 调用stu_group()存储过程
CALL stu_group();
SELECT * FROM mysql.proc WHERE db='数据库名称';
DROP PROCEDURE [IF EXISTS] 存储过程名称;
例:
-- 查看db6数据库中所有的存储过程
SELECT * FROM mysql.proc WHERE db='db6';
-- 删除存储过程(不需要加存储过程的小括号)
DROP PROCEDURE IF EXISTS stu_group;
DECLARE 变量名 数据类型 [DEFAULT 默认值];
SET 变量名 = 变量值;
SELECT 列名 INTO 变量名 FROM 表名 [WHERE 条件];
例:
-- 定义一个int类型变量,并赋默认值为10
DELIMITER $
CREATE PROCEDURE pro_test1()
BEGIN
-- 定义变量
DECLARE num INT DEFAULT 10;
-- 使用变量
SELECT num;
END$
DELIMITER ;
-- 变量赋值-方式一
DELIMITER $
CREATE PROCEDURE pro_test2()
BEGIN
-- 定义变量
DECLARE NAME VARCHAR(10);
-- 为变量赋值
SET NAME = '存储过程';
-- 使用变量
SELECT NAME;
END$
DELIMITER ;
-- 调用pro_test2存储过程
CALL pro_test2();
-- 变量赋值-方式二
DELIMITER $
CREATE PROCEDURE pro_test3()
BEGIN
-- 定义两个变量
DECLARE men, women INT;
-- 查询男同学的总分数,为men赋值
SELECT SUM(score) INTO men FROM student WHERE gender='男';
-- 查询女同学的总分数,为women赋值
SELECT SUM(score) INTO women FROM student WHERE gender='女';
-- 使用变量
SELECT men,women;
END$
DELIMITER ;
-- 调用pro_test3存储过程
CALL pro_test3();
IF 判断条件1 THEN 执行的SQL语句;
[ELSEIF 判断条件2 THEN 执行的SQL语句2;]
...
[ELSE 执行的SQL语句n;]
END IF;
DELIMITER $
CREATE PROCEDURE pro_test4()
BEGIN
-- 定义变量
DECLARE total INT;
DECLARE info varchar(10);
-- 查询总成绩,为total赋值
SELECT SUM(score) INTO total FROM student;
-- 对总成绩判断
IF total > 380 THEN
SET info = '学习优秀';
ELSEIF total >= 320 AND total <= 380 THEN
SET info = '学习不错';
ELSE
SET info = '学习一般';
END IF;
-- 查询总成绩和描述信息
SELECT total,info;
END$
DELIMITER ;
CALL pro_test4();
CREATE PROCEDURE 存储过程名称([IN|OUT|INOUT] 参数名 数据类型)
BEGIN
SQL语句列表;
END$
IN:代表输入参数,需要由调用者传递实际数据(默认)
OUT:代表输出参数,该参数可以作为返回值
INOUT:代表既可以作为输入参数,也可以作为输出参数
例:
DELIMITER $
CREATE PROCEDURE pro_test5(IN total INT, OUT info VARCHAR(10))
BEGIN
-- 对总成绩判断
IF total > 380 THEN
SET info = '学习优秀';
ELSEIF total >= 320 AND total <= 380 THEN
SET info = '学习不错';
ELSE
SET info = '学习一般';
END IF;
END$
DELIMITER ;
-- 调用pro_test5()存储过程
CALL pro_test5(383,@info);
CALL pro_test5((SELECT SUM(score) FROM student),@info)
SELECT @info;
初始化语句;
WHILE 条件判断语句 DO
循环体语句;
条件控制语句;
END WHILE;
例:
-- 计算1~100之间的偶数和
DELIMITER $
CREATE PROCEDURE pro_test6()
BEGIN
-- 定义求和变量
DECLARE result INT DEFAULT 0;
-- 定义初始化变量
DECLARE num INT DEFAULT 1;
WHERE num <= 100 DO
IF num % 2 = 0 THEN
SET result = result + num;
END IF;
SET num = num + 1;
END WHERE;
-- 查询求和结果
SELECT result;
END$
DELIMITER ;
-- 调用pro_test6存储过程
CALL pro_test6();
CREATE FUNCTION 函数名称(参数列表)
RETURNS 返回值类型
BEGIN
SQL语句列表;
RETURN 结果;
END$
SELECT 函数名称(实际参数);
DROP FUNCTION 函数名称;
例:
-- 定义存储函数,获取学生表中成绩大于95分的学生数量
DELIMITER $
CREATE FUNCTION fun_test1()
RETURNS INT
BEGIN
-- 定义变量
DECLARE s_count INT;
-- 查询成绩大于95分的数量,为s_count赋值
SELECT COUNT(*) INTO s_count FROM student WHERE score > 95;
-- 返回统计结果
RETURN s_count;
END$
DELIMITER ;
-- 调用函数
SELECT fun_test1();
-- 删除函数
DROP FUNCTION fun_test1;
触发器是与表有关的数据库对象,可以在insert、update、delete之前或之后触发并执行触发器中执行的SQL语句。
这种特性可以协助应用系统在数据库端确保数据的完整性、日志记录、数据校验等操作。
使用别名NEW和OLD来引用触发器中发生变化的内容记录。
触发器分类
| 触发器类型 | OLD | NEW |
|---|---|---|
| INSERT型触发器 | 无(因为插入前无数据) | NEW表示将要或者已经新增的数据 |
| UPDATE型触发器 | OLD表示修改之前的数据 | NEW表示将要或已经修改后的数据 |
| DELETE型触发器 | OLD表示将要或者已经删除的数据 | 无(因为删除后状态无数据) |
DELIMITER $
CREATE TRIGGER 触发器名称
BEFORE|AFTER INSERT|UPDATE|DELETE
ON 表名
FOR EACH ROW
BEGIN
触发器要执行的功能
END$
DELIMITER ;
例(INSERT型触发器):
-- 创建INSERT型触发器,用于对account表新增数据进行日志的记录
DELIMITER $
CREATE TRIGGER account_insert
AFTER INSERT
ON account
FOR EACH ROW
BEGIN
-- NOW()函数获取当前时间,CONCAT()实现字符串拼接
INSERT INTO account_log VALUES (NULL,'INSERT',NOW(),NEW.id,CONCAT('插入后(id=',new.id,',name=',new.name,',money=',new.money,')'));
END$
DELIMITER ;
-- 向account表添加一条记录
INSERT INTO account VALUES (NULL,'王五',2000);
-- 查询account表
SELECT * FROM account;
-- 查询account_log表
SELECT * FROM account;
例(UPDATE型触发器):
DELIMITER $
CREATE TRIGGER account_update
AFTER UPDATE
ON account
FOR EACH ROW
BEGIN
INSERT INTO account_log VALUES (NULL,'UPDATE',NOW(),CONCAT('更新前(id=',old.id,',name=',old.name,',money=',old.money,')','更新后(id=',new.id,',name=',new.name,',money=',new.money,')'))
END$
DELIMITER ;
-- 修改account表中李四的金额为2000
UPDATE account SET money=2000 WHERE id=2;
-- 查询account表
SELECT * FROM account;
-- 查询account_log表
SELECT * FROM account;
例(DELETE型触发器):
DELIMITER $
CREATE TRIGGER account_delete
AFTER DELETE
ON account
FOR EACH ROW
BEGIN
INSERT INTO account_log VALUES (NULL,'DELETE',NOW(),old.id,CONCAT('删除前(id=',old.id,',name=',old.name,',money=',old.money,')'))
END$
DELIMITER ;
-- 删除account表中王五
DELETE FROM account WHERE id=3;
-- 查询account表
SELECT * FROM account;
-- 查询account_log表
SELECT * FROM account;
SHOW TRIGGERS;
DROP TRIGGER 触发器名称;
START TRANSACTION;
ROLLBACK;
COMMIT;
例:
-- 开启事务
START TRANSACTION;
-- 1.张三账户-500
UPDATE account SET money=money-500 WHERE NAME='张三';
-- 2.李四账户+500
UPDATE account SET money=money+500 WHERE NAME='李四';
-- 回滚事务
ROLLBACK;
-- 提交事务
COMMIT;
SELECT @@AUTOCOMMIT;
SET @@AUTOCOMMIT=数字;
原子性(Atomicity)
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
一致性(Consistency)
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态。也就是说一个事务执行之前和执行之后都必须处于—致性状态。
隔离性(isolcation)
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务。不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
持久性(durability)
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的。即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
多个客户端操作时,各个客户端的事务之间应该是隔离的,相互独立的,不受影响的。而如果多个事务操作 同一批数据时,就会产生不同的问题,我们需要设置不同的隔离级别来解决这些问题。
| 隔离级别 | 名称 | 会引发的问题 |
|---|---|---|
| read uncommintted | 读未提交 | 脏读、不可重复读、幻读 |
| read commintted | 读已提交 | 不可重复读、幻读 |
| repeatable read | 可重复读 | 幻读 |
| serializable | 串行化 | 无 |
| 问题 | 现象 |
|---|---|
| 脏读 | 在一个事务处理过程中读取到了另一个未提交事务中的数据,导致两次查询结果不一致 |
| 不可重复读 | 在一个事务处理过程中读取到了另一个事务中修改并已提交的数据,导致两次查询结果不一致 |
| 幻读 | 查询某数据不存在,准备插入此记录,但执行插入时发现此记录已存在,无法插入。或查询数据不存在执行删除操作,却发现删除成功 |
SELECT @@TX_ISOLATION;
SET GLOBAL TRANSACTION ISOLATION LEVEL 级别字符串;
| 序号 | 隔离级别 | 名称 | 脏读 | 不可重复读 | 幻读 | 数据库默认隔离级别 |
|---|---|---|---|---|---|---|
| 1 | read uncommitted | 读未提交 | 是 | 是 | 是 | |
| 2 | read committed | 读已提交 | 否 | 是 | 是 | Oracle |
| 3 | repeatable read | 可重复读 | 否 | 否 | 是 | MySQL |
| 4 | serializable | 串行化 | 否 | 否 | 否 |
注意:隔离级别从小到大安全性越来越高,但是效率越来越低,所以不建议修改数据库默认的隔离级别。
客户端连接
支持接口∶支持的客户端连接,例如C、Java、PHP等语言来连接MySQL数据库。
第一层∶网络连接层
连接池∶管理、缓冲用户的连接,线程处理等需要缓存的需求。
第二层∶核心服务层
管理服务和工具:系统的管理和控制工具,例如备份恢复、复制、集群等。
SQL接口∶接受SQL命令,并且返回查询结果。
查询解析器:验证和解析SQL命令,例如过滤条件、语法结构等。
查询优化器︰在执行查询之前,使用默认的一套优化机制进行优化sql语句。
缓存∶如果缓存当中有想查询的数据,则直接将缓存中的数据返回。没有的话再重新查询。
第三层∶存储引擎层
插件式存储引擎∶管理和操作数据的一种机制,包括(存储数据、如何更新、查询数据等)
第四层:系统文件层
文件系统:配置文件、数据文件、日志文件、错误文件、二进制文件等等的保存。
在生活中,引擎就是整个机器运行的核心(发动机),不同的引擎具备不同的功能,应用于不同的场景之中。
MySQL数据库使用不同的机制存取表文件,包括存储方式、索引技巧、锁定水平等不同的功能。这些不同的技术以及配套的功能称为存储引擎。
Oracle、SqlServer等数据库只有一种存储引擎。而MySQL针对不同的需求,配置不同的存储引擎,就会让数据库采取不同处理数据的方式和扩展功能。
MySQL支持的存储引擎有很多,常用的有三种: InnoDB、MylSAM、MEMORY.
特性对比
SHOW ENGINES;
SHOW TABLE STATUS FROM 数据库名称;
SHOW TABLE STATUS FROM 数据库名称 WHERE NAME = '数据表名称';
CREATE TABLE 表名(
列名,数据类型,
...
)ENGINE = 引擎名称;
ALTER TABLE 表名 ENGNE = 引擎名称;
MylSAM
特点:不支持事务和外键操作。读取速度快,节约资源。
使用场景︰以查询操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高!
InnoDB
特点:MySQL的默认存储引擎,支持事务和外键操f作。
使用场景∶对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,读写频繁的操作!
MEMORY
特点∶将所有数据保存在内存中,在需要快速定位记录和其他类似数据环境下,可以提供更快的访问。使用场景:通常用于更新不太频繁的小表,用来快速得到访问的结果!
总结:针对不同的需求场景,来选择最合适的存储引擎即可!如果不确定,则使用数据库默认的存储引擎
按照功能分类
按照结构分类
CREATE [UNIQUE | FULLTEXT] INDEX 索引名称
[USING 索引类型] -- 默认是BTREE
ON 表名(列名...);
SHOW INDEX FROM 表名;
-- 普通索引:
ALTER TABLE 表名 ADD INDEX 索引名称(列名);
-- 组合索引:
ALTER TABLE 表名 ADD INDEX 索引名称(列名1,列名2,...);
-- 主键索引:
ALTER TABLE 表名 ADD PRIMARY KEY(主键列名);
-- 外键索引:
ALTER TABLE 表名 ADD CONSTRAINT 外键名 FOREIGN KEY(本表外键列名) REFERENCES 主表名(主键列名);
-- 唯一索引:
ALTER TABLE 表名 ADD UNIQUE 索引名称(列名);
-- 全文索引:
ALTER TABLE 表名 ADD FULLTEXT 索引名称(列名);
DROP INDEX 索引名称 ON 表名;
特点
非叶子节点只存储key值
所有数据存储在叶子节点
所有叶子节点之间都有连接指针
好处
创建索引遵循的原则
1.对查询频次较高,且数据量比较大的表建立索引。
2.使用唯一索引,区分度越高,使用索引的效率越高。
3.索引字段的选择,最佳候选列应当从where子句的条件中提取。
4.索引虽然可以有效的提升查询数据的效率,但并不是多多益善。
最左匹配原则(只适用于组合索引)
例如:为user表中的name、address、phone列添加组合索引
ALTER TABLE user ADD INDEX idx_three(name,address,phone);
此时,组合索引idx_three实际建立了(name)、(name,address)、(name,address,phone)三个索引
下面的三个SQL语句都可以命中索引
SELECT * FROM user WHERE address = '北京'AND phone = '12345'AND name= '张三';
SELECT * FROM user WHERE name = '张三' AND address = '北京';
SELECT * FROM user WHERE name = '张三';
这三条SQL语句在检索时分别会使用以下索引进行数据匹配
索引字段出现的顺序可以是任意的,MySQL优化器会帮我们自动的调整where条件中的顺序
如果组合索引中最左边的列不在查询条件中,则不会命中索引
SELECT * FROM user WHERE address = '北京';
锁机制:数据库为了保证数据的一致性,在共享的资源被并发访问时变得安全所设计的一种规则
锁机制类似多线程中的同步,作用就是可以保证数据的一致性和安全性。
按操作分类
按粒度分类
按使用方式分类
不同存储引擎支持的锁
| 存储引擎 | 表锁 | 行锁 |
|---|---|---|
| InnoDB | 支持 | 支持 |
| MyISAM | 支持 | 不支持 |
| MEMORY | 支持 | 不支持 |
共享锁特点
数据可以被多个事务查询,但是不能修改
创建共享锁格式
SELECT 语句 LOCK IN SHARE MODE;
排他锁特点
加锁的数据,不能被其他事务加锁查询或修改
创建排它锁格式
SELECT 语句 FOR UPDATE;
读锁特点
所有连接只能查询数据,不能修改
读锁语法格式
加锁:
LOCK TABLE 表名 READ;
解锁:
UNLOCK TABLES;
写锁特点
其他连接不能查询和修改(包括增删改)数据,当前连接(执行加锁语句的连接)可以
写锁语法格式
加锁:
LOCK TABLE 表名 WRITE;
解锁:
UNLOCK TABLES;
悲观锁
就是很悲观,它对于数据被外界修改的操作持保守态度,认为数据随时会修改。整个数据处理中需要将数据 加锁。悲观锁一般都是依靠关系型数据库提供的锁机制,我们之前所学的锁机制都是悲观锁。
乐观锁
就是很乐观,每次自己操作数据的时候认为没有人会来修改它,所以不去加锁。
但是在更新的时候会去判断 在此期间数据有没有被修改。
需要用户自己去实现,不会发生并发抢占资源,只有在提交操作的时候检查是否违反数据完整性
乐观锁的实现方式
方式一:
给数据表中添加一个version列,每次更新后都将这个列的值加1.
读取数据时,将版本号读取出来,在执行更新的时候,比较版本号。
如果相同则执行更新,如果不相同,说明此条数据已经发生了变化。
用户自行根据这个通知来决定怎么处理,比如重新开始一遍,或者放弃本次更新。
方式二:
和版本号方式基本一样,给数据表中添加一个列,名称无所谓,数据类型需要是timestamp(时间戳).
每次更新后都将最新时间插入到此列。
读取数据时,将时间读取出来,在执行更新的时候,比较时间。
如果相同则执行更新,如果不相同,说明此条数据已经发生了变化。
随着互联网的发展,数据的量级也是不断的增长,从GB到TB到PB。对数据的各种操作也是越来越困难,一台数据库服务器已经无法满足海量数据的存储需求,所以由多台数据库服务器构成的数据库集群成了必然的方式。不过,还要保证数据的一致性,查询效率等,同时又要解决多台服务器间的通信、负载均衡等问题。
MyCat是一款出色的数据库集群软件,不仅支持MySQL,常用关系型数据库也都支持。
其实就是一个数据库中间件产品,支持MySQL集群。提供高可用性数据分片集群。
我们可以像使用MySQL一样使用MyCat。对于开发人员来说几乎感觉不到 MyCat的存在。
1. MyCat官网
http://www.mycat.io
2. 通过CRT工具上传到linux
put D:\Mycat-server-1.6.7.1-release-20190627191042-linux.tar.gz
3. 解压并查看
tar -zxvf mycat.tar.gz
cd mycat
ls
4. 为mycat目录授权
chmod -R 777 mycat
5. 配置环境变量
编辑文件:vi /etc/profile
添加内容:export MYCAT_HOME=/root/mycat
加载文件:source /etc/profile
6. 启动mycat
进入目录:cd /root/mycat/bin
执行启动:./mycat start
7. 查看端口监听
netstat -ant|grep 8066
8. SQLyog连接mycat
默认用户名:root
默认密码:123456
默认端口号:8066
修改配置网卡
在第二个虚拟机中,生成全新mac地址
重启网络
// 重启网络
service network restart
//查看ip地址
ip addr
修改mysql配置文件,更改uuid
// 编辑配置文件
vi /var/lib/mysql/auto.cnf
// 将server-uuid更改
启动MySQL并查看
//将两台服务器的防火墙关闭
systemctl stop firewalld
//启动两台服务器的mysql
service mysqld restart
//启动两台服务器的mycat
cd /root/mycat/bin
./mycat restart
//查看监听端口
netstat -ant|grep 3306
netstat -ant|grep 8066
//使用sqlyog测试连接
主从复制的概念
主服务器的配置
// 编辑mysql配置文件
vi /etc/my.cnf
//在[mysqld]下面加上:
log-bin=mysql-bin # 开启复制操作
server-id=1 # master is 1
innodb_flush_log_at_trx_commit=1
sync_binlog=1
// 登录mysql
mysql -u root -p
// 去除密码权限
SET GLOBAL validate_password_policy=0;
SET GLOBAL validate_password_length=1;
// 创建用户
CREATE USER 'hm'@'%' IDENTIFIED BY 'itheima';
// 授权
GRANT ALL ON *.* TO 'hm'@'%';
// 重启mysql
service mysqld restart
// 登录mysql
mysql -u root -p
// 查看主服务器配置
show master status;
从服务器的配置
// 编辑mysql配置文件
vi /etc/my.cnf
// 在[mysqld]下面加上:
server-id=2
// 登录mysql
mysql -u root -p
// 执行
use mysql;
drop table slave_master_info;
drop table slave_relay_log_info;
drop table slave_worker_info;
drop table innodb_index_stats;
drop table innodb_table_stats;
source /usr/share/mysql/mysql_system_tables.sql;
// 重启mysql
service mysqld restart
// 重新登录mysql
mysql -u root -p
// 执行
change master to master_host='主服务器ip地址',master_port=3306,master_user='hm',master_password='itheima',master_log_file='mysql-bin.000001',master_log_pos=4642;
// 重启mysql
service mysqld restart
// 重新登录mysql
mysql -u root -p
// 开启从节点
start slave;
// 查询结果
show slave status\G;
//Slave_IO_Running和Slave_SQL_Running都为yes才表示同步成功。
测试
-- 主服务器创建db1数据库,从服务器会自动同步
CREATE DATABASE db1;
-- 从服务器创建db2数据库,主服务器不会自动同步
CREATE DATABASE db2;
启动失败的解决方案
启动失败:Slave_IO_Running为 NO
方法一:重置slave
slave stop;
reset slave;
start slave ;
方法二:重设同步日志文件及读取位置
slave stop;
change master to master_log_file=’mysql-bin.000001’, master_log_pos=1;
start slave ;
读写分离的概念
在主服务器上修改server.xml
<user name="root" defaultAccount="true">
<property name="password">123456property>
<property name="schemas">HEIMADBproperty>
user>
DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="HEIMADB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1">schema>
<dataNode name="dn1" dataHost="localhost1" database="db1" />
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="hostM1" url="localhost:3306" user="root"
password="itheima">
<readHost host="hostS1" url="192.168.203.135:3306" user="root" password="itheima" />
writeHost>
dataHost>
mycat:schema>
配置详解
schema标签逻辑库的概念和mysql数据库中Datebase的概念相同,我们在查询这两个逻辑库中的表的时候,需要切换到该逻辑库下才可以查到所需要的表。
dataNode属性:该属性用于绑定逻辑库到某个具体的database上。
dataNode标签: dataNode标签定义了mycat中的数据节点,也就是数据分片。一个dataNode标签就是一个独立的数据分片。
name属性:定义数据节点的名字,这个名字需要是唯一的,我们需要在table标签上应用这个名字,来建立表与分片对应的关系。
dataHost属性:该属性用于定义该分片属于那个数据库实例,属性值是引用datahost标签定义的name属性。
database属性:该属性用于定义该分片属于那个具体数据库实例上的具体库,因为这里使用两个纬度来定义分片,就是:实例+具体的库。因为每个库上建立的表和表结构是一样的。所以这样做就可以轻松的对表进行水平拆分。
dataHost标签:该标签在mycat逻辑库中也是作为最底层的标签存在,直接定义了具体的数据库实例、读写分离配置和心跳语句。
balance属性: 负载均衡类型
balance=0: 不开启读写分离,所有读操作都发送到当前可用的writeHost上。
balance=1: 全部的readHost与Stand by writeHost都参与select语句的负载均衡
balance=2: 所有的读操作都随机在writeHost,readHost上分发。
balance=3: 所有的读请求都随机分配到writeHost对应的readHost上执行,writeHost不负担读压力。
switchType属性:
-1:表示不自动切换。
1 :默认值,表示自动切换
2:表示基于MySQL主从同步状态决定是否切换,心跳语句: show slave status.
3:表示基于mysql galary cluster的切换机制,适合mycat1.4之上的版本,心跳语句show status like “%esrep%”;
writeHost标签,readHost标签:这两个标签指定后端数据库的相关配置给mycat,用于实例化后端连接池。唯一不同的是,writeHost指定写实例、readHost指定读实例,组合这些读写实例来满足系统的要求。
测试
// 重启mycat
cd /root/mycat/bin
./mycat restart
// 查看端口监听
netstat -ant|grep 8066
-- 创建学生表
CREATE TABLE student(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10)
);
-- 查询学生表
SELECT * FROM student;
-- 添加两条记录
INSERT INTO student VALUES (NULL,'张三'),(NULL,'李四');
-- 停止主从复制后,添加的数据只会保存到主服务器上。
INSERT INTO student VALUES (NULL,'王五');
-- 主服务器:查询学生表,可以看到数据
SELECT * FROM student;
-- 从服务器:查询学生表,可以看到数据(因为有主从复制)
SELECT * FROM student;
-- 从服务器:删除一条记录。(主服务器并没有删除,mycat中间件查询的结果是从服务器的数据)
DELETE FROM student WHERE id=2;
分库分表的概念
Mycat水平拆分
修改主服务器的server.xml
0:本地文件方式
在mycat/conf/sequence_conf.properties文件中:
GLOBAL.MINDI=10000最小值
GLOBAL.MAXID=20000最大值,建议修改到9999999999
1:数据库方式
分库分表中保证全局主键自增唯一,但是需要执行mycat函数,配置sequence_db_conf.properties
2:时间戳方式
mycat实现的时间戳,建议varchar类型,要注意id的长度
<property name="sequnceHandlerType">0property>
#default global sequence
GLOBAL.HISIDS= # 可以自定义关键字
GLOBAL.MINID=10001 # 最小值
GLOBAL.MAXID=20000 # 最大值
GLOBAL.CURID=10000
DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="HEIMADB" checkSQLschema="false" sqlMaxLimit="100">
<table name="product" primaryKey="id" dataNode="dn1,dn2,dn3" rule="mod-long"/>
schema>
<dataNode name="dn1" dataHost="localhost1" database="db1" />
<dataNode name="dn2" dataHost="localhost1" database="db2" />
<dataNode name="dn3" dataHost="localhost1" database="db3" />
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="hostM1" url="localhost:3306" user="root"
password="itheima">
<readHost host="hostS1" url="192.168.203.135:3306" user="root" password="itheima" />
writeHost>
dataHost>
mycat:schema>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<property name="count">3property>
function>
测试
-- 创建product表
CREATE TABLE product(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20),
price INT
);
-- 添加6条数据
INSERT INTO product(id,NAME,price) VALUES (NEXT VALUE FOR MYCATSEQ_GLOBAL,'苹果手机',6999);
INSERT INTO product(id,NAME,price) VALUES (NEXT VALUE FOR MYCATSEQ_GLOBAL,'华为手机',5999);
INSERT INTO product(id,NAME,price) VALUES (NEXT VALUE FOR MYCATSEQ_GLOBAL,'三星手机',4999);
INSERT INTO product(id,NAME,price) VALUES (NEXT VALUE FOR MYCATSEQ_GLOBAL,'小米手机',3999);
INSERT INTO product(id,NAME,price) VALUES (NEXT VALUE FOR MYCATSEQ_GLOBAL,'中兴手机',2999);
INSERT INTO product(id,NAME,price) VALUES (NEXT VALUE FOR MYCATSEQ_GLOBAL,'OOPO手机',1999);
-- 查询product表
SELECT * FROM product;
-- 在不同数据库中查询product表
SELECT * FROM product;
-- 在不同数据库中查询product表
SELECT * FROM product;
Mycat垂直拆分
DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="HEIMADB" checkSQLschema="false" sqlMaxLimit="100">
<table name="product" primaryKey="id" dataNode="dn1,dn2,dn3" rule="mod-long"/>
<table name="dog" primaryKey="id" autoIncrement="true" dataNode="dn4" />
<table name="cat" primaryKey="id" autoIncrement="true" dataNode="dn4" />
<table name="apple" primaryKey="id" autoIncrement="true" dataNode="dn5" />
<table name="banana" primaryKey="id" autoIncrement="true" dataNode="dn5" />
schema>
<dataNode name="dn1" dataHost="localhost1" database="db1" />
<dataNode name="dn2" dataHost="localhost1" database="db2" />
<dataNode name="dn3" dataHost="localhost1" database="db3" />
<dataNode name="dn4" dataHost="localhost1" database="db4" />
<dataNode name="dn5" dataHost="localhost1" database="db5" />
<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1"
writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select user()heartbeat>
<writeHost host="hostM1" url="localhost:3306" user="root"
password="itheima">
<readHost host="hostS1" url="192.168.203.135:3306" user="root" password="itheima" />
writeHost>
dataHost>
mycat:schema>
测试
-- 创建dog表
CREATE TABLE dog(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10)
);
-- 添加数据
INSERT INTO dog(id,NAME) VALUES (NEXT VALUE FOR MYCATSEQ_GLOBAL,'哈士奇');
-- 查询dog表
SELECT * FROM dog;
-- 创建cat表
CREATE TABLE cat(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10)
);
-- 添加数据
INSERT INTO cat(id,NAME) VALUES (NEXT VALUE FOR MYCATSEQ_GLOBAL,'波斯猫');
-- 查询cat表
SELECT * FROM cat;
-- 创建apple表
CREATE TABLE apple(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10)
);
-- 添加数据
INSERT INTO apple(id,NAME) VALUES (NEXT VALUE FOR MYCATSEQ_GLOBAL,'红富士');
-- 查询apple表
SELECT * FROM apple;
-- 创建banana表
CREATE TABLE banana(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10)
);
-- 添加数据
INSERT INTO banana(id,NAME) VALUES (NEXT VALUE FOR MYCATSEQ_GLOBAL,'香蕉');
-- 查询banana表
SELECT * FROM banana;
-- 查询dog表
SELECT * FROM dog;
-- 查询cat表
SELECT * FROM cat;
-- 查询apple表
SELECT * FROM apple;
-- 查询banana表
SELECT * FROM banana;
-- 查询dog表
SELECT * FROM dog;
-- 查询cat表
SELECT * FROM cat;
-- 查询apple表
SELECT * FROM apple;
-- 查询banana表
SELECT * FROM banana;
常用的分片规则:总共十个(基本够用)
一、枚举法
<tableRule name="sharding-by-intfile">
<rule>
<columns>user_idcolumns>
<algorithm>hash-intalgorithm>
rule>
tableRule>
<function name="hash-int" class="io.mycat.route.function.PartitionByFileMap">
<property name="mapFile">partition-hash-int.txtproperty>
<property name="type">0property>
<property name="defaultNode">0property>
function>
partition-hash-int.txt 配置:
10000=0
10010=1
上面columns 标识将要分片的表字段,algorithm 分片函数,
其中分片函数配置中,mapFile标识配置文件名称,type默认值为0,0表示Integer,非零表示String,
所有的节点配置都是从0开始,及0代表节点1
/**
* defaultNode 默认节点:小于0表示不设置默认节点,大于等于0表示设置默认节点,结点为指定的值
*
默认节点的作用:枚举分片时,如果碰到不识别的枚举值,就让它路由到默认节点
* 如果不配置默认节点(defaultNode值小于0表示不配置默认节点),碰到
* 不识别的枚举值就会报错,
* like this:can't find datanode for sharding column:column_name val:ffffffff
*/
二、固定分片hash算法
<tableRule name="rule1">
<rule>
<columns>user_idcolumns>
<algorithm>func1algorithm>
rule>
tableRule>
<function name="func1" class="io.mycat.route.function.PartitionByLong">
<property name="partitionCount">2,1property>
<property name="partitionLength">256,512property>
function>
配置说明:
上面columns 标识将要分片的表字段,algorithm 分片函数,
partitionCount 分片个数列表,partitionLength 分片范围列表
分区长度:默认为最大2^n=1024 ,即最大支持1024分区
约束 :
count,length两个数组的长度必须是一致的。
1024 = sum((count[i]*length[i])). count和length两个向量的点积恒等于1024
用法例子:
@Test
public void testPartition() {
// 本例的分区策略:希望将数据水平分成3份,前两份各占25%,第三份占50%。(故本例非均匀分区)
// |<---------------------1024------------------------>|
// |<----256--->|<----256--->|<----------512---------->|
// | partition0 | partition1 | partition2 |
// | 共2份,故count[0]=2 | 共1份,故count[1]=1 |
int[] count = new int[] { 2, 1 };
int[] length = new int[] { 256, 512 };
PartitionUtil pu = new PartitionUtil(count, length);
// 下面代码演示分别以offerId字段或memberId字段根据上述分区策略拆分的分配结果
int DEFAULT_STR_HEAD_LEN = 8; // cobar默认会配置为此值
long offerId = 12345;
String memberId = "qiushuo";
// 若根据offerId分配,partNo1将等于0,即按照上述分区策略,offerId为12345时将会被分配到partition0中
int partNo1 = pu.partition(offerId);
// 若根据memberId分配,partNo2将等于2,即按照上述分区策略,memberId为qiushuo时将会被分到partition2中
int partNo2 = pu.partition(memberId, 0, DEFAULT_STR_HEAD_LEN);
Assert.assertEquals(0, partNo1);
Assert.assertEquals(2, partNo2);
}
如果需要平均分配设置:平均分为4分片,partitionCount*partitionLength=1024
<function name="func1" class="org.opencloudb.route.function.PartitionByLong">
<property name="partitionCount">4property>
<property name="partitionLength">256property>
function>
三、范围约定
<tableRule name="auto-sharding-long">
<rule>
<columns>user_idcolumns>
<algorithm>rang-longalgorithm>
rule>
tableRule>
<function name="rang-long" class="io.mycat.route.function.AutoPartitionByLong">
<property name="mapFile">autopartition-long.txtproperty>
function>
# range start-end ,data node index
# K=1000,M=10000.
0-500M=0
500M-1000M=1
1000M-1500M=2
或
0-10000000=0
10000001-20000000=1
配置说明:
上面columns 标识将要分片的表字段,algorithm 分片函数,
rang-long 函数中mapFile代表配置文件路径
所有的节点配置都是从0开始,及0代表节点1,此配置非常简单,即预先制定可能的id范围到某个分片
四、求模法
<tableRule name="mod-long">
<rule>
<columns>user_idcolumns>
<algorithm>mod-longalgorithm>
rule>
tableRule>
<function name="mod-long" class="io.mycat.route.function.PartitionByMod">
<property name="count">3property>
function>
配置说明:
上面columns 标识将要分片的表字段,algorithm 分片函数,
此种配置非常明确即根据id与count(你的结点数)进行求模预算,相比方式1,此种在批量插入时需要切换数据源,id不连续
五、日期列分区法
<tableRule name="sharding-by-date">
<rule>
<columns>create_timecolumns>
<algorithm>sharding-by-datealgorithm>
rule>
tableRule>
<function name="sharding-by-date" class="io.mycat.route.function..PartitionByDate">
<property name="dateFormat">yyyy-MM-ddproperty>
<property name="sBeginDate">2014-01-01property>
<property name="sPartionDay">10property>
function>
配置说明:
上面columns 标识将要分片的表字段,algorithm 分片函数,
配置中配置了开始日期,分区天数,即默认从开始日期算起,分隔10天一个分区
还有一切特性请看源码
Assert.assertEquals(true, 0 == partition.calculate("2014-01-01"));
Assert.assertEquals(true, 0 == partition.calculate("2014-01-10"));
Assert.assertEquals(true, 1 == partition.calculate("2014-01-11"));
Assert.assertEquals(true, 12 == partition.calculate("2014-05-01"));
六、通配取模
<tableRule name="sharding-by-pattern">
<rule>
<columns>user_idcolumns>
<algorithm>sharding-by-patternalgorithm>
rule>
tableRule>
<function name="sharding-by-pattern" class="io.mycat.route.function.PartitionByPattern">
<property name="patternValue">256property>
<property name="defaultNode">2property>
<property name="mapFile">partition-pattern.txtproperty>
function>
partition-pattern.txt
# id partition range start-end ,data node index
###### first host configuration
1-32=0
33-64=1
65-96=2
97-128=3
######## second host configuration
129-160=4
161-192=5
193-224=6
225-256=7
0-0=7
配置说明:
上面columns 标识将要分片的表字段,algorithm 分片函数,patternValue 即求模基数,defaoultNode 默认节点,如果不配置了默认,则默认是0即第一个结点
mapFile 配置文件路径
配置文件中,1-32 即代表id%256后分布的范围,如果在1-32则在分区1,其他类推,如果id非数字数据,则会分配在defaoultNode 默认节点
String idVal = "0";
Assert.assertEquals(true, 7 == autoPartition.calculate(idVal));
idVal = "45a";
Assert.assertEquals(true, 2 == autoPartition.calculate(idVal));
七、ASCII码求模通配
<tableRule name="sharding-by-prefixpattern">
<rule>
<columns>user_idcolumns>
<algorithm>sharding-by-prefixpatternalgorithm>
rule>
tableRule>
<function name="sharding-by-pattern" class="io.mycat.route.function.PartitionByPrefixPattern">
<property name="patternValue">256property>
<property name="prefixLength">5property>
<property name="mapFile">partition-pattern.txtproperty>
function>
partition-pattern.txt
# range start-end ,data node index
# ASCII
# 48-57=0-9
# 64、65-90=@、A-Z
# 97-122=a-z
###### first host configuration
1-4=0
5-8=1
9-12=2
13-16=3
###### second host configuration
17-20=4
21-24=5
25-28=6
29-32=7
0-0=7
配置说明:
上面columns 标识将要分片的表字段,algorithm 分片函数,patternValue 即求模基数,prefixLength ASCII 截取的位数
mapFile 配置文件路径
配置文件中,1-32 即代表id%256后分布的范围,如果在1-32则在分区1,其他类推
此种方式类似方式6只不过采取的是将列种获取前prefixLength位列所有ASCII码的和进行求模sum%patternValue ,获取的值,在通配范围内的
即 分片数,
/**
* ASCII编码:
* 48-57=0-9阿拉伯数字
* 64、65-90=@、A-Z
* 97-122=a-z
*
*/
如
String idVal="gf89f9a";
Assert.assertEquals(true, 0==autoPartition.calculate(idVal));
idVal="8df99a";
Assert.assertEquals(true, 4==autoPartition.calculate(idVal));
idVal="8dhdf99a";
Assert.assertEquals(true, 3==autoPartition.calculate(idVal));
八、编程指定
<tableRule name="sharding-by-substring">
<rule>
<columns>user_idcolumns>
<algorithm>sharding-by-substringalgorithm>
rule>
tableRule>
<function name="sharding-by-substring" class="io.mycat.route.function.PartitionDirectBySubString">
<property name="startIndex">0property>
<property name="size">2property>
<property name="partitionCount">8property>
<property name="defaultPartition">0property>
function>
配置说明:
上面columns 标识将要分片的表字段,algorithm 分片函数
此方法为直接根据字符子串(必须是数字)计算分区号(由应用传递参数,显式指定分区号)。
例如id=05-100000002
在此配置中代表根据id中从startIndex=0,开始,截取siz=2位数字即05,05就是获取的分区,如果没传默认分配到defaultPartition
九、字符串拆分hash解析
<tableRule name="sharding-by-stringhash">
<rule>
<columns>user_idcolumns>
<algorithm>sharding-by-stringhashalgorithm>
rule>
tableRule>
<function name="sharding-by-substring" class="io.mycat.route.function.PartitionByString">
<property name=length>512property>
<property name="count">2property>
<property name="hashSlice">0:2property>
function>
配置说明:
上面columns 标识将要分片的表字段,algorithm 分片函数
函数中length代表字符串hash求模基数,count分区数,hashSlice hash预算位
即根据子字符串 hash运算
hashSlice : 0 means str.length(), -1 means str.length()-1
/**
* "2" -> (0,2)<br/>
* "1:2" -> (1,2)<br/>
* "1:" -> (1,0)<br/>
* "-1:" -> (-1,0)<br/>
* ":-1" -> (0,-1)<br/>
* ":" -> (0,0)<br/>
*/
public class PartitionByStringTest {
@Test
public void test() {
PartitionByString rule = new PartitionByString();
String idVal=null;
rule.setPartitionLength("512");
rule.setPartitionCount("2");
rule.init();
rule.setHashSlice("0:2");
// idVal = "0";
// Assert.assertEquals(true, 0 == rule.calculate(idVal));
// idVal = "45a";
// Assert.assertEquals(true, 1 == rule.calculate(idVal));
//last 4
rule = new PartitionByString();
rule.setPartitionLength("512");
rule.setPartitionCount("2");
rule.init();
//last 4 characters
rule.setHashSlice("-4:0");
idVal = "aaaabbb0000";
Assert.assertEquals(true, 0 == rule.calculate(idVal));
idVal = "aaaabbb2359";
Assert.assertEquals(true, 0 == rule.calculate(idVal));
}
十、一致性hash
<tableRule name="sharding-by-murmur">
<rule>
<columns>user_idcolumns>
<algorithm>murmuralgorithm>
rule>
tableRule>
<function name="murmur" class="io.mycat.route.function.PartitionByMurmurHash">
<property name="seed">0property>
<property name="count">2property><!-- 要分片的数据库节点数量,必须指定,否则没法分片—>
<property name="virtualBucketTimes">160property>
function>
一致性hash预算有效解决了分布式数据的扩容问题,前1-9中id规则都多少存在数据扩容难题,而10规则解决了数据扩容难点