mysql 查看索引、添加索引、删除索引命令

查看索引

mysql> show index from tblname;
mysql> show keys from tblname;


mysql> show index from center_bank_rate;
+------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table            | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| center_bank_rate |          0 | PRIMARY  |            1 | id          | A         |           8 |     NULL | NULL   |      | BTREE      |         |               |
+------------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.00 sec)

· Table
表的名称。

· Non_unique
如果索引不能包括重复词,则为0。如果可以,则为1。

· Key_name
索引的名称。

· Seq_in_index
索引中的列序列号,从1开始。

· Column_name
列名称。

· Collation
列以什么方式存储在索引中。在MySQL中,有值‘A’(升序)或NULL(无分类)。

· Cardinality
索引中唯一值的数目的估计值。通过运行ANALYZE TABLE或myisamchk -a可以更新。基数根据被存储为整数的统计数据来计数,所以即使对于小型表,该值也没有必要是精确的。基数越大,当进行联合时,MySQL使用该索引的机 会就越大。

· Sub_part
如果列只是被部分地编入索引,则为被编入索引的字符的数目。如果整列被编入索引,则为NULL。

· Packed
指示关键字如何被压缩。如果没有被压缩,则为NULL。

· Null
如果列含有NULL,则含有YES。如果没有,则该列含有NO。

· Index_type
用过的索引方法(BTREE, FULLTEXT, HASH, RTREE)。

· Comment

添加索引

ALTER TABLE Persons
ADD CONSTRAINT uc_PersonID UNIQUE (Id_P,LastName)

删除索引

mysql> alter table center_bank_rate drop index UK_r60biiucgoujcw6htlywu02bg;

Query OK, 8 rows affected (0.11 sec)
Records: 8  Duplicates: 0  Warnings: 0

读懂MySQL执行计划

前言

在之前的面试过程中,问到执行计划,有很多童鞋不知道是什么?甚至将执行计划与执行时间认为是同一个概念。今天我们就一起来了解一下执行计划到底是什么?有什么用途?

执行计划是什么?

执行计划,简单的来说,是SQL在数据库中执行时的表现情况,通常用于SQL性能分析,优化等场景。在MySQL使用 explain 关键字来查看SQL的执行计划。如下所示:

//1. 查询t_base_user
select * from t_base_user where name="andyqian";

//2. 查看上述语句的执行计划
explain select * from t_base_user where name="andyqian";

执行查看上述2语句后,我们可以得出以下执行计划结果

id | select_type|table|type|possible_kes|key|key_len|ref|rows|Extra
—|—|—|—|—|—|—|—|—|—
1|SIMPLE|t_base_user|ALL| | | |1|Using where

上面执行计划是什么意思呢?有什么参考价值呢?

上面这个执行计划给到的信息是: 这个结果通过一个简单的语句全表扫描,共扫描1行,使用where条件在t_base_user表中筛选出的。发现该语句并没有走索引,为什么是这样的呢?别急,我们紧接着看下一节。

读懂执行计划

通过上面,我们知道了什么是执行计划,也看到了执行计划到底是什么东西,现在我们来具体了解一下,MySQL执行计划中,每个属性代表的是什么意思?

id  select_type table   type    possible_kes    key        key_len      ref    rows    Extra

我们一一来介绍,并说明每个属性有哪些可选值,以及每个可选值的意思。

  • id
    表示查询中select操作表的顺序,按顺序从大到依次执行

  • select_type :
    该表示选择的类型,可选值有: SIMPLE(简单的),

  • type :

该属性表示访问类型,有很多种访问类型。
最常见的其中包括以下几种: ALL(全表扫描), index(索引扫描),range(范围扫描),ref (非唯一索引扫描),eq_ref(唯一索引扫描,),(const)常数引用, 访问速度依次由慢到快。

其中 : range(范围)常见与 between and …, 大于 and 小于这种情况。
提示 : 慢SQL是否走索引,走了什么索引,也就可以通过该属性查看了。

  • table :
    表示该语句查询的表

  • possible_keys :
    顾名思义,该属性给出了,该查询语句,可能走的索引,(如某些字段上索引的名字)这里提供的只是参考,而不是实际走的索引,也就导致会有possible_Keys不为null,key为空的现象。

  • key :
    显示MySQL实际使用的索引,其中就包括主键索引(PRIMARY),或者自建索引的名字。

  • key_len :
    表示索引所使用的字节数,

  • ref :
    连接匹配条件,如果走主键索引的话,该值为: const, 全表扫描的话,为null值

  • rows :
    扫描行数,也就是说,需要扫描多少行,采能获取目标行数,一般情况下会大于返回行数。通常情况下,rows越小,效率越高, 也就有大部分SQL优化,都是在减少这个值的大小。

注意: 理想情况下扫描的行数与实际返回行数理论上是一致的,但这种情况及其少,如关联查询,扫描的行数就会比返回行数大大增加)

  • Extra
    这个属性非常重要,该属性中包括执行SQL时的真实情况信息,如上面所属,使用到的是”using where”,表示使用where筛选得到的值,常用的有:
    “Using temporary”: 使用临时表 “using filesort”: 使用文件排序

看到这里,我们应该已经发现,在第一步中,我们的这条SQL

select * from t_base_user where name="andyqian";

是没有走索引的,而且还是全表扫描,在数据量少的情况下,问题还不会特别突出,如果数据量比较大,这可是个会造成生产事故的慢查询哦,现在我们改造一下,将name字段添加上索引,

# 添加索引
alter table t_base_user add index idx_name(name);

看看它的执行计划是怎样的。

id | select_type|table|type|possible_kes|key|key_len|ref|rows|Extra
—|—|—|—|—|—|—|—|—|—
1|SIMPLE|t_base_user|ref|idx_name|idx_name|93|cons|1|Using where

你看,现在已经走idx_name索引了,其type从All(全表扫描)到ref(非唯一索引了),别看就只有这一点点小区别,在大数据量的时候,可是会起大作用的哦。

数据结

本文中演示的数据结构如下:

# 创建表  
create table t_base_user(
oid bigint(20) not null primary key auto_increment,
name varchar(30) null comment "name",
email varchar(30) null comment "email",
age int null comment "age",
telephone varchar(30) null comment "telephone",
status tinyint(4) null comment "0  无效 1 有效",
created_at datetime null comment "",
updated_at datetime null comment ""
)

## 新增记录:
insert into t_base_user(name,email,age,telephone,created_at,updated_at)values("andyqian","[email protected]",20,"15608411",now(),now());
)

最后

一个好的数据库表设计,从一开始就应该考虑添加索引,而不是到最后发现慢SQL了,影响业务了,才来补救。其实我在工作经历当中,由于新建表,或新加字段后,忘记添加索引也造成了多次生产事故,记忆犹新!!!
其实新建索引也是有一定的原则的,建什么索引,建在哪些字段上,这里面还有不少知识呢,下一篇文章写,尽请期待吧!

MySQL 备份和恢复机制

一、 备份恢复策略

进行备份或恢复操作时需要考虑一些因素:

  • 确定要备份的表的存储引擎是事务型还是非事务型,两种不同的存储引擎备份方式在处理数据一致性方面是不太一样的。

  • 确定使用全备份还是增量备份。全备份的优点是备份保持最新备份,恢复的时候可以花费更少的时间;缺点是如果数据量大,将会花费很多的时间,并对系统造成较长时间的压力。增量备份相反,只需要备份每天的增量日志,备份时间少,对负载压力也小;缺点就是恢复的时候需要全备份加上次备份到故障前的所有日志,恢复时间长一些。

  • 可以考虑采用复制的方法来做异地备份,但不能代替备份,它对数据库的误操作也无能为力。

  • 要定期做备份,备份的周期要充分考虑系统可以承受的恢复时间。备份要在系统负载较小的时候进行

  • 确保 MySQL 打开 log-bin 选项,有了 binlog,MySQL 才可以在必要的时候做完整恢复,或基于时间点的恢复,或基于位置的恢复。

  • 经常做备份恢复测试,确保备份时有效的,是可以恢复的。

二、 逻辑备份和恢复

在 MySQL 中,逻辑备份的最大优点是对于各种存储引擎都可以用同样的方法来备份;而物理备份则不同,不同的存储引擎有着不同的备份方法,因此,对于不同存储引擎混合的数据库,逻辑备份会简单一点。

1. 备份

MySQL 中的逻辑备份是将数据库中的数据备份为一个文本文件,备份的文件可以被查看和编辑。在 MySQL 中,可以使用 mysqldump 工具来完成逻辑备份:

// 备份指定的数据库或者数据库中的某些表  
shell> mysqldump [options] db_name [tables]  

// 备份指定的一个或多个数据库  
shell> mysqldump [options] --database DB1 [DB2,DB3...]  

// 备份所有数据库  
shell> mysqldump [options] --all-database

如果没有指定数据库中的任何表,默认导出所有数据库中的所有表。

示例:

(1). 备份所有数据库:

shell>mysqldump -uroot -p --all-database > all.sql

(2). 备份数据库 test

shell>mysqldump -uroot -p test > test.sql

(3). 备份数据库 test 下的表 emp

shell> mysqldump -uroot -p test emp > emp.sql

(4). 备份数据库 test 下的表 emp 和 dept

shell> mysqldump -uroot -p test emp dept > emp_dept.sql 

(5). 备份数据库test 下的所有表为逗号分割的文本,备份到 /tmp:

shell> mysqlddump -uroot -p -T /tmp test emp --fields-terminated-by ','
shell> more emp.txt  

1,z1
2,z2
3,z3
4,z4

注意: 为了保证数据备份的一致性,myisam 存储引擎在备份时需要加上 -l 参数,表示将所有表加上读锁,在备份期间,所有表将只能读而不能进行数据更新。但是对于事务存储引擎来说,可以采用更好的选项 –single-transaction,此选项使得 innodb 存储引擎得到一个快照(snapshot),使得备份的数据能够保证一致性。

2. 完全恢复

mysqldump 的恢复也很简单,将备份作为输入执行即可:

mysql -uroot -p db_name < backfile

注意,将备份恢复后数据并不完整,还需要将备份后执行的日志进行重做:

mysqlbinlog binlog-file | mysql -uroot -p

完整的 mysqldump 备份与恢复示例:

(1). 凌晨 2:00,备份数据库:

root@bogon:/usr/local/mysql/bin$ ./mysqldump -uroot -p -l -F t2 > t2.dmp
Enter password: 

其中 -l 参数表示给所有表加读锁,-F 表示生成一个新的日志文件,此时,t2 中 emp 表的数据如下:

# 为了便于测试,执行 reset master 删除所有 binlog。
MySQL [(none)]> reset master;
Query OK, 0 rows affected (0.00 sec)

# 此时只有一个 binlog 日志文件   mysql-bin.000001
MySQL [t2]> select * from test;
+------+------+
| id   | name |
+------+------+
|    1 | a    |
|    2 | b    |
+------+------+
2 rows in set (0.00 sec)

(2). 备份完毕后,插入新的数据:

# 因为上一步执行是加入了 -F 选项, 所以接下来的操作会被记录到新的二进制文件,即名为 mysql-bin.000002 的文件
MySQL [t2]> insert into test values (3,'c');
Query OK, 1 row affected (0.00 sec)

MySQL [t2]> insert into test values (4,'d');
Query OK, 1 row affected (0.00 sec)

(3). 数据库突然故障(其实是小伙伴没事儿删库练手玩儿),数据无法访问。需要恢复备份:

删库跑路:

# 这里为了便于测试,不把删库操作记入日志,当前 session 设置 sql_log_bin 为 off。
# 删库后,执行 flush logs,让后续的 binlog 到新的文件中,即名为 mysql-bin.000003中
MySQL [t2]> set sql_log_bin = 0;
Query OK, 0 rows affected (0.00 sec)

MySQL [t2]> show variables like "%sql_log_bin%";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| sql_log_bin   | OFF   |
+---------------+-------+
1 row in set (0.00 sec)

MySQL [t2]> drop database t2;
Query OK, 1 row affected (0.01 sec)

MySQL [(none)]> flush logs;
Query OK, 0 rows affected (0.22 sec)


MySQL [t2]> drop database t2;
Query OK, 3 rows affected (0.23 sec)

MySQL [(none)]> exit;
Bye

数据恢复:

root@bogon:/usr/local/mysql/bin# ./mysql -e "create database t2"   
root@bogon:/usr/local/mysql/bin# ./mysql t2 < t2.dmp 

*******************************************************************
MySQL [t2]> select * from test;
+------+------+
| id   | name |
+------+------+
|    1 | a    |
|    2 | b    |
+------+------+
2 rows in set (0.00 sec)

(4). 使用 mysqlbinlog 恢复自 mysqldump 备份以来的 binglog

根据前面操作的内容,可知从备份的时间点到删库的时间点之间的操作被记录到了 mysql-bin.000002 文件中

root@bogon:/usr/local/mysql/bin# ./mysqlbinlog --no-defaults /data/mysql/mysql-bin.000002 | ./mysql t2

*******************************************************
MySQL [t2]> select * from test;
+------+------+
| id   | name |
+------+------+
|    1 | a    |
|    2 | b    |
|    3 | c    |
|    4 | d    |
+------+------+
4 rows in set (0.00 sec)

至此,数据恢复成功。

3. 基于时间点恢复

由于误操作,比如误删除了一张表,这时使用完全恢复时没有用的,因为日志里面还存在误操作的语句,我们需要的是恢复到误操作之前的状态,然后跳过误操作语句,再恢复后面执行的语句,完成恢复。这种恢复叫不完全恢复,在 MySQL 中,不完全恢复分为 基于时间点的恢复和基于位置的恢复。 基于时间点恢复的操作步骤:

(1) 如果是上午 10 点发生了误操作,可以用以下语句用备份和 binlog 将数据恢复到故障前:

shell>mysqlbinlog --stop-date="2017-09-30 9:59:59" /data/mysql/mysql-bin.123456 | mysql -uroot -ppassword

(2) 跳过故障时的时间点,继续执行后面的 binlog,完成恢复。

shell>mysqlbinlog --start-date="2017-09-30 10:01:00" /data/mysql/mysql-bin.123456 | mysql -uroot -ppassword

4. 基于位置恢复

和基于时间点的恢复类似,但是更精确,因为同一个时间点可能有很多条 sql 语句同时执行。恢复的操作步骤如下:

(1) 在 shell 下执行命令:

shell>mysqlbinlog --start-date="2017-09-30 9:59:59" --stop-date="2017-09-30 10:01:00" /data/mysql/mysql-bin.123456 > /tmp/mysql_restore.sql

该命令将在 /tmp 目录创建小的文本文件,编辑此文件,知道出错语句前后的位置号,例如前后位置号分别为 368312 和 368315。

(2) 恢复了以前的备份文件后,应从命令行输入下面的内容:

shell>mysqlbinlog --stop-position="368312" /data/mysql/mysql-bin.123456 | mysql -uroot -ppassword  
shell>mysqlbinlog --start-position="368315" /data/mysql/mysql-bin.123456 | mysql -uroot -ppassword 

上面的第一行将恢复到停止位置为止的所有事务。下一行将恢复从给定的起始位置直到二进制日志结束的所有事务。因为 mysqlbinlog 的输出包括每个 sql 语句记录之前的 set timestamp 语句,因此恢复的数据和相关的 mysql 日志将反应事务执行的原时间。

三、物理备份和恢复

物理备份又分为冷备份和热备份两种,和逻辑备份相比,它的最大优点是备份和恢复的速度更快,因为物理备份的原理都是基于文件的 cp。

1. 冷备份

冷备份其实就是停掉数据库服务,cp 数据文件的方法。(基本不考虑这种方法)

2. 热备份

在 MySQL 中,对于不同的存储引擎热备份的方法也有所不同。

(1) myisam 存储引擎

myisam 存储引擎的热备份有很多方法,本质其实就是将要备份的表加读锁,然后再 cp 数据文件到备份目录。常用的有以下两种方法:

  • 使用 mysqlhotcopy 工具
// mysqlhotcopy 是 MySQL 的一个自带的热备份工具  
shell> mysqlhotcopy db_name [/path/to/new_directory]
  • 手工锁表 copy
// 在 mysqlhotcopy 使用不正常的情况下,可以用手工来做热备份

mysql>flush tables for read;

cp 数据文件到备份目录即可,

(2) innodb 存储引擎(另写)

使用第三方工具 ibbackup、xtrabackup、innobacupex

四、 表的导入导出

在数据库的日常维护中,表的导入导出时很频繁的一类操作。

1. 导出

在某些情况下,为了一些特定的目的,经常需要将表里的数据导出为某些符号分割的纯数据文本,而不是 sql 语句:

  • 用来作为 Excel 显示;

  • 单纯为了节省备份空间;

  • 为了快速的加载数据,load data 的加载速度比普通 sql 加载要快 20 倍以上。

使用 select …into outfile … 命令来导出数据,具体语法如下:

mysql> select * from tablename into outfile 'target_file' [option];

其中 option 参数可以是以下选项:

fields terminated by 'string'                   // 字段分隔符,默认为制表符't'
fields [optionally] enclosed by 'char'          // 字段引用符,如果加 optionally 选项则只用在 char、varchar 和 text 等字符型字段上,默认不使用引用符  
fields escaped by ‘char’                        // 转移字符、默认为 ''  
lines starting by 'string'                      // 每行前都加此字符串,默认''  
lines terminated by 'string'                    // 行结束符,默认为'n'  

# char 表示此符号只能是单个字符,string表示可以是字符串。

例如,将 test 表中数据导出为数据文本,其中,字段分隔符为“,”,字段引用符为“””,记录结束符为回车符:

MySQL [t2]> select * from test into outfile '/data/mysql/outfile.txt' fields terminated by "," enclosed by '"';
Query OK, 4 rows affected (0.02 sec)
zj@bogon:/data/mysql$ more outfile.txt 
"1","a","helloworld"
"2","b","helloworld"
"3","c","helloworld"
"4","d","helloworld"

发现第一列是数值型,如果不希望字段两边用引号引起,则语句改为:

MySQL [t2]> select * from test into outfile '/data/mysql/outfile2.txt' fields terminated by "," optionally  enclosed by '"';
Query OK, 4 rows affected (0.03 sec)

zj@bogon:/data/mysql$ more outfile2.txt 
1,"a","helloworld"
2,"b","helloworld"
3,"c","helloworld"
4,"d","helloworld"

测试转义字符,MySQL 导出数据中需要转义的字符主要包括以下 3 类:

  • 转义字符本身

  • 字段分隔符

  • 记录分隔符

MySQL [t2]> update test set content = '\"##!aa' where  id=1;
Query OK, 1 row affected (0.05 sec)
Rows matched: 1  Changed: 1  Warnings: 0

MySQL [t2]> select * from test into outfile '/data/mysql/outfile3.txt' fields terminated by "," optionally enclosed by '"';
Query OK, 4 rows affected (0.03 sec)

*******************************************
zj@bogon:/data/mysql$ more outfile3.txt 
1,"a","\"##!aa"
2,"b","helloworld"
3,"c","helloworld"
4,"d","helloworld"
  • 当导出命令中包含字段引用符时,数据中含有转义字符本身和字段引用符的字符需要被转义;

  • 当导出命令中不包含字段引用符时,数据中含有转义字符本身和字段分割符的字符需要被转义。

注意: select … into outfile … 产生的输出文件如果在目标目录下有重名文件,将不会被创建成功,源文件不会被自动覆盖。

使用 mysqldump 导出数据为文本的具体语法如下:

mysqldump -u username -T target_dir dbname tablename [option]

其中,option 参数可以是以下选项:

  • –fields-terminated-by=name (字段分隔符);

  • –fields-enclosed-by=name (字段引用符);

  • –fields-optionally-enclosed-by=name (字段引用符,只用在 char、varchar 和 test 等字符型字段上);

  • –fields-escaped-by=name (转义字符);

  • –lines-terminated-by=name (记录结束符);

例子:

root@bogon:/usr/local/mysql/bin# ./mysqldump -uroot -p -T /data/mysql/dump t2 test --fields-terminated-by ',' --fields-optionally-enclosed-by '"'

**************** test.txt **********************
zj@bogon:/data/mysql/dump$ more test.txt 
1,"a","\"##!aa"
2,"b","helloworld"
3,"c","helloworld"
4,"d","helloworld"

***************** test.sql *********************
zj@bogon:/data/mysql/dump$ more test.sql 
-- MySQL dump 10.13  Distrib 5.7.18, for Linux (x86_64)
--
-- Host: localhost    Database: t2
-- ------------------------------------------------------
-- Server version    5.7.18-log

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Table structure for table `test`
--

DROP TABLE IF EXISTS `test`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `test` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(10) DEFAULT NULL,
  `content` varchar(100) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;

/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2017-09-25 11:14:06

可以发现,除多了一个表的创建脚本文件,mysqldump 和 select … into outfile … 的选项和语法非常相似。其实 mysqldump 实际调用的就是后者提供的接口,并在其上面添加了一些新的功能而已。

2. 导入

(导入用 select … into outfile 或者 mysqldump 导出的纯数据文本)

和导出类似,导入也有两种不同的方法,分别是 load data infile… 和 mysqlimport,它们的本质是一样的,区别只是在于一个在 MySQL 内部执行,另一个在 MySQL 外部执行。

使用 “load data infile…” 命令,具体语法如下

mysql> load data [local]infile 'filename' into table tablename [option]  

option 可以是以下选项:

  • fields terminated by ‘string’ (字段分割符,默认为制表符’t’);

  • fields [optionally] enclosed by ‘char’ (字段引用符,如果加 optionally 选项则只用在 char varchar text 等字符型字段上。默认不使用引用符);

  • fields escaped by ‘char’ (转义字符,默认为”)

  • lines starting by ‘string’ (每行前都加此字符串,默认为”)

  • lines terminated by ‘string’ (行结束符,默认为’n’)

  • ignore number lines (忽略输入文件中的前几行数据)

  • (col_name_or_user_var,…) (按照列出的字段顺序和字段数量加载数据);

  • set col_name = expr,…将列做一定的数值转换后再加载。

fields 、lines 和前面 select…into outfile…的含义完全相同,不同的是多了几个不同的选项,下面的例子将文件’test.txt’中的数据加载到表 test 中:

// 清空表 test  
MySQL [t2]> truncate table test;
Query OK, 0 rows affected (0.07 sec)

MySQL [t2]> load data infile '/data/mysql/outfile.txt' into table test fields terminated by ',' enclosed by '"';
Query OK, 4 rows affected (0.10 sec)
Records: 4  Deleted: 0  Skipped: 0  Warnings: 0


MySQL [t2]> select * from test;
+------+------+------------+
| id   | name | content    |
+------+------+------------+
|    1 | a    | helloworld |
|    2 | b    | helloworld |
|    3 | c    | helloworld |
|    4 | d    | helloworld |
+------+------+------------+
4 rows in set (0.00 sec)

如果不希望加载文件中的前两行,可以进行如下操作:

MySQL [t2]> truncate table test;
Query OK, 0 rows affected (0.02 sec)

MySQL [t2]> load data infile '/data/mysql/outfile.txt' into table test fields terminated by ',' enclosed by '"' ignore 2 lines;
Query OK, 2 rows affected (0.00 sec)
Records: 2  Deleted: 0  Skipped: 0  Warnings: 0

MySQL [t2]> select * from test;
+------+------+------------+
| id   | name | content    |
+------+------+------------+
|    3 | c    | helloworld |
|    4 | d    | helloworld |
+------+------+------------+
2 rows in set (0.02 sec)

使用 mysqldump 实现

语法:

shell> mysqlimport -uroot -p [--local] dbname order_tab.txt [option]  

其中,option 参数可以是以下选项:

  • –fields-terminated-by=name (字段分隔符)

  • –fields-enclosed-by=name (字段引用符)

  • –fields-optionally-enclosed-by=name (字段引用符,只用在 char、varchar、text等字符型字段上)

  • –fields-escaped-by=name (转义字符)

  • –lines-terminated-by=name (记录结束符)

  • –ignore-lines=number (忽略前几行)

注意:

如果导入和导出时跨平台操作的(windows 和 linux),那么要注意设置参数 line-terminated-by,windows 上设置为 line-terminated-by=’rn’, linux 上设置为 line-terminated-by=’n’。

MySQL登录与退出

mysql连接登录参数

  • -?, –help
Display this help and exit.
  • -I, –help
Synonym for -?

以上为mysql参数帮助命令

以下为常用的参数命令

  • –auto-rehash
Enable automatic rehashing. One doesn't need to use
'rehash' to get table and field completion, but startup
and reconnecting may take a longer time. Disable with
--disable-auto-rehash.
(Defaults to on; use --skip-auto-rehash to disable.)
  • -A, –no-auto-rehash
No automatic rehashing. One has to use 'rehash' to get
table and field completion. This gives a quicker start of
mysql and disables rehashing on reconnect.    
  • -D, –database=name Database to use.
  • –delimiter=name Delimiter to be used.
  • -h, –host=name Connect to host.
  • -p, –password[=name]
Password to use when connecting to server. If password is
not given it's asked from the tty.
  • -P, –port=#
Port number to use for connection or 0 for default to, in
order of preference, my.cnf, $MYSQL_TCP_PORT,
/etc/services, built-in default (3306).
  • –prompt=name
Set the mysql prompt to this value.
  • -u, –user=name
User for login if not current user.
  • -V, –version
Output version information and exit.

不带口令的登录

C:UsersoneTOinf                                                            
λ  mysql -u root -p --auto-rehash                                            
Enter password: ****                                                          
Welcome to the MySQL monitor.  Commands end with ; or g.                    
Your MySQL connection id is 2                                                
Server version: 5.5.53 MySQL Community Server (GPL)                          

Copyright (c) 2000, 2016, 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.

登录时修改提示符

C:UsersoneTOinf                                                            
λ  mysql -u root -p --prompt u@h>                                          
Enter password: ****                                                          
Welcome to the MySQL monitor.  Commands end with ; or g.                    
Your MySQL connection id is 3                                                
Server version: 5.5.53 MySQL Community Server (GPL)                          

Copyright (c) 2000, 2016, 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.

root@localhost>                                                              

登录后修改提示符

mysql> prompt u@h>
PROMPT set to 'u@h>'
root@localhost>

有用的提示符

未分类

退出命令

  • exit
  • quit
  • q

微服务MySQL分库分表数据到MongoDB同步方案

需求背景

近年来,微服务概念持续火热,网络上针对微服务和单体架构的讨论也是越来越多,面对日益增长的业务需求是,很多公司做技术架构升级时优先选用微服务方式。我所在公司也是选的这个方向来升级技术架构,以支撑更大访问量和更方便的业务扩展。

发现问题

微服务拆分主要分两种方式:拆分业务系统不拆分数据库,拆分业务系统拆分库。如果数据规模小的话大可不必拆分数据库,因为拆分数据看必将面对多维度数据查询,跨进程之间的事务等问题。而我所在公司随着业务发展单数据库实例已经不能满足业务需要,所以选择了拆分业务系统同时拆分数据库的模式,所以也面临着以上的问题。本文主要介绍多维度数据实时查询解决方案。当前系统架构和存储结构如下:

未分类

解决思路

  • 要对多数据库数据进行查询,首先就需要将数据库同步到一起以方便查询
  • 为了满足大数据量数据需求,所以优先选择NOSQL数据库做同步库
  • NOSQL数据库基本无法进行关联查询,所以需要将关系数据进行拼接操作,转换成非关系型数据
  • 业务多维度查询需要实时性,所以需要选择NOSQL中实时性相对比较好的数据库:MongoDB

根据以上思路,总结数据整合架构如下图所示:

未分类

解决方案

目前网上一些数据同步案例分两种:MQ消息同步和binlog数据读取同步

先说MQ消息同步,该同步方式我所在公司试用过一段时间,发现以下问题:

  • 数据围绕业务进行,对业务关键性数据操作发送MQ消息,对业务系统依赖性比较高
  • 对于数据库中存量数据需要单独处理
  • 对于工具表还需要单独维护同步
  • 每次新增数据表都需要重新添加MQ逻辑

考虑到以上问题,用MQ方式同步数据并不是最优解决办法

使用binlog 数据读取方式目前有一些成熟方案,比如tungsten replicator,但这些同步工具只能实现数据1:1复制,数据复制过程自定义逻辑添加比较麻烦,不支持分库分表数据归集操作。综上所述,最优方案应该是读取后binlog后自行处理后续数据逻辑。目前binlog读取binlog工具中最成熟的方案应该就是alibaba开源的canal了。

canal

canal是阿里巴巴mysql数据库binlog的增量订阅&消费组件 。阿里云DRDS、阿里巴巴TDDL 二级索引、小表复制. 都是基于canal做的,应用广泛。

canal原理相对比较简单:

  • canal模拟mysql slave的交互协议,伪装自己为mysql slave,向mysql master发送dump协议
  • mysql master收到dump请求,开始推送binary log给slave(也就是canal)
  • canal解析binary log对象(原始为byte流)

canal介绍: https://github.com/alibaba/canal/wiki

我使用的是canal的HA模式,由zookeeper选举可用实例,每个数据库一个instance,服务端配置如下:

目录:

conf
    database1
        -instance.properties
    database2
        -instance.properties
    canal.properties

instance.properties

canal.instance.mysql.slaveId = 1001
canal.instance.master.address = X.X.X.X:3306
canal.instance.master.journal.name = 
canal.instance.master.position = 
canal.instance.master.timestamp = 
canal.instance.dbUsername = canal
canal.instance.dbPassword = canal
canal.instance.defaultDatabaseName =
canal.instance.connectionCharset = UTF-8
canal.instance.filter.regex = .*\..*
canal.instance.filter.black.regex =  

canal.properties

canal.id= 1
canal.ip=X.X.X.X
canal.port= 11111
canal.zkServers=X.X.X.X:2181,X.X.X.X:2181,X.X.X.X:2181
canal.zookeeper.flush.period = 1000
canal.file.data.dir = ${canal.conf.dir}
canal.file.flush.period = 1000
canal.instance.memory.buffer.size = 16384
canal.instance.memory.buffer.memunit = 1024 
canal.instance.memory.batch.mode = MEMSIZE
canal.instance.detecting.enable = true
canal.instance.detecting.sql = select 1
canal.instance.detecting.interval.time = 3
canal.instance.detecting.retry.threshold = 3
canal.instance.detecting.heartbeatHaEnable = false
canal.instance.transaction.size =  1024
canal.instance.fallbackIntervalInSeconds = 60
canal.instance.network.receiveBufferSize = 16384
canal.instance.network.sendBufferSize = 16384
canal.instance.network.soTimeout = 30
canal.instance.filter.query.dcl = true
canal.instance.filter.query.dml = false
canal.instance.filter.query.ddl = false
canal.instance.filter.table.error = false
canal.instance.filter.rows = false
canal.instance.binlog.format = ROW,STATEMENT,MIXED 
canal.instance.binlog.image = FULL,MINIMAL,NOBLOB
canal.instance.get.ddl.isolation = false
canal.destinations= example,p4-test
canal.conf.dir = ../conf
canal.auto.scan = true
canal.auto.scan.interval = 5
canal.instance.global.mode = spring 
canal.instance.global.lazy = false
canal.instance.global.spring.xml = classpath:spring/default-instance.xml

部署数据流如下:

未分类

tip:

虽然canal同时支持mixed和row类型的binlog日志,但是获取行数据时如果是mixed类型的日志则获取不到表名,所以本方案暂只支持row格式的binlog

数据同步

创建canal client应用订阅canal读取的binlog数据

1、开启多instance 订阅,订阅多个instance

public void initCanalStart() {
    List<String> destinations = canalProperties.getDestination();
    final List<CanalClient> canalClientList = new ArrayList<>();
    if (destinations != null && destinations.size() > 0) {
        for (String destination : destinations) {
            // 基于zookeeper动态获取canal server的地址,建立链接,其中一台server发生crash,可以支持failover
            CanalConnector connector = CanalConnectors.newClusterConnector(canalProperties.getZkServers(), destination, "", "");
            CanalClient client = new CanalClient(destination, connector);
            canalClientList.add(client);
            client.start();
        }
    }
    Runtime.getRuntime().addShutdownHook(new Thread() {
        public void run() {
            try {
                logger.info("## stop the canal client");
                for (CanalClient canalClient : canalClientList) {
                    canalClient.stop();
                }
            } catch (Throwable e) {
                logger.warn("##something goes wrong when stopping canal:", e);
            } finally {
                logger.info("## canal client is down.");
            }
        }
    });
}

订阅消息处理

private void process() {
    int batchSize = 5 * 1024;
    while (running) {
        try {
            MDC.put("destination", destination);
            connector.connect();
            connector.subscribe();
            while (running) {
                Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据
                long batchId = message.getId();
                int size = message.getEntries().size();
                if (batchId != -1 && size > 0) {
                    saveEntry(message.getEntries());
                }
                connector.ack(batchId); // 提交确认
                // connector.rollback(batchId); // 处理失败, 回滚数据
            }
        } catch (Exception e) {
            logger.error("process error!", e);
        } finally {
            connector.disconnect();
            MDC.remove("destination");
        }
    }
}

根据数据库事件处理消息,过滤消息列表,对数据变动进行处理,用到信息为:

  • insert :schemaName,tableName,beforeColumnsList
  • update :schemaName,tableName,afterColumnsList
  • delete :schemaName,tableName,afterColumnsList
RowChange rowChage = null;
    try {
        rowChage = RowChange.parseFrom(entry.getStoreValue());
    } catch (Exception e) {
        throw new RuntimeException("parse event has an error , data:" + entry.toString(), e);
    }
    EventType eventType = rowChage.getEventType();
    logger.info(row_format,
            entry.getHeader().getLogfileName(),
            String.valueOf(entry.getHeader().getLogfileOffset()), entry.getHeader().getSchemaName(),
            entry.getHeader().getTableName(), eventType,
            String.valueOf(entry.getHeader().getExecuteTime()), String.valueOf(delayTime));
    if (eventType == EventType.QUERY || rowChage.getIsDdl()) {
        logger.info(" sql ----> " + rowChage.getSql());
        continue;
    }
    DataService dataService = SpringUtil.getBean(DataService.class);
    for (RowData rowData : rowChage.getRowDatasList()) {
        if (eventType == EventType.DELETE) {
            dataService.delete(rowData.getBeforeColumnsList(), entry.getHeader().getSchemaName(), entry.getHeader().getTableName());
        } else if (eventType == EventType.INSERT) {
            dataService.insert(rowData.getAfterColumnsList(), entry.getHeader().getSchemaName(), entry.getHeader().getTableName());
        } else if (eventType == EventType.UPDATE) {
            dataService.update(rowData.getAfterColumnsList(), entry.getHeader().getSchemaName(), entry.getHeader().getTableName());
        } else {
            logger.info("未知数据变动类型:{}", eventType);
        }
    }
}

ColumnsList转换成MongoTemplate 可用的数据类:DBObject,顺便做下数据类型转换

public static DBObject columnToJson(List<CanalEntry.Column> columns) {
    DBObject obj = new BasicDBObject();
    try {
        for (CanalEntry.Column column : columns) {
            String mysqlType = column.getMysqlType();
            //int类型,长度11以下为Integer,以上为long
            if (mysqlType.startsWith("int")) {
                int lenBegin = mysqlType.indexOf('(');
                int lenEnd = mysqlType.indexOf(')');
                if (lenBegin > 0 && lenEnd > 0) {
                    int length = Integer.parseInt(mysqlType.substring(lenBegin + 1, lenEnd));
                    if (length > 10) {
                        obj.put(column.getName(), StringUtils.isBlank(column.getValue()) ? null : Long.parseLong(column.getValue()));
                        continue;
                    }
                }
                obj.put(column.getName(), StringUtils.isBlank(column.getValue()) ? null : Integer.parseInt(column.getValue()));
            } else if (mysqlType.startsWith("bigint")) {
                obj.put(column.getName(), StringUtils.isBlank(column.getValue()) ? null : Long.parseLong(column.getValue()));
            } else if (mysqlType.startsWith("decimal")) {
                int lenBegin = mysqlType.indexOf('(');
                int lenCenter = mysqlType.indexOf(',');
                int lenEnd = mysqlType.indexOf(')');
                if (lenBegin > 0 && lenEnd > 0 && lenCenter > 0) {
                    int length = Integer.parseInt(mysqlType.substring(lenCenter + 1, lenEnd));
                    if (length == 0) {
                        obj.put(column.getName(), StringUtils.isBlank(column.getValue()) ? null : Long.parseLong(column.getValue()));
                        continue;
                    }
                }
                obj.put(column.getName(), StringUtils.isBlank(column.getValue()) ? null : Double.parseDouble(column.getValue()));
            } else if (mysqlType.equals("datetime") || mysqlType.equals("timestamp")) {
                obj.put(column.getName(), StringUtils.isBlank(column.getValue()) ? null : DATE_TIME_FORMAT.parse(column.getValue()));
            } else if (mysqlType.equals("date")) {
                obj.put(column.getName(), StringUtils.isBlank(column.getValue()) ? null : DATE_FORMAT.parse(column.getValue()));
            } else if (mysqlType.equals("time")) {
                obj.put(column.getName(), StringUtils.isBlank(column.getValue()) ? null : TIME_FORMAT.parse(column.getValue()));
            } else {
                obj.put(column.getName(), column.getValue());
            }
        }
    } catch (ParseException e) {
        e.printStackTrace();
    }
    return obj;
}

tip:

DBObject对象如果同时用于保存原始数据和组合数据或其他数据,使用时应该深度拷贝对象生成副本,然后使用副本

数据拼接

我们获取了数据库数据后做拼接操作,比如两张用户表:

user_info:{id,user_no,user_name,user_password}
user_other_info:{id,user_no,idcard,realname}

拼接后mongo数据为:

user:{_id,user_no,userInfo:{id,user_no,user_name,user_password},userOtherInfo:{id,user_no,idcard,realname})

接收到的数据信息很多,如何才能简单的触发数据拼接操作呢?

先看我们能获取的信息:schemaName,tableName,DBObject,Event(insert,update,delete)

将这些信息标识拼接起来看看:/schemaName/tableName/Event(DBObject),没错,就是一个标准的restful链接。只要我们实现一个简单的springMVC 就能自动获取需要的数据信息进行拼接操作。

先实现@Controller,定义名称为Schema,value对应schemaName

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public  @interface Schema {
 String value() default "";
}

然后实现@RequestMapping,定义名称为Table,直接使用Canal中的EventType 对应RequestMethod

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public  @interface Table {
    String value() default "";
    CanalEntry.EventType[] event() default {};
}

然后创建springUtil,实现接口ApplicationContextAware,应用启动 加载的时候初始化两个Map:intanceMap,handlerMap

private static ApplicationContext applicationContext = null;
//库名和数据处理Bean映射Map
private static Map<String, Object> instanceMap = new HashMap<String, Object>();
//路劲和数据处理Method映射Map
private static Map<String, Method> handlerMap = new HashMap<String, Method>();
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
    if (SpringUtil.applicationContext == null) {
        SpringUtil.applicationContext = applicationContext;
        //初始化instanceMap数据
        instanceMap();
        //初始化handlerMap数据
        handlerMap();
    }
}
private void instanceMap() {
    Map<String, Object> beans = applicationContext.getBeansWithAnnotation(Schema.class);
    for (Object bean : beans.values()) {
        Class<?> clazz = bean.getClass();
        Object instance = applicationContext.getBean(clazz);
        Schema schema = clazz.getAnnotation(Schema.class);
        String key = schema.value();
        instanceMap.put(key, instance);
        logger.info("instanceMap [{}:{}]", key, bean == null ? "null" : clazz.getName());
    }
}
private void handlerMap() {
    if (instanceMap.size() <= 0)
        return;
    for (Map.Entry<String, Object> entry : instanceMap.entrySet()) {
        if (entry.getValue().getClass().isAnnotationPresent(Schema.class)) {
            Schema schema = entry.getValue().getClass().getAnnotation(Schema.class);
            String schemeName = schema.value();
            Method[] methods = entry.getValue().getClass().getMethods();
            for (Method method : methods) {
                if (method.isAnnotationPresent(Table.class)) {
                    Table table = method.getAnnotation(Table.class);
                    String tName = table.value();
                    CanalEntry.EventType[] events = table.event();
                    //未标明数据事件类型的方法不做映射
                    if (events.length < 1) {
                        continue;
                    }
                    //同一个方法可以映射多张表
                    for (int i = 0; i < events.length; i++) {
                        String path = "/" + schemeName + "/" + tName + "/" + events[i].getNumber();
                        handlerMap.put(path, method);
                        logger.info("handlerMap [{}:{}]", path, method.getName());
                    }
                } else {
                    continue;
                }
            }
        } else {
            continue;
        }
    }
}

调用方法:

public static void doEvent(String path, DBObject obj) throws Exception {
    String[] pathArray = path.split("/");
    if (pathArray.length != 4) {
        logger.info("path 格式不正确:{}", path);
        return;
    }
    Method method = handlerMap.get(path);
    Object schema = instanceMap.get(pathArray[1]);
    //查找不到映射Bean和Method不做处理
    if (method == null || schema == null) {
        return;
    }
    try {
        long begin = System.currentTimeMillis();
        logger.info("integrate data:{},{}", path, obj);
        method.invoke(schema, new Object[]{obj});
        logger.info("integrate data consume: {}ms:", System.currentTimeMillis() - begin);
    } catch (Exception e) {
        logger.error("调用组合逻辑异常", e);
        throw new Exception(e.getCause());
    }
}

数据拼接消息处理:

@Schema("demo_user")
public class UserService {
    @Table(value = "user_info", event = {CanalEntry.EventType.INSERT, CanalEntry.EventType.UPDATE})
    public void saveUser_UserInfo(DBObject userInfo) {
        String userNo = userInfo.get("user_no") == null ? null : userInfo.get("user_no").toString();
        DBCollection collection = completeMongoTemplate.getCollection("user");
        DBObject queryObject = new BasicDBObject("user_no", userNo);
        DBObject user = collection.findOne(queryObject);
        if (user == null) {
            user = new BasicDBObject();
            user.put("user_no", userNo);
            user.put("userInfo", userInfo);
            collection.insert(user);
        } else {
            DBObject updateObj = new BasicDBObject("userInfo", userInfo);
            DBObject update = new BasicDBObject("$set", updateObj);
            collection.update(queryObject, update);
        }
    }
}

示例源码

https://github.com/zhangtr/canal-mongo

欢迎讨论方案或者指正代码

linux下安装mariadb(mysql)并创建账户

安装mariadb(mysql)并创建账户

(red hat内置的安装已经是mariadb,而非mysql,因为二者差不多,所以不建议重新下载mysql,建议直接用mariadb)

安装:

[root@localhost ~]# dnf -y install mariadb*

启动:

[root@localhost ~]# systemctl start mariadb

[root@localhost ~]# systemctl status mariadb

[root@localhost ~]# firewall-cmd --permanent --add-service=mysql

[root@localhost ~]# firewall-cmd --reload

[root@localhost ~]# firewall-cmd --permanent --add-service=mariadb

Error: INVALID_SERVICE: mariadb

[root@localhost ~]# mysql_secure_installation

进入root下的mariadb(即mysql)

[root@localhost ~]# mysql -u root -p

Enter password: (这里要输入密码)

Welcome to the MariaDB monitor.  Commands end with ; or g.

Your MariaDB connection id is 13

Server version: 10.1.26-MariaDB MariaDB Server



Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.



Type 'help;' or 'h' for help. Type 'c' to clear the current input

输入密码后进入

MariaDB [(none)]>





#------

建账户:

MariaDB [(none)]> create database menmydb;

Query OK, 1 row affected (0.02 sec)

创建密码:

MariaDB [(none)]> create user menxiaolei@localhost identified by 'men12xiao34lei56';

Query OK, 0 rows affected (0.02 sec)

给权限:

MariaDB [(none)]> grant all on menmydb.* to menxiaolei@localhost identified by 'men12xiao34lei56';

Query OK, 0 rows affected (0.02 sec)

退出

MariaDB [(none)]>



Ctrl+D

#——————————————————–

普通用户使用menmydb

[xjj@localhost ~]$ mysql -u menxiaolei -p

Enter password: (这里输入密码)

Welcome to the MariaDB monitor.  Commands end with ; or g.

Your MariaDB connection id is 17

Server version: 10.1.26-MariaDB MariaDB Server



Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.



Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.
MariaDB [(none)]> show databases;

+--------------------+

| Database           |

+--------------------+

| information_schema |

| menmydb            |

+--------------------+

2 rows in set (0.00 sec)
MariaDB [(none)]> show tables;



MariaDB [(none)]> describe servers;



Ctrl+D

docker安装mysql5.6

未分类

下载镜像

cjinle@debian:~$ sudo docker pull mysql:5.6
启动mysql
cjinle@debian:~$ sudo docker run --name mysql5.6 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.6
80b766cca7ce0af73787a626153a92286cc7ab8c125e42c1c75e3e680ecefcf5

查看进程

cjinle@debian:~$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
80b766cca7ce        mysql:5.6           "docker-entrypoint..."   8 minutes ago       Up 8 minutes        0.0.0.0:3306->3306/tcp   mysql5.6
d4d5aa2e10f7        redis               "docker-entrypoint..."   3 hours ago         Up 3 hours          0.0.0.0:6379->6379/tcp   musing_wescoff

连接测试

用有mysql客户端的登录测试

[root@dev ~]# mysql -uroot -p123456 -h192.168.56.101
Warning: Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or g.
Your MySQL connection id is 20
Server version: 5.6.38 MySQL Community Server (GPL)

Copyright (c) 2000, 2017, 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>

使用 Docker 完成 MySQL 数据库主从配置

使用 docker 进行数据库主从配置,因为我有这个需求,而在网上搜索后发现没有满足我需求的相关实践文档,有的是一些零零碎碎的文档,而且在参照这些文档进行部署的时候我还踩了许多坑。

因此根据我自己部署成功的经验,我写了这个文档。

使用 docker 自然就需要有 docker 环境,当然 docker 的镜像在国内访问比较慢,建议使用国内的源。

构建 DockerFile

我们的工作是在 Mysql 镜像基础上进行的。

docker pull mysql:5.7.20 这条命令会下载最新的 mysql 镜像,当然也可以指定版本。

新建一个 DockerFile 文件:

FROM mysql:5.7.20

EXPOSE 3306

COPY my.cnf /etc/mysql/

CMD ["mysqld"]

在构建镜像前,我们需要 mysql 的配置文件 my.cnf ,可以先启动一个 mysql 的容器,然后进入容器复制它下来,并对其进行修改:

!includedir /etc/mysql/conf.d/
[mysqld]
pid-file=/var/run/mysqld/mysqld.pid
socket=/var/run/mysqld/mysqld.sock
datadir=/var/lib/mysql
#log-error=/var/log/mysql/error.log
# By default we only accept connections from localhost
#bind-address   = 127.0.0.1
log-bin=/var/log/mysql/mysql-bin.index
server-id=1
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0

以上是我修改好的一个 my.conf 文件,我们主要修改的是以下这些选项:

#bind-address   = 127.0.0.1 # 注释这个选项可以让远程机器访问,或者可以修改为 0.0.0.0
log-bin=/var/log/mysql/mysql-bin.index # 开启 log-bin 日志,日志路径
server-id=1 # 服务器唯一ID,默认是1,一般取IP最后一段,这里看情况分配

这是我的主库的 my.cnf,从库的基本一样,只是 servier-id 不一样,从库为 2,将这个文件也放在文件夹里。

构建需要的文件结构就是以下这样:

master
├── Dockerfile
└── my.cnf
slave
├── Dockerfile
└── my.cnf

然后在master目录中使用 docker build -t master/mysql:5.7.20 . 命令构建镜像主库,从库构建命令 docker build -t slave/mysql:5.7.20 .。命令最后有个.,不要忘记,代表当前目录。-t 的意思是tag,是 –tag 的缩写,也就是命名这个镜像,如果不加docker会随机给这个镜像一个名字,建议格式为name:tag

构建完成后我们使用以下命令启动主库和从库:

docker run -p 3306 --name mysql-master -e MYSQL_ROOT_PASSWORD=root -d master/mysql:5.7.20

docker run -p 3306 --name mysql-slave --link mysql-master:master -e MYSQL_ROOT_PASSWORD=root -d slave/mysql:5.7.20

然后分别执行docker exec -it mysql-master bash和docker exec -it slave-master bash命令进入到容器内部。执行mysql -uroot -proot进入mysql环境,这时我们的环境搭建就已经完成了,下面正式配置主从连接。

当然此时docker会分配一个唯一的端口给容器,我们也可以用mysql客户端连接进行配置。使用docker ps查看端口号,就可以用客户端连接进行管理。

☁  mysql-master-slave  docker ps
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                     NAMES
6c7648e829e0        slave/mysql:5.7.20    "docker-entrypoint..."   4 minutes ago       Up 4 minutes        0.0.0.0:32769->3306/tcp   mysql-slave
483842c63235        master/mysql:5.7.20   "docker-entrypoint..."   5 minutes ago       Up 5 minutes        0.0.0.0:32768->3306/tcp   mysql-master

使用GRANT REPLICATION SLAVE ON . to ‘user’@’%’ identified by ‘mysql’; 或者 GRANT REPLICATION SLAVE ON . TO ‘user’@’192.168.1.200’ IDENTIFIED BY ‘mysql’;创建一个用户,上一个是所有ip都可以访问,下一个是指定ip才可以访问。

然后使用GRANT SELECT,REPLICATION SLAVE ON . TO ‘user’@’%’;给这个用户读取权限。

使用show master status查看mysql主容器的状态,打印如下信息:

+------------------+----------+--------------+------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000003 |      154 |              |                  |                   |
+------------------+----------+--------------+------------------+-------------------+

使用hostname查看主容器的hostname,如我的主容器是483842c63235。

接下来配置从库mysql:

change master to
master_host='master',#要连接的主服务器的ip
master_user='user',#指定的用户名,最好不要用root
master_log_file='mysql-bin.000003',#主库记录的值
master_log_pos=154,#主库的pos值
master_port=3306,#主库3306映射的端口
master_password='mysql';#主库要连接的用户的密码了

使用start slave启动主从同步

使用命令查看show slave statusG

打印如下信息:

*************************** 1. row ***************************
               Slave_IO_State: Connecting to master
                  Master_Host: master //主服务器地址
                  Master_User: user //授权帐户名,尽量避免使用root
                  Master_Port: 3306 //数据库端口,部分版本没有此行
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000003 //同步主库的日志文件名
          Read_Master_Log_Pos: 154 //同步读取二进制日志的位置,大于等于
               Relay_Log_File: 6c7648e829e0-relay-bin.000001
                Relay_Log_Pos: 4
        Relay_Master_Log_File: mysql-bin.000003
             Slave_IO_Running: Yes //此状态必须YES
            Slave_SQL_Running: Yes //此状态必须YES
              Replicate_Do_DB:
          Replicate_Ignore_DB:
           Replicate_Do_Table:
       Replicate_Ignore_Table:
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
                   Last_Errno: 0
                   Last_Error:
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 154
              Relay_Log_Space: 154
              Until_Condition: None
               Until_Log_File:
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File:
           Master_SSL_CA_Path:
              Master_SSL_Cert:
            Master_SSL_Cipher:
               Master_SSL_Key:
        Seconds_Behind_Master: NULL
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error:
               Last_SQL_Errno: 0
               Last_SQL_Error:
  Replicate_Ignore_Server_Ids:
             Master_Server_Id: 0
                  Master_UUID:
             Master_Info_File: /var/lib/mysql/master.info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind:
      Last_IO_Error_Timestamp:
     Last_SQL_Error_Timestamp:
               Master_SSL_Crl:
           Master_SSL_Crlpath:
           Retrieved_Gtid_Set:
            Executed_Gtid_Set:
                Auto_Position: 0
         Replicate_Rewrite_DB:
                 Channel_Name:
           Master_TLS_Version:
1 row in set (0.00 sec)

这样就是完成了一组主从mysql的配置。

shell脚本

一下一个相应的shell脚本,可以在1分钟能启动一组mysql主从容器,运行这个脚本需要稍作修改,主要是mysql配置文件。

#!/bin/bash

MASTER_DIR=/var/lib/mysql/mysql-master
SLAVE_DIR=/var/lib/mysql/mysql-slave

## First we could rm the existed container
docker rm -f mysql-master
docker rm -f mysql-slave

## Rm the existed directory
rm -rf $MASTER_DIR
rm -rf $SLAVE_DIR

## Start instance
docker run -p 3306 --name mysql-master  -v /etc/master.cnf:/etc/mysql/my.cnf -v $MASTER_DIR:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root -d master/mysql:5.7.20
docker run -p 3306 --name mysql-slave  -v /etc/slave.cnf:/etc/mysql/my.cnf -v $MASTER_DIR:/var/lib/mysql --link mysql-master:master -e MYSQL_ROOT_PASSWORD=root -d slave/mysql:5.7.20

## Creating a User for Replication
docker stop mysql-master mysql-slave
docker start mysql-master mysql-slave

sleep 3

docker exec -it mysql-master mysql -S /var/lib/mysql/mysql.sock -e "CREATE USER 'users'@'127.0.0.1' IDENTIFIED BY 'mysql';GRANT REPLICATION SLAVE ON *.* TO 'users'@'127.0.0.1';"

## Obtaining the Replication Master Binary Log Coordinates
master_status=`docker exec -it master mysql -S /var/lib/mysql/mysql.sock -e "show master statusG"`
master_log_file=`echo "$master_status" | awk  'NR==2{print substr($2,1,length($2)-1)}'`
master_log_pos=`echo "$master_status" | awk 'NR==3{print $2}'`
master_log_file="'""$master_log_file""'"

## Setting Up Replication Slaves 
docker exec -it mysql-slave mysql -S /var/lib/mysql/mysql.sock -e "CHANGE MASTER TO MASTER_HOST='master',MASTER_PORT=3306,MASTER_USER='users',MASTER_PASSWORD='mysql',MASTER_LOG_FILE=$master_log_file,MASTER_LOG_POS=$master_log_pos;"
docker exec -it mysql-slave mysql -S /var/lib/mysql/mysql.sock -e "start slave;"
docker exec -it mysql-slave mysql -S /var/lib/mysql/mysql.sock -e "show slave statusG"

## Creates shortcuts
grep "alias mysql-master" /etc/profile
if [ $? -eq 1 ];then
    echo 'alias mysql="docker exec -it mysql-master mysql"' >> /etc/profile
    echo 'alias master="docker exec -it mysql-master mysql -h 127.0.0.1 -P3306"' >> /etc/profile
    echo 'alias slave="docker exec -it mysql-master mysql -h 127.0.0.1 -P3307"' >> /etc/profile
    source /etc/profile
fi

虚拟机Ubuntu17.04环境下搭建PHP7.0+ Apache+MySQL+PhpMyAdmin 攻略

打开“终端窗口”进行软件源的更新,不更新软件源直接安装PHP,会出现有些软件包下载失败,终端更新命令“sudo apt-get update ” –回车– “输入管理员密码”进行更新。

未分类

(1)软件更新完后,就可以安装PHP、Apache、MySQL了,一般安装顺序是先安装MySQL在安装Apache最后安装PHP,Pache和MySQL安装顺序可以颠倒,因为二者依赖性并不是很强,但PHP要安装在这两个后面,因为是要依赖Apache和MySQL的服务的。

(2)MysSQL安装:

终端命令输入:“ sudo apt-get install mysql-server php-mysql ” — “ 输入管理员密码 ” — “ 回车 ” 开始安装。

未分类

安装中间需要设定MySQL的root管理员密码,密码需要输入两遍。

未分类

(3)Apache2 安装:

终端命令:“ sudo apt-get install apache2 ” — “ 输入管理员密码 ” — “ 回车 ” 开始安装。

未分类

安装完成后可以在浏览器中输入“ 127.0.0.1 ” 或者 “ localhost ” ,就能查看Apache2是否安装成功。

未分类

(4) PhpMyAdminde 安装:

终端命令:“ sudo apt-get install phpmyadmin ” — “ 输入管理员密码 ” — “ 确定 ” 。

未分类

安装 PhpMyAdminde 时需要选择Apache2 和MySQL的root密码,这里的密码也需要输入两遍。

未分类

未分类

(5)PHP 和php相关插件包安装:

终端命令:“ sudo apt-get install php libapache2-mod-php php-mcrypt php-curl php-imagick php-cli ” — “输入管理员密码” — “确定”开始安装

未分类

可以用这个命令新建脚本文件:

sudo vi /var/www/html/info.php

打开info.php文件,输入以下内容。

<?php
phpinfo();
?>

未分类

在浏览器访问该文件本地就用“ 127.0.0.1/info.php ” 或者“ http://你的服务器的IP地址/info.php ” 查看,要是看到下面信息就说明php安装成功。

测试成功后,最好删掉这个info.php文件,因为这个文件会让其他人看到你服务器的一些配置,

输入下边的命令:sudo rm /var/www/html/info.php

未分类

(6)总结:现在已经安装完LAMP了,快去开发属于自己的网站吧。

CentOS源码安装Apache、MySQL、PHP

一、下载Apache

1. 点击官网链接进入官网,按照下图下载源码包。

有很多下载方式,可以选择http也可以选择ftp,上面的gif我是用的是http方式,随便选择了一个镜像进入站点,然后选择了http目录,进入http目录后,选择了httpd-2.4.29.tar.gz文件,右键复制了该文件的链接地址。

在命令行输入下面的命令下载源码文件:

# wget http://apache.claz.org/httpd/httpd-2.4.29.tar.gz

2.下载后输入下面的命令解压:

# tar -zxvf httpd-2.4.29.tar.gz

二、下载依赖

1. 下载apr

点击链接官网地址下载,如下图

未分类

我选择的是apr-1.6.3.tar.gz的链接,依然是右键,然后选择复制链接地址,输入如下命令下载:

# wget http://apache.mirrors.lucidnetworks.net//apr/apr-1.6.3.tar.gz

输入如下命令解压:

# tar -zxvf apr-1.6.3.tar.gz

进入解压目录

# cd apr-1.6.3

输入如下命令安装

# ./configure --prefix=/wwwAdmin/apr-1.6.3
# make
# make install

--prefix=/wwwAdmin/apr-1.6.3是指定安装目录.

2. 下载apr-util

点击链接官网地址下载,如下图

未分类

我依然选择的是第一个链接,然后复制其地址,输入如下命令下载:

# wget http://mirror.stjschools.org/public/apache//apr/apr-util-1.6.1.tar.gz

然后输入如下命令解压:

# tar -zxvf apr-util-1.6.1.tar.gz

进入解压目录

# cd apr-util-1.6.1

输入如下命令安装

# ./configure --prefix=/wwwAdmin/apr-util-1.6.1 -with-apr=/wwwAdmin/apr-1.6.3/
# make
# make install

--prefix=/wwwAdmin/apr-util-1.6.1是指定安装目录,-with-apr=/wwwAdmin/apr-1.6.3/是apr的安装目录

3. 下载pcre

点击链接官网地址下载,选择一个版本(我选择的pcre2-10.30.tar.gz),然后右键复制其链接地址,输入如下命令下载:

# wget https://ftp.pcre.org/pub/pcre/pcre2-10.30.tar.gz

输入下面的命令解压:

# tar -zxvf pcre2-10.30.tar.gz

进入解压后的目录:

# cd pcre2-10.30

输入如下命令安装

# ./configure
# make
# make install</span>

使用默认安装位置/usr/local/pcre。

4. 下载OpenSSL

点击链接官网地址下载,我选择的是openssl-1.1.0g.tar.gz,然后右键复制其链接地址,输入如下命令下载:

# wget https://www.openssl.org/source/openssl-1.1.0g.tar.gz

注:get下载似乎是提示404,所以我直接点击下载到我的电脑上,然后用ftp工具上传到服务器目录下的。

输入下面的命令解压:

# tar -zxvf openssl-1.1.0g.tar.gz

进入解压后的目录:

# cd openssl-1.1.0g

输入如下命令安装

# ./config shared --prefix=/wwwAdmin/openssl1.1.0g --openssldir=/usr/local/ssl<span class="redactor-invisible-space">
# make
# make install
--prefix=/wwwAdmin/pcre2-10.30是指定安装目录

三、安装Apache

进入解压后的目录

# cd httpd-2.4.29

执行下面的命令安装

# ./configure --prefix=/wwwAdmin/apache2.4.29 --sysconfdir=/wwwAdmin/apache2.4.29/conf --enable-so --enable-ssl=/wwwAdmin/openssl1.1.0g --enable-cgi --enable-rewrite --with-zlib --with-pcre=/usr/local/pcre --with-apr=/wwwAdmin/apr-1.6.3 --with-apr-util=/wwwAdmin/apr-util-1.6.1 --enable-modules=most --enable-mpms-shared=all --with-mpm=event
# make
# make install

安装完成后进入安装目录/wwwAdmin/apache2.4.29下的conf目录,使用vi httpd.conf命令打开文件,找到

#ServerName www.example.com:80

修改为(去掉前面的#号,#号是注释)

ServerName 你的服务器ip:80

这样就可以启动服务器了,启动命令往下读就会看到。(修改ServerName)

注意:安装的时候如果报某个库没有安装是正常的,因为系统中可能默认安装了某些库,有的可能没有默认安装,如果用到了没有安装的库自然会报错。如果遇到困难可以留言给我。

四、安装MySQL

1. 下载MySQL

点击进入Community版下载地址,如下图所示

未分类

根据自己的需要选择版本和系统,然后复制下载链接地址,使用wget命令下载

# wget https://downloads.mysql.com/archives/get/file/mysql-5.5.47.tar.gz

2. 解压缩源码包

# tar -zxvf mysql-5.5.47.tar.gz

3. 源码安装CMake

安装MySQL需要CMake,所以我们先用源码安装CMake。

下载网址:https://cmake.org/download/

下载之后解压, 并进入解压后的目录,执行下面的命令安装:

# ./bootstrap 
# make 
# make install 

4. 源码安装

可以参考这张网页,也可以直接跟着我的描述往下操作。

安装Perl-GD库

# yum install perl-GD

创建用户和组

为了保证安全,我们为MySQL创建一个单独的用户和组。按照下面的操作创建(其中我的mysql要安装在/wwwAdmiin/mysql5.5.47目录下,数据库数据放置在/wwwAdmiin/mysql5.5.47/data目录下,所以下面的目录根据自己要安装的位置而定。)

# useradd -r -U mysql -M -d /wwwAdmiin/mysql5.5.47/data

安装Boost

下载Boost

下载地址:http://sourceforge.net/projects/boost/files/boost/…

下载成功后解压到/usr/local/boost目录下,其他目录也可以,这里是举个例子,下面会用到。Boost使用的是下载目录,而不是安装目录。

安装ncurses lib

网址:http://invisible-island.net/ncurses/ncurses.html#d…

下载地址:ftp://invisible-island.net/ncurses/ncurses.tar.gz

解压后进入解压后的目录依次执行下面的命令:

# ./configuration 
# make 
# make install 

安装MySQL

进入mysql源码包解压后的目录,依次执行下面的命令:

# cmake . -DCMAKE_INSTALL_PREFIX=/wwwAdmiin/mysql5.5.47 -DMYSQL_DATADIR=/wwwAdmiin/mysql5.5.47/data -DMYSQL_USER=mysql -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DWITH_BOOST=/usr/local/boost 
# make 
# make install 

-DMYSQL_USER使用我们上面创建的用户mysql,-DWITH_BOOST指定的是boost的解压目录。

启动MySQL

进入MySQL安装目录的scripts目录下,执行下面的命令:

# ./mysql_install_db --user=mysql --basedir=/wwwAdmiin/mysql5.5.47 --datadir=/wwwAdmiin/mysql5.5.47/data

–user指定用户。如果报错:没有mysql_install_db的权限,使用chmod 777 mysql_install_db即可。

这行命令执行完后,命令行会给出提示接下来如何配置root密码,这个我忘记了。可以按照给出的提示修改,如果不敢操作,可以按照我下面介绍的方法修改,结果是一样的。

进入MySQL安装目录的bin目录下,依次执行下面的命令:

# ./mysqld_safe --user=mysql &
# ./mysql -u root -p //输入这行命令后提示输入密码时直接回车,因为默认密码是空的
# use mysql;
# UPDATE user SET Password = PASSWORD('更改后的密码') WHERE user = 'root';<span class="redactor-invisible-space">
# FLUSH PRIVILEGES;<span class="redactor-invisible-space"></span></span>

将MySQL启动服务添加到系统服务

进入MySQL安装目录,执行下面的命令:

# cp support-files/mysql.server /etc/init.d/mysql

五、安装PHP

1. 下载

下载地址:http://php.net/

2. 依赖库安装

Python(安装libxml2需要用到)

# yum search python | grep python-devel sudo yum install python-devel.x86_64 

libxml2

下载地址:ftp://xmlsoft.org/libxml2/

进入解压后的根目录,依次执行下面的命令:

# ./configuration 
# make 
# make install

libjpeg

# yum install libjpeg-devel

freetype

# yum install freetype-devel 

libmcrypt

下载地址ftp://mcrypt.hellug.gr/pub/crypto/mcrypt/libmcrypt

进入解压后的根目录,依次执行下面的命令:

# ./configuration 
# make
# make install 

libpng

下载地址:http://www.libpng.org/pub/png/libpng.html

下载完成后解压,然后进入解压目录,执行下面的命令:

# ./configuration
# make
# make install

3. 配置

# ./configure --prefix=/wwwAdmin/php5.5.38 --with-config-file-path=/wwwAdmin/php5.5.38/etc --with-apxs2=/wwwAdmin/apache2.4.29/bin/apxs --with-mysql=/wwwAdmin/mysql5.5.47/ --with-mysqli=/wwwAdmin/mysql5.5.47/bin/mysql_config --with-pcre-dir=/wwwAdmin/pcre2-10.30/ --with-libxml-dir --with-png-dir --with-jpeg-dir --with-fretype-dir --with-gd --with-zlib-dir --with-mcrypt --with-curl --enable-zip --enable-soap --enable-mbstring=all --enable-sockets --enable-calendar

4. 安装

# make
# make test
# make install

注意:以上安装并没有将各个服务设为开机启动,如果服务器发生reboot,需要手动开启Apache和MySQL。

apache的启动方式是进入安装目录下的bin目录,然后执行

# ./apachectl start

重启命令

# ./apachectl restart

停止命令

# ./apachectl stop

MySQL的启动相关命令

# service mysql start
# service mysql restart
# service mysql stop

喘口气~

到这里LAMP环境就安装完成了,下面进行配置

1、修改apache的用户名和组

我们先使用下面的命令新建用户:

# groupAdd wwwAdmin
# userAdd -g wwwAdmin wwwAdmin

然后进入apache安装目录下的conf目录下,使用 vi httpd.conf 命令打开apache的配置文件,找到

User daemon
Group daemon

修改为

User wwwAdmin
Group wwwAdmin

然后重启apache即可生效。

2、修改网站根目录

Apache默认的目录是在Apache安装目录下的htdocs目录下,我们可能希望将目录指向我们自建的位置,那么就需要做如下示例的配置:

  • 新建目录

我上面的安装目录都是wwwAdmin,我希望网站也放在这个目录中的某个文件夹下,进入wwwAdmin目录,执行下面的命令创建一个名为www的目录,用了存放网站。

mkdir www
  • 修改目录所属者和权限

我们将www目录的用户和组设置为Apache的用户和组,同时设置权限为可读写。

chown -r wwwAdmin:wwwAdmin www
chmod -r u+w www
  • 配置Apache网站根目录指向

打开apache的配置文件httpd.conf,找到

# symbolic links and aliases may be used to point to other locations.
#
DocumentRoot "/wwwAdmin/apache2.4.29/htdocs"
<Directory "/wwwAdmin/apache2.4.29/htdocs">
...
</Directory>

修改为

# symbolic links and aliases may be used to point to other locations.
#
DocumentRoot "/wwwAdmin/www"
<Directory "/wwwAdmin/www">
...
</Directory>

保存,然后重启apache即可生效。

3、配置Apache解析php

打开Apache的配置文件httpd.conf,找到

<Directory />
    AllowOverride none
    Require all denied
    ...
</Directory>

修改为(改为allow from all,不然会提示403)

<Directory />
    AllowOverride none
    #Require all denied
    Order deny,allow
    allow from all
</Directory>

找到

AddType application/x-gzip .gz .tgz

在下面添加(添加对php的解析)

AddType application/x-gzip .gz .tgz
AddType application/x-httpd-php .php 

找到

<IfModule dir_module>
    DirectoryIndex index.html index.htm index.php
</IfModule>

改为

<IfModule dir_module>
    DirectoryIndex index.html index.htm index.php
</IfModule>
  • 配置域名

打开Apache的配置文件httpd.conf,在文件末尾添加如下内容:

<VirtualHost *:80>
    ServerName www.geek-era.com
    DocumentRoot /wwwAdmin/www/basic/web
    <Directory "/wwwAdmin/www/basic/web">
        Options FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

DocumentRoot指定的是网站目录。

到这里整个环境的搭建就完成了(但是我的心情却有点复杂)

下面介绍几个可能会碰到的小问题

  • Fatal error: Can’t open and lock privilege tables: Table ‘mysql.user’ doesn’t exist

MySQL启动的时候使用的命令是

进入MySQL安装目录的scripts目录下,执行下面的命令:

# ./mysql_install_db --user=mysql --basedir=/wwwAdmiin/mysql5.5.47 --datadir=/wwwAdmiin/mysql5.5.47/data 

如果执行这行命令报如上的错误,则使用下面的命令:

# ./mysql_install_db --user=mysql --basedir=/wwwAdmiin/mysql5.5.47 --datadir=/wwwAdmiin/mysql5.5.47/data --ldata=/var/lib/mysql

我安装过程中确实碰到过这个问题,而且用这个方法解决了。

  • 安装PHP报错 ‘ext/date/lib/parse_date.lo’ is not a valid libtool object

出现这个问题是因为在源码包中已经运行过make,然后再次运行。解决方法很简单,执行 make clean 然后再执行 make 即可。