数据库

mysql 基础知识 (8) – 主从复制

mysql 有三种主从复制方式

MySQL 传统的高可用解决方案是通过 binlog 复制来搭建主从或一主多从的数据库集群。主从之间的复制模式支持异步模式 (async replication) 和半同步模式 (semi-sync replication)。无论哪种模式下,都是主库 master 提供读写事务的能力,而 slave 只能提供只读事务的能力。在 master 上执行的更新事务通过 binlog 复制的方式传送给 slave,slave 收到后将事务先写入 redo log,然后重放事务,即在 slave 上重新执行一次事务,从而达到主从机事务一致的效果。 

MySQL 的三种日志

  • binlog
  • redo log
  • undo log

参考

http://mysql.taobao.org/monthly/2017/08/01/
http://blog.csdn.net/d6619309/article/details/53691352
http://blog.51cto.com/wangwei007/1893703
https://www.digitalocean.com/community/tutorials/how-to-configure-mysql-group-replication-on-ubuntu-16-04
http://blog.csdn.net/d6619309/article/details/53691352
http://mysql.taobao.org/monthly/2017/08/01/

在 Ubuntu 上安装 MySQL

安装

sudo apt -y install mysql-server

默认账号密码:

cat /etc/mysql/debian.cnf

或者直接通过 sudo mysql 就可以进去。这是因为默认情况下 root 用户是通过 sudo 来校验的,而不需要密码,我们可以改成通过密码校验的方式:

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '';
FLUSH PRIVILEGES;

需要把 ubuntu 绑定地址改为 0.0.0.0。在 /etc/mysql/mysql.conf.d/mysqld.cnf 中注释掉 bind-address=127.0.0.1 行

开启 root 的远程登录:

GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'password';

在 8.0 以上需要显示创建 root 用户,见参考资料。

参考

  1. https://stackoverflow.com/questions/11223235/mysql-root-access-from-all-hosts

mysql 基础知识 (7) – JSON 字段

在前公司的时候,大家习惯在每个表加一个 extra 字段来表示一些额外的字段,然后在 ORM 中使用的时候再解析出来,方便了扩展字段,但是缺点也很明显,extra 字段只能读取而无法进行查询。MySQL 5.7 终于支持了 json 字段,相当于加入了一些 NoSQL 的特性,这样就可以很方便得查询了。

json 字段的使用

建表:

CREATE TABLE `book` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(200) NOT NULL,
  `tags` json DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

直接使用 json 类型就可以了。注意 json 字段不可以作为主键,不可以作为外键,不过既然是 json 字段了,谁会这么做呢。

插入:

INSERT INTO `book` (`title`, `tags`)
VALUES (
  "ECMAScript 2015: A SitePoint Anthology",
  "["JavaScript", "ES2015", "JSON"]"
);

使用一个 json 字符串作为值插入即可。或者你也可以使用 json 相关的函数来表示 json。

json 相关函数

json path

-- returns "SitePoint":
SELECT JSON_EXTRACT(
  "{"id": 1, "website": "SitePoint"}",
  "$.website"
);

json path 的语法,用 $ 开头,然后跟着下面的选择器:

  • . 点后面跟着跟着一个字典里的名字,比如 $.website
  • [N] 表示数组里的第 N 个元素
  • .[*] 表示选择字典里的所有元素
  • [*] 表示选择数组里的所有元素
  • prefix**suffix 表示以 prefix 开头,suffix 结尾的所有路径

举个栗子

{
  "a": 1,
  "b": 2,
  "c": [3, 4],
  "d": {
    "e": 5,
    "f": 6
  }
}
the following paths:

$.a returns 1
$.c returns [3, 4]
$.c[1] returns 4
$.d.e returns 5
$**.e returns [5]

构造、修改 json 的函数

函数都比较简单,看注释就明白了。

-- returns [1, 2, "abc"]:
SELECT JSON_ARRAY(1, 2, "abc");

-- returns {"a": 1, "b": 2}:
SELECT JSON_OBJECT("a", 1, "b", 2);

-- returns ["a", 1, {"key": "value"}]:
SELECT JSON_MERGE("["a", 1]", "{"key": "value"}");

-- returns ARRAY:
SELECT JSON_TYPE("[1, 2, "abc"]");

-- returns OBJECT:
SELECT JSON_TYPE("{"a": 1, "b": 2}");

-- returns an error:
SELECT JSON_TYPE("{"a": 1, "b": 2");

-- returns 1:
SELECT JSON_VALID("[1, 2, "abc"]");

还有其他一些函数,可以查看文档:

  • JSON_SET(doc, path, val[, path, val]…) —
    inserts or updates data in the document
  • JSON_INSERT(doc, path, val[, path, val]…) —
    inserts data into the document
  • JSON_REPLACE(doc, path, val[, path, val]…) —
    replaces data in the document
  • JSON_MERGE(doc, doc[, doc]…) —
    merges two or more documents
  • JSONARRAYAPPEND(doc, path, val[, path, val]…) —
    appends values to the end of an array
  • JSONARRAYINSERT(doc, path, val[, path, val]…) —
    inserts an array within the document
  • JSON_REMOVE(doc, path[, path]…) —
    removes data from the document.

查询 json 函数

用于 where 子句中的函数

json_contains 用于选取数组中包含某个元素的行

-- all books with the "JavaScript" tag:
SELECT * FROM `book`
WHERE JSON_CONTAINS(tags, "["JavaScript"]");

json_search 用于选取字典中包含某个值的行

-- all books with tags starting "Java":
SELECT * FROM `book`
WHERE JSON_SEARCH(tags, "one", "Java%") IS NOT NULL;

用于 select 子句中的 json 函数

可以使用 json path 语法从得到的 json 文档中抽取出某个值。

select 语句

要想在 select 语句中使用 json path 抽取元素可以使用下面的语法,也就是 column->path

SELECT
  name,
  tags->"$[0]" AS `tag1`
FROM `book`;

一个更复杂一点的例子:

| id | name | profile |
| — | ——— | ———————————————————————————— |
| 1 | Craig | {“twitter”: “@craigbuckler”,“facebook”: “craigbuckler”,“googleplus”: “craigbuckler”} |
| 2 | SitePoint | {“twitter”: “@sitepointdotcom”} |

SELECT
  name, profile->"$.twitter" AS `twitter`
FROM `user`;
SELECT
  name, profile->"$.twitter" AS `twitter`
FROM `user`
WHERE
  profile->"$.twitter" IS NOT NULL;

参考

  1. https://www.sitepoint.com/use-json-data-fields-mysql-databases/

mysql 基础知识 (6) – Join 和 Subquery

看到网上 [有篇文章][1] 用韦恩图来讲解了一下 SQL 的 join 操作,但是感觉举的例子似乎不太实际,遂自己写了一篇,图是从那篇文章里面盗的(逃

假设我们有下面两张表,上边的是表 user,下边的是 package,表示每个用户对应的包裹

| id | name |
| — | —— |
| 1 | Luke |
| 2 | Leia |
| 3 | Anakin |
| 4 | Padem |

| id | content | user_id |
| — | ———- | ——- |
| 1 | droid | 3 |
| 2 | lightsaber | 2 |
| 3 | blaster | 1 |
| 4 | R2D2 | 5 |

创建这两个表的语句分别是:

create table user (id integer, name string);
create table package (id integer, content string, user_id integer);
insert into user (id, name) values (1, "Luke");
insert into user (id, name) values (2, "Leia");
insert into user (id, name) values (3, "Anakin");
insert into user (id, name) values (4, "Padme");
insert into package (id, content, user_id) values (1, "droid", 3);
insert into package (id, content, user_id) values (2, "lightsaber", 2);
insert into package (id, content, user_id) values (3, "blaster", 1);
insert into package (id, content, user_id) values (4, "R2D2", 5);

Veen diagram(韦恩图)是一种表示集合的图形语言。SQL 的 join 本质上也是从集合论里面来的,可以从集合论的角度来学习和记忆 Join 的语法。

Inner Join

如果我们要选出每个有包裹的人,以及对应的包裹,可以使用 inner join。内连接(inner join)计算的是两个表的交集,也就是 A ∩ B

select
    user.id, user.name, package.id, package.content
from
    user inner join package
on user.id == package.user_id;

结果一共有 3 列,每个表中的第四列都因为在对方表中没有而没有出现在结果里。

id          name        id          content
----------  ----------  ----------  ----------
1           Luke        3           blaster
2           Leia        2           lightsaber
3           Anakin      1           droid

如果需要使用过滤条件,那么可以使用 where 语句或者 on 中的语句,不过在 inner join 中,这两者几乎是等价的。

Left Outer Join

如果我们要取出每个人的包裹情况,没有包裹的也写上 null,那么这用情况下应该使用 left outer join。

select
    user.id, user.name, package.id, package.content
from
    user left outer join package
on user.id == package.user_id;

id          name        id          content
----------  ----------  ----------  ----------
1           Luke        3           blaster
2           Leia        2           lightsaber
3           Anakin      1           droid
4           Padme       NULL        NULL

Where 和 on 的区别

They are not the same thing.

Consider these queries:

SELECT *
FROM Orders
LEFT JOIN OrderLines ON OrderLines.OrderID=Orders.ID
WHERE Orders.ID = 12345

and

SELECT *
FROM Orders
LEFT JOIN OrderLines ON OrderLines.OrderID=Orders.ID 
    AND Orders.ID = 12345

The first will return an order and its lines, if any, for order number 12345. The second will return all orders, but only order 12345 will have any lines associated with it.

With an INNER JOIN, the clauses are effectively equivalent. However, just because they are functionally the same, in that they produce the same results, does not mean the two kinds of clauses have the same semantic meaning.

我们还可以使用 left join 来过滤不包含某些对应关系的行。

Full Outer Join

如果我们想要选出所有的任何包裹的对应关系,哪怕是对应得人或者包裹不存在的话,可以使用 full outer join。全连接计算的是两个表的并集,也就是 A ∪ B

select
    user.id, user.name, package.id, package.content
from
    user full outer join package
on user.id == package.user_id;

结果一共有 6 列,注意其中缺字段的地方被补上了 null。另外 SQLite 不支持 full outer join。感觉这个 Join 似乎用的不是太多,因为实际情况中,往往 package.userid 是 user.id 的外键,所以不会出现 userid 不存在的情况。

// 结果省略

Cross Join

要获得 A 表和 B 表左右可能的交叉组合的话,可以使用 cross join,也就是笛卡尔乘积。

select
    user.id, user.name, package.id, package.content
from
    user cross join package;

结果如下

id          name        id          content
----------  ----------  ----------  ----------
1           Luke        1           droid
1           Luke        2           lightsaber
1           Luke        3           blaster
1           Luke        4           R2D2
2           Leia        1           droid
2           Leia        2           lightsaber
2           Leia        3           blaster
2           Leia        4           R2D2
3           Anakin      1           droid
3           Anakin      2           lightsaber
3           Anakin      3           blaster
3           Anakin      4           R2D2
4           Padme       1           droid
4           Padme       2           lightsaber
4           Padme       3           blaster
4           Padme       4           R2D2

Subquery

子查询是可以和 join 等价的一种查询方式。在以往(2010 年左右),子查询被认为是比较低效的查询方式,但是随着 SQL 引擎的进步,子查询应该在性能上问题不大了。

子查询其实很简单,就是一个查询的结果是另一个查询的数据基础。和函数调用有一些相似。

第一种常见用法,把子查询的结果用在 IN 中。

select * from students where id in (select sudent_id from student_awards);

第二种使用子查询来作为结果列的数值。

select id, (select count(*) from student_awards where students.id = student_awards.student_id) from students;

参考

  1. https://blog.codinghorror.com/a-visual-explanation-of-sql-joins/
  2. https://stackoverflow.com/questions/354070/sql-join-where-clause-vs-on-clause
  3. https://stackoverflow.com/questions/25890534/mysql-select-rows-where-left-join-is-null
  4. https://stackoverflow.com/questions/2577174/join-vs-sub-query

mysql 基础知识 (5) – 聚合语句 (group by)

Group by 用来按照某一列或者某几列的值聚合数据。group by x 按照 x 相同的值聚合,group by x, y 按照 x 和 y 都相同的值聚合。而查询的列要么是聚合的列,要么应该通过聚合函数来选取一列。而且所有的 null 会被聚合成一行。

在 SQL 规范中,只能查询 group_by 的字段,如果查询其他字段,这种行为是未定义的。

比如说下面的数据表中

-- How many countries are in each continent?
select
  continent
  , count(*)
from
  countries
group by
  continent

执行查询可以得到每个洲的国家的数量。

过滤

在 SQL 中,Where 子句是在 group 子句之前运行的,所以我们无法通过 where 来过滤 group 之后的结果,而应该使用 having 子句来过滤。

select
 continent
  , max(area)
from
  countries
group by
  1
having
  max(area) >= 1e7

隐式聚合

当你没有使用 group by,而使用了 max、min、count 等聚合函数的时候已经在聚合了

-- What is the largest and average country size in Europe?
select
  max(area) as largest_country
  , avg(area) as avg_country_area
from
  countries
where
  continent = "Europe"

MySQL 的特殊处理

如果在查询中有没有聚合的列,那么 MySQL 就会随机选取一个列,比如下面就会随机选取一个州。总之,这个行为是未定义的,不要这么查询。

select
  country
  , state
  , count(*)
from
  countries
group by
  country

参考

  1. https://www.periscopedata.com/blog/everything-about-group-by

完全理解 SQL 的内在逻辑

太多的程序员认为 SQL 像是洪水猛兽一样。它是少有的几种声明式的语言,和其他的命令似的面向对象的甚至函数使得语言大相径庭。
我每天都会写 SQL 而且在我的开源项目中大量的使用 SQL,因此我非常地想要把 SQL 的美展现给你们这些还在挣扎着使用它的渣渣们。下面的教程适合

  1. 使用过 SQL 但是从来没有完全理解他的人
  2. 很了解 SQL,但是从来没有思考过他的语法的人
  3. 想要把 SQL 交给其他人的人

这个教程将会这关注 SELECT 语句,其他的 DML 将会在另一篇文章中介绍

SQL 是声明式的

首先要记住,声明式。唯一的一种范式就是你可以只是声明你想要的结果就得到了他。而不是告诉你的电脑怎样去把这个结果计算出来,不错吧?

Select first_name, last_name FROM employees WHERE salary > 100000

很简单,你不需要关心 employee 的记录是存在哪里的,你只想要知道那些薪水还不错的人。

如此简单,那么问题在哪里呢?问题在于我们大部分时候是在按照命令式的编程思维在思考,比如“机器,干这个,然后干那个,但是在这之前检查一下,如果是这样或者那样就不行”。这其中包括了存储临时结果在变量里,循环,迭代,调用函数等等。

忘掉那些东西,思考如何声明东西,而不是告诉机器怎样去计算。

SQL 语法的顺序有些问题

常见的混乱的来源可能是 SQL 语法并不是按他们的执行顺序来排序的,词法(Lexical)排序是

  1. SELECT [DISTINCT]
  2. FROM
  3. WHERE
  4. GROUP BY
  5. HAVING
  6. UNION
  7. ORDER BY

简洁起见,并没有列出所有语句,而从逻辑上来说,真正的逻辑执行顺序是这样的:

  1. FROM。FROM 后面的表标识了这条语句要查询的数据源。和一些子句如,(1-J1)笛卡尔积,(1-J2)ON 过滤,(1-J3)添加外部列,所要应用的对象。FROM 过程之后会生成一个虚拟表 VT1。

    1. (1-J1) 笛卡尔积 这个步骤会计算两个相关联表的笛卡尔积 (CROSS JOIN) ,生成虚拟表 VT1-J1。
    2. (1-J2)ON 过滤 这个步骤基于虚拟表 VT1-J1 这一个虚拟表进行过滤,过滤出所有满足 ON 谓词条件的列,生成虚拟表 VT1-J2。
    3. (1-J3) 添加外部行 如果使用了外连接,保留表中的不符合 ON 条件的列也会被加入到 VT1-J2 中,作为外部行,生成虚拟表 VT1-J3。
  2. WHERE 对 VT1 过程中生成的临时表进行过滤,满足 where 子句的列被插入到 VT2 表中。

  3. GROUP BY 这个子句会把 VT2 中生成的表按照 GROUP BY 中的列进行分组。生成 VT3 表。

  4. HAVING 这个子句对 VT3 表中的不同的组进行过滤,满足 HAVING 条件的子句被加入到 VT4 表中。

  5. SELECT 这个子句对 SELECT 子句中的元素进行处理,生成 VT5 表。

    1. (5-1) 计算表达式 计算 SELECT 子句中的表达式,生成 VT5-1
    2. (5-2)DISTINCT 寻找 VT5-1 中的重复列,并删掉,生成 VT5-2
    3. (5-3)TOP 从 ORDER BY 子句定义的结果中,筛选出符合条件的列。生成 VT5-3 表
  6. ORDER BY 从 VT5-3 中的表中,根据 ORDER BY 子句的条件对结果进行排序,生成 VC6 表。

当然强大的 SQL 执行引擎在实际执行过程用会有各种优化,不一定严格按照这个顺序来。但是在写和看 SQL 的时候可以按照这个逻辑思考。

例子

可以思考一下下面这个语句的执行过程

SELECT C.customerid, COUNT(O.orderid) AS numorders
FROM dbo.Customers AS C
  LEFT OUTER JOIN dbo.Orders AS O
    ON C.customerid = O.customerid
WHERE C.city = "Madrid"
GROUP BY C.customerid
HAVING COUNT(O.orderid) < 3
ORDER BY numorders

SQL 是关于表的(而不是列)

因为词法排序和逻辑排序上的不同,很多的初学者认为列的值是 SQL 中的一等公民,实际上,不是。最重要的是表的引用。

比如说

FROM a,b

这个语句实际上是 a cross join b,也就是笛卡尔乘积。比如说,a 中有 3 列 3 行数据,b 中有 5 列 5 行数据。上面的一句产生的结果是一个 3+5=8 列,3×5=15 行的数据。

不过,尽量显式 join 的表,而不要使用逗号。

SQL 中衍生的表可以看做表的变量。

-- A derived table
FROM (SELECT * FROM author) a -- 后边这个变量是可选的
-- Get authors" first and last names, and their age in days
SELECT first_name, last_name, age
FROM (
  SELECT first_name, last_name, current_date - date_of_birth age
  FROM author
)
-- If the age is greater than 10000 days
WHERE age > 10000

在 MySQL 8.0 中还可以使用 with 语句

WITH a AS (
  SELECT first_name, last_name, current_date - date_of_birth age
  FROM author
)
SELECT *
FROM a
WHERE age > 10000

SQL 中的 Select 语句在关系代数中被称作投影(projection)。一旦你生成了表的引用,然后过滤,转换,接着你就可以把它投影成另一种形式。在 select 语句中,你终于可以按列操作生成的表了。也就是说其他的语句都是按表,或者说按照行操作的,只有到了 select 语句中你才可以操作列。

执行完了 select 语句之后,你就可以执行其他的集合排序等等操作了。

  • distinct 删除重复的行
  • union 把两个查询组合起来,并且删除重复的行
  • union all 把两个查询组合起来,并且不删除重复
  • except 做差集并且删除重复的行
  • intersect 求交集

ORDER BY 排序

参考

  1. https://web.archive.org/web/20150424213133/http://tech.pro:80/tutorial/1555/10-easy-steps-to-a-complete-understanding-of-sql
  2. http://www.cnblogs.com/myprogram/archive/2013/01/24/2874666.html

sqlite3 和在 Python 中的使用教程

Yifei’s notes: 所谓的事务、锁、存储过程、外键等等不适合互联网公司的业务场景,而更适合于“企业级”“IOE”这些应用。

Why Sqlite

相对于 MySQL 来说少了好多维护的东西,比如说不用关心用户、权限和备份语句都是什么,只要保证自己的数据库文件是安全的就好了。

数据类型

sqlite 是动态数据类型的,也就是实际上他并不会限定每一列一定要存什么数据。sqlite 中定义数据类型紧紧是为了兼容 sql 语句。

sqlite 中有五种数据类型:null, integer, real(float), text, blob. sqlite 使用了亲和度来表示一个列倾向于存储什么类型。也分为了五种:integer, read, numeric, text, blob. 其中 numeric 就是表示浮点和整数都可以。

mysql 中的 varchar, text, integer, datetime 等都是支持的,分别会对应到这五种类型中对应的亲和度。值得注意的是,datetime 对应到的是数字类型。

另一点需要注意的是,即使定义了类型,sqlite 依然可以存储任意数据,比如说往 int 列中插入一个 str,并不会报错。sqlite 的数据类型是和数据本身绑定的,而不是列。

主键

使用 primary key 定义主键。默认情况下,主键就是自增的,但是有可能会复用被删除的 id, 使用 autoincrement 关键字之后就会严格自增,而不会复用之前的 id.

sqlite 中的索引可以使用 create index 语句创建:

CREATE [UNIQUE] INDEX if not exists index_name on table_name (column_name1, column_name2);

或者在 create table 语句的字段后面直接添加 index 关键字。

插入数据

可以直接采用 MySQL 的多行语法批量插入数据:

INSERT INTO 'tablename' ('column1', 'column2') VALUES
    ('data1', 'data2'),
    ('data1', 'data2');

upsert

sqlite 采用了 postgres 的 upsert 语句

INSERT INTO players (user_name, age)
  VALUES('steven', 32) 
  ON CONFLICT(user_name) 
  DO UPDATE SET age=excluded.age;  -- 这里使用 excluded 引用本来要添加的字段,如果是原来的字段直接使用,比如:count=count+1

命令行操作

内置管理命令

% sqlite3 DATABASE # 打开一个数据库

.databases  show databases connected to
.dump TABLE dump table in SQL format
.import FILE ?TABLE?    import SQL data into table
.indices ?TABLE?    show indices of table
.mode MODE  set output mode(csv, column
.read FILE  excute FILE
.schema ?TABLE? show create statement
.restore  ?DB? FILE restore db from file
.headers ON|OFF show headers

美化输出

让 sqlite 的输出更美观,使用 .mode column.headers on 两个命令

sqlite> select * from foo;
234|kshitiz|dba.se

sqlite> .mode column
sqlite> select * from foo;
234         kshitiz     dba.se

sqlite> .headers on
sqlite> select * from foo;
bar         baz         baf
----------  ----------  ----------
234         kshitiz     dba.se

导入导出 csv

导出

sqlite> .mode csv   -- use ".separator SOME_STRING" for something other than a comma.
sqlite> .headers on 
sqlite> .out file.csv 
sqlite> select * from MyTable;

导入

sqlite> .mode csv
sqlite> .import CSV_FILE TABLE_NAME

导入导出 sql

sqlite3 DATABASE .dump > backup.sql
sqlite3 DATABASE < backup.sql

或者

sqlite3> .read backup.sql
sqlite3> .dump backup.sql

参考

  1. https://www.sqlite.org/datatype3.html
  2. https://stackoverflow.com/questions/1609637/is-it-possible-to-insert-multiple-rows-at-a-time-in-an-sqlite-database
  3. https://stackoverflow.com/questions/15277373/sqlite-upsert-update-or-insert

MyISAM vs InnoDB: mysql 的两种存储引擎的区别

  • InnoDB 有行级别的锁,而 MyISAM 只能锁定到表级别。
  • InnoDB 有更好的故障恢复机制。
  • InnoDB 实现了事务、外键和关系限制,MyISAM 没有.

总的来说,引用完整性和事物才是数据库的本质,所以说:“MyISAM is a file system that understands SQL. There’s no comparison. If you want a database engine with MySQL, use InnoDB.”

聚簇索引

MyISAM 没有使用聚簇索引,InnoDB 使用了聚簇索引。

参考

  1. https://dba.stackexchange.com/questions/1/what-are-the-main-differences-between-innodb-and-myisam

  2. InnoDB versus MyISAM: no comparison

mysql 基础知识 (4) – 用户和权限

创建用户

CREATE USER "newuser"@"%" IDENTIFIED BY "password";

授权所有权限

GRANT ALL PRIVILEGES ON *.* TO "newuser"@"%";

其中的 % 代表这个用户可以在任意主机登录。

如果只需要在本机登录,使用 localhost

显示一个用户的当前授权:

SHOW GRANTS FOR newuser

更改用户密码:

ALTER USER 'root'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'yourpasswd';
flush privileges;

其他问题

EXPLAIN, slow-log

解决小内存机器 MySQL 总是 OOM 的问题

有一台 512M 内存的小机器总是报数据库错误,查看了下日志是 OOM 了

解决方案:

一 Add swap file to cloud instance

http://www.prowebdev.us/2012/05/amazon-ec2-linux-micro-swap-space.html

  1. Run dd if=/dev/zero of=/swapfile bs=1M count=1024
  2. Run mkswap /swapfile
  3. Run swapon /swapfile
  4. Add this line /swapfile swap swap defaults 0 0 to /etc/fstab  

Some useful command related to SWAP space:

$ swapon -s
$ free -k
$ swapoff -a
$ swapon  -a

二 limit mysql buffersize

innodbbufferpool_size = 8M

三 limit apache memory cosumption,editing /etc/apache2/mods-enabled/mpm_prefork.conf

<IfModule mpm_prefork_module>
    StartServers        3
    MinSpareServers     3
    MaxSpareServers     5
    MaxRequestWorkers   25
    MaxConnectionsPerChild  0
</IfModule>