MySQL架构和历史
存储引擎架构

将查询处理及其他系统任务(MySQL服务层)和数据的存储/提取相分离(存储引擎层)

  1. 连接层:连接处理、授权认证、安全

  2. MySQL服务层:解析器、优化器、查询缓存

  3. 存储引擎:存储和提取

LOCK TABLES/UNLOCK TABLES 在MySQL服务层实现,而事务是由存储引擎层实现的,索引也是有存储引擎层实现的。

并发控制

只要有两个查询在同一时刻修改数据,都会产生并发控制的问题,MySQL有两个层面的并发控制:服务器层(比如邮箱 表锁)和存储引擎层(比如邮件 行锁)。

  1. 读写锁 S X
  2. 锁粒度 尽量只锁定需要修改的部分数据,而不是所有的数据,加锁也需要消耗资源,锁的各种操作,包括获得锁、检查锁是否已经解除、释放锁等,都会增加系统的开销。所谓的锁策略就是在锁的开销和数据的安全性之间寻求平衡。
  3. 表锁 table lock
  4. 行级锁 row lock --> 只在存储引擎层实现

事务:一组原子性的SQL查询,或者说一个独立的工作单元,事务内的语句要么全部执行,要么全部执行失败

除非系统通过了严格的ACID测试,否则空谈事务的概念是不够的。

隔离级别:每一种级别都规定了一个事务中所作的修改,哪些在事务内和事务间是可见的,哪些是不可见的。脏读、不可重复读、幻读以及加锁读。

事务日志:使用事务日志,存储引擎在修改表的数据时只需要修改其内存拷贝,再把该修改行为记录到持久在硬盘上的事务日志中(顺序I/O),而不用每次都将修改的数据本身持久化到磁盘中(随机I/O)。

-- 查询AUTOCOMMIT变量
SHOW VARIABLES LIKE 'AUTOCOMMIT';
-- 开启AUTOCOMMIT
SET AUTOCOMMIT = 1;
SHOW VARIABLES LIKE 'AUTOCOMMIT';
-- 关闭AUTOCOMMIT
SET AUTOCOMMIT = 0;
SHOW VARIABLES LIKE 'AUTOCOMMIT';
-- 修改AUTOCOMMIT对非事务型的表,比如MyISAM或者内存表,不会有任何影响-- 设置事务隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SELECT @@TX_ISOLATION;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT @@TX_ISOLATION;
SET TX_ISOLATION = 'READ-COMMITTED';
SHOW VARIABLES LIKE 'TX_ISOLATION';
SET SESSION TX_ISOLATION = 'REPEATABLE-READ';
SHOW VARIABLES LIKE 'TX_ISOLATION';

隐式和显式锁定:两阶段锁定协议(two-phase locking protocol)

隐式加锁:事务执行过程中,随时都可以锁定,锁只有在执行commit或rollback时才会释放,innodb根据隔离级别在需要的时候自动加锁

显式加锁:select…lock in share mode、select…for update

多版本并发控制

MYSQL的大多数事务型存储引擎实现的都不是简单的行级锁,基于提升并发性能的考虑,它们一般都实现了多版本并发控制(MVCC Multiversion Coucurrency Control),不仅仅是MYSQL,ORCALE等其他数据库也都实现了MVCC,但各自实现机制不尽相同,因此MVCC没有统一的实现标准。典型的有乐观(optimistic)并发控制悲观(pessimistic)并发控制
MYSQL的innodb的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存行的过期事件(或删除时间).当然存储的并不是实际的时间值,而是系统版本号(system version number).每开始一个新的事务,系统版本号都会自动递增,事务开始时刻的系统版本号作为事务的版本号,用来和查询到的每行记录的版本号进行控制。
– 涉及到三个版本:当前事务版本、行的创建系统版本号、行的删除系统版本号,在查询时比较当前事务版本与行创建系统版本号与删除系统版本号筛选结果,在插入和删除时保存当前系统版本号为行版本号和行删除标识,在更新时,同时保存当前系统版本号到原来的行版本号和行删除标识。

MVCC只在REPEATABLE READ和READ COMMITED两个隔离级别下工作,其他的两个隔离级别都和MVCC不兼容。因为READ UNCOMMITED总是读取最新的数据行,而不是符合当前事务版本的数据行,REPEATABLE按当前事务版本读取数据行, 而SERIALIZABLE则会对所有读取的行都加锁。

USE mysql;
SHOW TABLE STATUS LIKE 'user';  -- 查询表的信息   表的定义保存在.frm文件中

InnoDB存储引擎:

InnoDB的数据存储在表空间中,表空间是由InnoDB管理的一个黑盒子,由一系列的数据文件组成。

InnoDB采用MVCC来支持高并发,并且实现了四个标准的隔离级别,其默认级别是REPEATABLE READ(可重复读),并且通过间隙锁(next-key locking)策略防止幻读的出现,间隙锁使得InnoDB不仅仅锁定查询涉及的行,还会对索引中的间隙进行锁定,以防止幻影行的出现。

InnoDB基于聚簇索引,聚簇索引对主键查询有很高的性能。不过二级索引(secondary index 非主键索引)如果为非覆盖索引,则返回主键列,再次查询聚簇索引,称为回表。

InnoDB的优势:支持事务、热备份、崩溃恢复

MyISAM: 可以压缩 支持地理空间搜索(比较鸡肋) 可以崩溃恢复(MyISAM只将数据写到内存中,等待操作系统定期将数据输入磁盘,因此发生故障时损坏的概率比InnoDB要高很多) 表锁

不要相信MyISAM比InnoDB快之类的经验之谈:MyISAM引擎在一开始可能没有任何问题,但随着应用压力的上升,则可能迅速恶化,各种(表)锁争用、崩溃后的数据丢失等问题都会随之而来。

转换表的存储引擎:

  1. ALTER TABLE mytable ENGINE = InnoDB; – 需要执行很长时间以及索引重建、丢失等问题
  2. 导出和导入 mysqldump
  3. 创建与查询
CREATE TABLE innodb_table LIKE myisam_table;
ALTER TABLE innodb_table ENGINE = InnoDB;
INSERT INTO innodb_table SELECT * FROM myisam_table;-- 在默认情况下,每一次插入都会自动提交 涉及一个小事务 因此导致插入速度非常慢 因此考虑一批一批数据的插入START TRANSACTION;
INSERT INTO innodb_table SELECT * FROM myisam_table WHERE id BETWEEN x AND y;
COMMIT;

总结:

MYSQL拥有分层的架构,上层是服务器层的服务和查询执行引擎,下层则是存储引擎。虽然有很多不同作用的插件API,但存储引擎API还是最重要的。如果能理解MySQL在存储引擎和服务层之间处理查询是如何通过API来回交互,就能抓住MySQL的核心基础架构的精髓。

MySQL最初基于ISAM构建,后来被MyISAM取代,其后陆续添加了更多的存储引擎和事务支持。MySQL有一些怪异的行为是由于历史遗留导致的。例如,在执行ALTER TABLE时,MySQL提交事务的方式是由存储引擎的架构直接导致的,并且数据字典也保存在.frm文件中(这并不是说InnoBD会导致ALTER变成非事务型的,对于InnoDB来说,所有的操作都是事务)

当然,存储引擎API的架构也有一些缺点。有时候选择多并非好事,而在MySQL5.0和MySQL5.1中有太多的存储引擎可以选择.InnoDB对于95%以上的用户来说都是最佳选择,所以其他的存储引擎可能只是让事情变得复杂难搞,当然也不可否认某些情况下某些存储引擎能更好地满足需求。

Oracle一开始收购了InnoDB,之后又收购了MySQL,在同一个屋檐下对于两者都是有利的。InnoDB和MySQL服务器之间可以更快地协同发展。MySQL依然基于GPL协议开放全部源代码,社区和客户可以获得坚固而稳定的数据库,MySQL正在变得越来越可扩展和有用。

MYSQL基准测试
测试指标:

吞吐量— 单位时间内的事务处理数 常用的测试单位是每秒事务数(TPS Transaction Per Second),也可以采用每分钟事务数(TPM)。
响应时间或者延迟— 用于测试任务所需的整体时间
并发性— 并发性是一个非常重要又经常被误解和误用的指标,它经常被表示成多少用户在同一时间浏览一个Web站点,经常使用的指标是有多少个会话,然而http协议是无状态的,大多数用户只是简单地读取浏览器上显示的信息,这并不等同于Web服务器的并发性,而且Web服务器的并发性也不等同于数据库的并发性,而仅仅只是表示会话存储机制可以处理处理多少数据的能力,Web服务器的并发性能更准确的度量指标,应该是在任意时间有多少同时发生的并发请求。注意不要将创建数据库连接和并发性搞混淆,一个设计良好的应用,可以同时打开成千上百个MySQL数据库连接,但可能同时只有少数连接在执行查询,所以说,一个Web站点“同时有50000个用户”访问,却可能只有10-15个并发请求到了MYSQL数据库。换句话说,并发性基准测试需要关注的是正在工作的并发操作,或者是同时工作中的线程数或者连接数,当并发性增加时,需要测量吞吐量是否下降,响应时间是否变长,可以通过sysbench指定32、64或者128个线程的测试,然后再测试期间记录MySQL数据库的Threads_running状态值。
可扩展性— 可扩展性指的是,给系统增加一倍的工作,在理性情况下就能获得两倍的结果(即吞吐量增加一倍),或者说,给系统增加一倍的资源(比如两倍的CPU数),就可以获得两倍的吞吐量,当然性能也必须在可以接受的范围内,大多数的系统是无法做到如此理想的线性扩展的,随着压力的变化,吞吐量和性能都可能越来越差。

– 查询cpu信息

cat /proc/cpuinfo
sysbench --test=cpu --cpu-max-prime=20000 run

暴力搜索算法 naive search algorithm
性能剖析工具(profile)
pt-query-digest
New Relic

通过tcpdump抓去tcp数据包

通过设置long_query_time为0来捕获所有的查询,而且查询的响应时间单位已经可以做到微秒级。慢查询日志带来的开销可以忽略不计,但是需要担心的是日志会占用大量的磁盘空间,如果长期开启慢查询日志,注意需要部署日志轮转(log rotation)工具。

SHOW PROCESSLIST;
SHOW FULL PROCESSLIST;SELECT * FROM ttrd_otc_trade;SHOW PROFILES;-- 开启性能就监控SET profiling = 1;SET @query_id = 83;--
SELECT STATE,SUM(DURATION) AS TOTAL_R,ROUND(100*SUM(DURATION)/(SELECT SUM(DURATION) FROM information_schema.PROFILING WHERE QUERY_ID = @query_id),2) AS Pct_R,COUNT(*) AS Calls,SUM(DURATION)/COUNT(*) AS "R/Call"FROM information_schema.PROFILING WHERE QUERY_ID = @query_id GROUP BY STATE ORDER BY TOTAL_R DESCSTATE                   TOTAL_R     Pct_R  Calls   R/Call
Sending data            0.024006    99.05   1   0.0240060000
updating status         0.000063    0.26    1   0.0000630000
init                    0.000044    0.18    1   0.0000440000
starting                0.000031    0.13    1   0.0000310000
Opening tables          0.000012    0.05    1   0.0000120000
optimizing              0.000012    0.05    1   0.0000120000
end                     0.000012    0.05    1   0.0000120000
preparing               0.000010    0.04    1   0.0000100000
statistics              0.000009    0.04    1   0.0000090000
closing tables          0.000007    0.03    1   0.0000070000
After opening tables    0.000007    0.03    2   0.0000035000
freeing items           0.000006    0.02    1   0.0000060000
query end               0.000004    0.02    1   0.0000040000
checking permissions    0.000004    0.02    1   0.0000040000
cleaning up             0.000004    0.02    1   0.0000040000
Table lock              0.000002    0.01    1   0.0000020000
executing               0.000002    0.01    1   0.0000020000
System lock             0.000002    0.01    1   0.0000020000
-- 这个命令每秒捕获一次SHOW GLOBAL STATUS的数据,输出给awk计算并输出每秒的查询数、Thread_connected和Thread_running(表示当前正在执行的查询数)
mysqladmin ext -i1 | awk '/Queries/{q=$4-qp;qp=$4}/Threads_connected/{tc=$4}/Thread_running/{printf "%5d %5d %5d\n",q,tc,$4}'

通过远程执行该命令

mysqladmin: connect to server at 'localhost' failed
error: 'Access denied for user 'root'@'localhost' (using password: NO)'

在mysql数据库中的root用户权限问题。Host为localhost和127.0.0.1,需要进行权限设置 可以进行远程访问。
进行mysql用户的访问权限控制设置方法??

mysql -e 'show processlist\G' | grep State: | sort | uniq -c | sort -n
mysqladmin -uxir_trd -pxpar ext -i1 | awk '/Queries/{q=$4-qp;qp=$4}/Threads_connected/{tc=$4}/Thread_running/{printf "%5d %5d %5d\n",q,tc,$4}'
mysql -uxir_trd -pxpar -e 'show processlist\G' | grep State: | sort | uniq -c | sort -n
mysql -uxir_trd -pxpar -e 'show processlist\G' | grep -c "State:freeing items"

误报(false positive)或者 漏检(false negative).误报是指收集了很多诊断数据,但期间没有发生问题,这可能浪费时间,而且令人沮丧。而漏检则指在问题出现时没有捕获到数据,错失了机会,一样很浪费时间。

Schema与数据类型优化
drop TABLE if EXISTS enum_test;
create TABLE enum_test(e ENUM('fish','apple','dog') NOT NULL
);INSERT INTO enum_test(e) VALUES ('fish'),('apple'),('dog'),('dog');SELECT e+0 FROM enum_test;  -- 1,2,3,3
SELECT * FROM enum_test ORDER BY e;   -- fish,apple,dog,dog

在特定情况下,把CHAR/VARCHAR列与枚举列进行关联可能会比直接关联CHAR/VARCHAR列更慢。
如果不是必须有VARCHAR列进行关联,那么转换这些列为ENUM就是个好主意,这是一个通用的设计实践,在“查找表”时采用整数主键而避免采用基于字符串的值进行关联。

-- ip地址的转换
SELECT INET_ATON('192.168.56.102');   -- 3232249958
SELECT INET_NTOA('3232249958');       -- 192.168.56.102
DROP TABLE IF EXISTS MY_SUMMARY_NEW,MY_SUMMARY_OLD;
CREATE TABLE MY_SUMMARY_NEW LIKE MY_SUMMARY;
RENAME TABLE MY_SUMMARY TO MY_SUMMARY_OLD,MY_SUMMARY_NEW TO MY_SUMMARY;

范式与反范式

范式一:列要纯粹

范式二 :只有一个业务主键

范式三 : 既不部分依赖,也不传递依赖非业务主键

范式:节省存储空间、更新快 但查询需要关联多张表

反范式:减少表关联 提升查询速度 空间消耗 更新慢

简单的原则:

  1. 尽量避免过度设计, 例如会导致极其复杂查询的schema设计,或者有很多列的表设计
  2. 使用小而简单的合适数据类型,除非真实数据模型中有确切的需要,否则应该尽可能的避免使用NULL值。
  3. 尽量使用相同的数据类型存储相似或相关的值,尤其是要在关联条件中使用的列,
  4. 注意可变长字符串,其在临时表和排序时可能导致悲观的按最大长度分配内存。
  5. 尽量使用整形定义标识列
  6. 避免使用mysql已经弃用的特性,例如指定浮点数的精度,或者整形的显示宽度
  7. 小心是用Enum或Set,虽然它们用起来很方便,但是不要滥用,否则有时候会变成陷进。最后避免使用BIT.
创建高性能的索引

索引:存储引擎用于快速找到记录的一种数据结构

索引的类型

在MySQL中,索引是在存储引擎层而不是服务层实现的,不同存储引擎的索引工作方式并不一样,也不是所有的存储引擎都支持所有类型的索引。

  1. B-Tree索引:

实际上很多存储引擎使用的是B+Tree,即每一个叶子节点都包含指向下一个叶子节点的指针,从而方便叶子节点的范围遍历。

B-Tree索引能加快访问数据的速度,因为存储引擎不再需要进行全表扫描来获取需要的数据,取而代之的是从索引的根节点开始进行搜索。

可以使用B-Tree索引的查询类型:B-Tree索引适用于全键值、键值范围或键前缀查找,其中键前缀查找只使用于根据最左前缀的查找。(比如索引为:last_name first_name dob)

a. 全值匹配:和索引中的所有类按顺序匹配(last_name first_name dob)b. 匹配最左前缀:只使用索引的第一列(last_name)c. 匹配列前缀:匹配某一列的开头部分 last_name like 'a%' 只使用了索引的第一列d. 匹配范围值:last_name in ('Allen','Barrymore')  只使用了索引的第一列e. 精确匹配某一列并范围匹配另外一列:last_name = 'Allen' and first_name like 'K%'f. 只访问索引的查询:查询只需要访问索引,无须访问数据行 覆盖索引 select last_name,first_name,dob from...

因为索引树中的节点是有序的,所以除了按值查找之外,索引还可以用于查询中的ORDER BY操作

B-Tree索引的限制:

a. 如果不是按照索引的最左列开始查找,则无法使用索引 比如first_name dobb. 不能跳过索引中的列 比如 last_name dobc. 如果查询中有某个列的范围查询,则其右边所有列都无法使用索引优化 比如 last_name = 'A' and first_name like 'a%' and dob .. 其中的dob就无法使用索引优化了
  1. 哈希索引 --》不能用于排序(与列的排序规则可能不一致 数字 字符 日期等各不一样) 不支持匹配查找 不支持范围查找 哈希冲突等
DROP TABLE IF EXISTS pseudohash;
CREATE TABLE pseudohash(id int UNSIGNED not NULL auto_increment,url VARCHAR(255) NOT NULL,url_crc int UNSIGNED NOT NULL DEFAULT 0,PRIMARY KEY(id)
);DELIMITER //
CREATE TRIGGER pseudohash_crc_ins BEFORE INSERT ON pseudohash FOR EACH ROW BEGIN
SET NEW.url_crc=crc32(NEW.url);
END;
//
CREATE TRIGGER pseudohash_crc_upd BEFORE UPDATE ON pseudohash FOR EACH ROW BEGIN
SET NEW.url_crc=crc32(NEW.url);
END;
//
DELIMITER ;INSERT INTO pseudohash(url) VALUES ('http://www.mysql.com');
SELECT * FROM pseudohash;UPDATE pseudohash SET url = 'http://www.mysql.com/' WHERE id = 1;
SELECT * FROM pseudohash;

– 创建自定义hash索引
– 间接使用了哈希索引以及处理哈希冲突问题

SELECT * FROM pseudohash WHERE url = 'http://www.mysql.com/' AND CRC32('http://www.mysql.com/');

使用SHA1和MD5作为哈希函数计算出来的哈希值是非常长的字符串,会浪费大量空间,比较时也会慢,SHA1和MD5属于强加密函数,设计目标是最大限度消除冲突。
可以考虑自己实现一个简单的64位哈希函数。

SELECT CONV(RIGHT(MD5('http://www.mysql.com'),16),16,10) AS HASH64;   -- 6715708524825699085
索引的优点

最常见的B-Tree索引,按照顺序存储数据,所以MySQL可以用来做ORDER BY和GROUP BY操作,B-Tree索引中存储了实际的列值,所以某些查询只使用索引就能完成全部查询,总结下来:

1. 索引大大减少了服务器需要扫描的数据量
2. 索引可以帮助服务器避免排序临时表
3. 索引可以将随机I/O变为顺序I/O

索引是最好的解决方案吗?

1. 对于非常小的表,大部分情况下简单的全表扫描更高效
2. 对于中到大型的表,索引就非常有效
3. 对于特大型的表,建立和使用索引的代价就随之增长,在这种情况下,则需要一种技术可以直接区分查询需要的一组数据,而不是一条记录一条记录的匹配,例如可以使用分区技术。

表中的索引越多插入速度会越慢,一般来说,增加索引将会导致INSERT、UPDATE/DELETE等操作的速度变慢,特别是新增索引后导致达到了内存瓶颈的时候。

高性能的索引策略:
  1. 独立的列:索引列不能是表达式的一部分,也不能是函数的参数。
SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;
-- 这个查询语句就无法使用actor_id列的索引。我们应该简化where条件的习惯,始终将索引列单独放在比较符号的一侧。
  1. 前缀索引与索引选择性

有时候需要索引很长的字符列,这会让索引变得大且慢,一个策略是前面提到过的模拟哈希索引。也可以索引开始的部分字符。

索引的选择性是指,不重复的索引值(也称为基数)和数据表的记录总数(#T)的比值,范围从1/#T到1之间。唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。

SELECT COUNT(DISTINCT LEFT(city,3))/COUNT(*) AS sel3,COUNT(DISTINCT LEFT(city,4))/COUNT(*) AS sel4,COUNT(DISTINCT LEFT(city,5))/COUNT(*) AS sel5,COUNT(DISTINCT LEFT(city,6))/COUNT(*) AS sel6,COUNT(DISTINCT LEFT(city,7))/COUNT(*) AS sel7 FROM siksla.city_demo;
-- 通过计算不同的前缀长度的选择值选择合适的前缀长度。
ALTER TABLE sakila.city_demo ADD KEY(city(7));
  1. 多列索引

注意多列索引而不是多个独立的单列索引。注意索引合并策略的风险(大量的资源消耗以及并发性能问题)。在EXPLAIN的Extra中有Using union(PRIMARY,idx_fk_film_id);Using where 代表了使用索引合并的策略

  1. 使用合适的索引列顺序

将选择性最高的列放到索引最前列。

ALTER TABLE payment ADD KEY(customer_id,staff_id)
  1. 聚簇索引 – 同一个结构中保存了B-Tree索引和数据行

术语聚簇表示数据行和相邻的键值紧凑地存储在一起。因为无法同时把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引(不过覆盖索引可以模拟多个聚簇索引的情况)

InnoDB通过主键聚集数据,没有主键则选择一个唯一的非空索引代替,如果没有这样的索引,则会隐式定义一个主键来作为聚簇索引。

聚簇的数据有一些重要的优点:

可以把相关数据保存在一起,例如实现电子邮箱时,可以根据用户ID来聚集数据,这样只需要从磁盘读取少数的数据也就可以获取某个用户的全部邮件。数据访问更快:聚簇索引将索引和数据保存在同一个B-Tree中,因此从聚簇索引中获取数据通常比在非聚簇索引中获取数据要快使用覆盖索引扫描的查询可以直接使用叶节点中的主键值

聚簇索引的一些缺点:

最大限度的提高了I/O密集型应用的性能,但如果数据全部存放在内存中,则访问的顺序就没那么重要了,聚簇索引也就没有什么优势了。插入速度严重依赖于插入顺序,否则导致页分类操作,导致表占用更多的磁盘空间。使用InnoDB时应该尽可能按照主键顺序插入数据,并且尽可能使用单调增加的聚簇键值来插入新行。
顺序的主键什么时候会造成更坏的结果?并发插入可能导致间隙锁竞争、另一个热点可能是AUTO_INCREMENT锁机制。更新聚簇索引列的代价很高,因为会强制InnoDB将每个被更新的行移动到新的位置聚簇索引可能导致**全表扫描变慢**,尤其是行比较稀疏或页分裂导致数据存储不连续的时候二级索引(非聚簇索引)可能比想象的要更大,因为二级索引的叶子节点包含了引用行的主键列,而不是指针...指针。二级索引访问需要两次索引查找,而不是一次。 存储引擎需要找到二级索引的叶子节点对应的主键值,然后根据这个值去聚簇索引中查找对应的行,这里做了重复的工作,两次B-Tree查找而不是一次,除非是覆盖索引,此时主键值包含了全部的信息。覆盖索引==》索引包含或覆盖所有需要查询的字段的值,是非常有用的工具,能极大地提高性能。
CREATE TABLE `rental` (`rental_id` int(11) NOT NULL AUTO_INCREMENT,`rental_date` datetime NOT NULL,`inventory_id` mediumint(8) unsigned NOT NULL,`customer_id` smallint(5) unsigned NOT NULL,`return_date` datetime DEFAULT NULL,`staff_id` tinyint(3) unsigned NOT NULL,`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`rental_id`),    -- 主键索引UNIQUE KEY `rental_date` (`rental_date`,`inventory_id`,`customer_id`),KEY `idx_fk_inventory_id` (`inventory_id`),KEY `idx_fk_customer_id` (`customer_id`),KEY `idx_fk_staff_id` (`staff_id`),CONSTRAINT `fk_rental_customer` FOREIGN KEY (`customer_id`) REFERENCES `customer` (`customer_id`) ON UPDATE CASCADE,CONSTRAINT `fk_rental_inventory` FOREIGN KEY (`inventory_id`) REFERENCES `inventory` (`inventory_id`) ON UPDATE CASCADE,CONSTRAINT `fk_rental_staff` FOREIGN KEY (`staff_id`) REFERENCES `staff` (`staff_id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=16050 DEFAULT CHARSET=utf8
ORDER BY Optimization

​ This section describes when MySQL can use an index to satisfy an ORDER BY clause, the filesort algorithms used when an index cannot be used, and execution plan information available from the
optimizer about ORDER BY.

• Use of Indexes to Satisfy ORDER BY

​ In some cases, MySQL can use an index to satisfy an ORDER BY clause without doing extra sorting.

​ ORDER BY语句可以用到索引来进行排序 与索引完全匹配

​ The index can also be used even if the ORDER BY does not match the index exactly, as long as all
unused portions of the index and all extra ORDER BY columns are constants in the WHERE clause.

​ where部分(常数)+order by(条件) = 索引

​ The following queries use the index to resolve the ORDER BY part:

SELECT * FROM t1 ORDER BY key_part1, key_part2;SELECT * FROM t1 WHERE key_part1 = constant ORDER BY key_part2;SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 DESC;SELECT * FROM t1 WHERE key_part1 = 1 ORDER BY key_part1 DESC, key_part2 DESC;SELECT * FROM t1 WHERE key_part1 > constant ORDER BY key_part1 ASC;
--(MariaDB验证有问题)SELECT * FROM t1 WHERE key_part1 < constant ORDER BY key_part1 DESC;
--(MariaDB验证有问题)SELECT * FROM t1 WHERE key_part1 = constant1 AND key_part2 > constant2 ORDER BY key_part2;

​ In some cases, MySQL cannot use indexes to resolve the ORDER BY, although it may still use indexes
to find the rows that match the WHERE clause. Examples:

  1. The query uses ORDER BY on different indexes:
SELECT * FROM t1 ORDER BY key1, key2;    -- 不同的索引
  1. The query uses ORDER BY on nonconsecutive(不连续的) parts of an index:
SELECT * FROM t1 WHERE key2=constant ORDER BY key_part1, key_part3;
  1. The query mixes ASC and DESC:
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;
  1. The index used to fetch the rows differs from the one used in the ORDER BY
SELECT * FROM t1 WHERE key2=constant ORDER BY key1;
  1. The query uses ORDER BY with an expression that includes terms other than the index column
    name:
SELECT * FROM t1 ORDER BY ABS(key);
SELECT * FROM t1 ORDER BY -key;
  1. The query joins many tables, and the columns in the ORDER BY are not all from the first nonconstant
    table that is used to retrieve rows. (This is the first table in the EXPLAIN output that does not have a
    const join type.)
  2. The query has different ORDER BY and GROUP BY expressions.
  3. There is an index on only a prefix of a column named in the ORDER BY clause. In this case, the index
    cannot be used to fully resolve the sort order. For example, if only the first 10 bytes of a CHAR(20)
    column are indexed, the index cannot distinguish values past the 10th byte and a filesort is
    needed
  4. The index does not store rows in order. For example, this is true for a HASH index in a MEMORY table.

**备注:**GROUP BY会隐式调用到ORDER BY 在不需要排序的情况下 可能会带来一定的性能损耗 因此可以明确限制在GROUP BY时进行排序。

SELECT a, COUNT(*) FROM bar GROUP BY a ORDER BY NULL;

• Optimization Using filesort

​ MySQL has multiple filesort algorithms for sorting and retrieving results. The original algorithm
uses only the ORDER BY columns. The modified algorithm uses not just the ORDER BY columns, but all
columns referenced by the query.

​ The optimizer selects which filesort algorithm to use. It normally uses the modified algorithm
except when BLOB or TEXT columns are involved, in which case it uses the original algorithm. For each
algorithm, the sort buffer size is the sort_buffer_size system variable value.

• The Original filesort Algorithm

• The Modified filesort Algorithm

• Comparison of filesort Algorithms

• Influencing ORDER BY Optimization

​ For slow ORDER BY queries for which filesort is not used, try lowering max_length_for_sort_data to a value that is appropriate to trigger a filesort. – 调节参数触发文件排序

​ To increase ORDER BY speed, check whether you can get MySQL to use indexes rather than an extra
sorting phase. If this is not possible, you can try the following strategies: – 尽量使用索引排序,否则调节参数

  1. Increase the sort_buffer_size variable value. Ideally, the value should be large enough for the
    entire result set to fit in the sort buffer (to avoid writes to disk and merge passes), but at minimum the
    value must be large enough to accommodate fifteen tuples.
  2. Increase the read_rnd_buffer_size variable value.
  3. Use less RAM per row by declaring columns only as large as they need to be to hold the values
    stored in them. For example, CHAR(16) is better than CHAR(200) if values never exceed 16
    characters.
  4. Change the tmpdir system variable to point to a dedicated file system with large amounts of free
    space.

• ORDER BY Execution Plan Information Available

​ With EXPLAIN SELECT … ORDER BY, you can check whether MySQL can use indexes to resolve
the query. It cannot if you see Using filesort in the Extra column. See Section 8.8.1, “Optimizing
Queries with EXPLAIN”. Filesort uses a fixed-length row-storage format similar to that used by the MEMORY storage engine. Variable-length types such as VARCHAR are stored using a fixed length.
​ If a filesort is done, EXPLAIN output includes Using filesort in the Extra column.
​ An ORDER BY with and without LIMIT may return rows in different orders, as discussed in
Section 8.2.1.13, “LIMIT Query Optimization”.

-- 即使order by子句不满足最左前缀的要求 也可用于查询排序 因为第一列被指定为一个常数
MariaDB [sakila]> EXPLAIN select rental_id,staff_id FROM sakila.rental WHERE rental_date = '2005-05-25' ORDER BY inventory_id,customer_id\G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: rentaltype: ref
possible_keys: rental_datekey: rental_datekey_len: 5ref: constrows: 1Extra: Using where
1 row in set (0.00 sec)-- 第一列提供了常量条件 第二列进行排序 两列组合形成了索引的最左前缀
MariaDB [sakila]> EXPLAIN select rental_id,staff_id FROM sakila.rental WHERE rental_date = '2005-05-25' ORDER BY inventory_id\G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: rentaltype: ref
possible_keys: rental_datekey: rental_datekey_len: 5ref: constrows: 1Extra: Using where
1 row in set (0.00 sec)-- 与下一个比较进行区别
MariaDB [sakila]> EXPLAIN select rental_id,staff_id FROM sakila.rental WHERE rental_date < '2005-05-25' ORDER BY rental_date,inventory_id\G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: rentaltype: range
possible_keys: rental_datekey: rental_datekey_len: 5ref: NULLrows: 8Extra: Using index condition
1 row in set (0.00 sec)-- 按道理说order by使用的两列就是索引的最左前缀 但实际上Using filesort 与官方文档有冲突
MariaDB [sakila]> EXPLAIN select rental_id,staff_id FROM sakila.rental WHERE rental_date > '2005-05-25' ORDER BY rental_date,inventory_id\G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: rentaltype: ALL
possible_keys: rental_datekey: NULLkey_len: NULLref: NULLrows: 14986Extra: Using where; Using filesort
1 row in set (0.00 sec)-- 两种不同的排序方向 无法使用索引进行排序
MariaDB [sakila]> EXPLAIN select rental_id,staff_id FROM sakila.rental WHERE rental_date = '2005-05-25' ORDER BY inventory_id desc,customer_id asc\G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: rentaltype: ref
possible_keys: rental_datekey: rental_datekey_len: 5ref: constrows: 1Extra: Using where; Using filesort
1 row in set (0.00 sec)-- order by子句引用一个不在索引中的列
MariaDB [sakila]> EXPLAIN select rental_id,staff_id FROM sakila.rental WHERE rental_date = '2005-05-25' ORDER BY inventory_id,staff_id\G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: rentaltype: ref
possible_keys: rental_datekey: rental_datekey_len: 5ref: constrows: 1Extra: Using where; Using filesort
1 row in set (0.00 sec)-- order by子句不符合最左前缀规则
MariaDB [sakila]> EXPLAIN select rental_id,staff_id FROM sakila.rental WHERE rental_date = '2005-05-25' ORDER BY customer_id\G
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: rentaltype: ref
possible_keys: rental_datekey: rental_datekey_len: 5ref: constrows: 1Extra: Using where; Using filesort
1 row in set (0.00 sec)
  1. 压缩(前缀压缩)索引

MyISAM使用前缀索引来减少索引的大小 默认只压缩字符串,但通过参数设置也可以对整数做压缩

存在perform,则performance则压缩为7,ance类似的形式

压缩块节省空间,代价是某些操作更慢 每个值都依赖前面的值 无法采用二分查找 也不可以倒序扫描

测试表明:对于CPU密集型应用,因为扫描需要随机查找,压缩索引使得MyISAM在索引上查找要慢好几倍,压缩索引的倒序扫描就更慢了,压缩索引需要在CPU内存资源与磁盘之间做好权衡。压缩索引可能只需要十分之一大小的磁盘空间,如果是I/O密集型应用,对某些查询带来的好处比成本多很多。

7.冗余和重复索引

重复索引指在相同的列上按相同的顺序创建的相同类型的索引。应该避免这样创建重复索引,发现之后应该立即删除。

冗余索引和重复索引有些不同,如果创建了索引(A,B),再创建索引A就是冗余索引,因为这是前一个索引的前缀索引。

索引和锁

索引–》查询锁定更少的行–》索引需要额外开销、锁定超过需要的行增加锁争用和减少并发性

SELECT * FROM actor WHERE actor_id < 5 AND actor_id <> 1 FOR UPDATE;-- 上一条语句在mysql5.7中锁定了actor_id 为 1 2 3 4的4条数据 而在mariadb中只锁定了 2 3 4的3条数据
优化分页排序

对于选择性非常低的列,可以增加一些特殊的索引来做排序

SELECT <cols>  FROM profiles WHERE sex = 'M' ORDER BY rating LIMIT 10;

这个查询同时使用了ORDER BY 和 LIMIT,如果没有索引的话会很慢。

即使有索引,如果用户界面上需要翻页,并且翻页翻到比较靠后时查询也可能非常慢。

SELECT <cols>  FROM profiles WHERE sex = 'M' ORDER BY rating LIMIT 10000,10;

因为随着偏移量的增加,MySQL需要花费大量的时间来扫描需要丢弃的数据。

反范式化、预先计算和缓存可能是解决这类查询的仅有策略

一个更好的办法是限制用户能翻页的数量

另一个更好的办法是是用延迟关联,通过使用覆盖索引返回需要的主键,在根据这些主键关联原表需要获得的行,这样可以减少MySQL扫描那些需要丢弃的行数。

SELECT <cols>  FROM profiles INNER JOIN
(SELECT <primary key cols> FROM profilesWHERE x.sex = 'M' ORDER BY rating LIMIT 10000,10
) AS x USING(<primary key cols>);
索引的碎片化

行碎片(ROW fragmentation)、行间碎片(Intra-row fragmentation)、剩余空间碎片(Free Space …)

  1. 通过执行OPTIMIZE TABLE或者导出再导入的方式来重新整理数据
  2. 通过先删除,然后再重新创建索引的方式来清除索引的碎片化
  3. 不支持OPTIMIZE TABLE的存储引擎,可以同一个不做任何操作的ALTER TABLE操作来重建表,只需要将表的存储引擎修改为当前的存储引擎
ALTER TABLE <table> ENGINE=<engine>

MariaDB [sakila]> show create table address\G;
*************************** 1. row ***************************Table: address
Create Table: CREATE TABLE `address` (`address_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,`address` varchar(50) NOT NULL,`address2` varchar(50) DEFAULT NULL,`district` varchar(20) NOT NULL,`city_id` smallint(5) unsigned NOT NULL,`postal_code` varchar(10) DEFAULT NULL,`phone` varchar(20) NOT NULL,`location` geometry NOT NULL,`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`address_id`),KEY `idx_fk_city_id` (`city_id`),CONSTRAINT `fk_address_city` FOREIGN KEY (`city_id`) REFERENCES `city` (`city_id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=606 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)MariaDB [sakila]> optimize table address\G;
*************************** 1. row ***************************Table: sakila.addressOp: optimize
Msg_type: note
Msg_text: Table does not support optimize, doing recreate + analyze instead
*************************** 2. row ***************************Table: sakila.addressOp: optimize
Msg_type: status
Msg_text: OK
2 rows in set (0.20 sec)MariaDB [sakila]> analyze table address\G;
*************************** 1. row ***************************Table: sakila.addressOp: analyze
Msg_type: status
Msg_text: OK
1 row in set (0.01 sec)MariaDB [sakila]> alter table address engine = innodb\G;
Query OK, 0 rows affected (0.18 sec)
Records: 0  Duplicates: 0  Warnings: 0
总结

在选择索引和编写利用这些索引的查询时,有如下三个原则需要始终记住:

  1. 单行访问时很慢的
  2. 按顺序访问数据是最快的
  3. 索引覆盖查询是很快的

总的来说,编写查询语句应该竟可能选择合适的索引避免单行查找,尽可能使用数据原生顺序从而避免额外的排序操作,并尽可能使用索引覆盖查询。

查询性能优化
分析查询的成本
SELECT SQL_NO_CACHE COUNT(*) FROM film_actor;SHOW STATUS LIKE 'Last_query_cost';
MariaDB [sakila]> EXPLAIN SELECT film.film_id, film.title, film.release_year, actor.actor_id, actor.first_name, actor.last_name FROM film INNER JOIN film_actor USING (film_id) INNER JOIN actor USING (actor_id)\G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: filmtype: ALL
possible_keys: PRIMARYkey: NULLkey_len: NULLref: NULLrows: 949Extra:
*************************** 2. row ***************************id: 1select_type: SIMPLEtable: film_actortype: ref
possible_keys: PRIMARY,idx_fk_film_idkey: idx_fk_film_idkey_len: 2ref: sakila.film.film_idrows: 2Extra: Using index
*************************** 3. row ***************************id: 1select_type: SIMPLEtable: actortype: eq_ref
possible_keys: PRIMARYkey: PRIMARYkey_len: 2ref: sakila.film_actor.actor_idrows: 1Extra:
3 rows in set (0.00 sec)ERROR: No query specifiedMariaDB [sakila]> SHOW STATUS LIKE 'Last_query_cost'\G;
*************************** 1. row ***************************
Variable_name: Last_query_costValue: 3807.693560
1 row in set (0.00 sec)ERROR: No query specifiedMariaDB [sakila]> EXPLAIN SELECT STRAIGHT_JOIN  film.film_id, film.title, film.release_year, actor.actor_id, actor.first_name, actor.last_name FROM film INNER JOIN film_actor USING (film_id) INNER JOIN actor USING (actor_id)\G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: filmtype: ALL
possible_keys: PRIMARYkey: NULLkey_len: NULLref: NULLrows: 949Extra:
*************************** 2. row ***************************id: 1select_type: SIMPLEtable: film_actortype: ref
possible_keys: PRIMARY,idx_fk_film_idkey: idx_fk_film_idkey_len: 2ref: sakila.film.film_idrows: 2Extra: Using index
*************************** 3. row ***************************id: 1select_type: SIMPLEtable: actortype: eq_ref
possible_keys: PRIMARYkey: PRIMARYkey_len: 2ref: sakila.film_actor.actor_idrows: 1Extra:
3 rows in set (0.00 sec)ERROR: No query specifiedMariaDB [sakila]> SHOW STATUS LIKE 'Last_query_cost'\G;
*************************** 1. row ***************************
Variable_name: Last_query_costValue: 3807.693560
1 row in set (0.00 sec)ERROR: No query specified
-- 如果对某个查询执行EXPLAIN EXTENDED后,在执行SHOW WARNINGS可以看到执行计划重构后的查询EXPLAIN EXTENDED SELECT film.film_id,film_actor.actor_id FROM film INNER JOIN film_actor USING(film_id) WHERE film.film_id = 1;SHOW WARNINGS;select 1 AS `film_id`,`sakila`.`film_actor`.`actor_id` AS `actor_id` from `sakila`.`film` join `sakila`.`film_actor` where (`sakila`.`film_actor`.`film_id` = 1)
MySQL排序算法

两次传输排序(旧版本使用):一次顺序I/O(行指针和排序字段)和一次随机I/O(所需数据行)

单次传输排序(新版本使用):只需要一次顺序I/O (所需数据行) 如果返回数据列非常多 会额外占用大量的空间

当查询需要所有列的总长度不超过max_length_for_sort_data时,MySQL使用单次传输排序,可以通过这个参数来影响MySQL排序算法的选择。

MySQL进行文件排序所需要的临时存储空间会比想象大很多。

-- show_profiles.sqlSET @query_id = 1;SELECT STATE, SUM(DURATION) AS Total_R,ROUND(100 * SUM(DURATION) /(SELECT SUM(DURATION)FROM INFORMATION_SCHEMA.PROFILINGWHERE QUERY_ID = @query_id), 2) AS Pct_R,COUNT(*) AS Calls,SUM(DURATION) / COUNT(*) AS "R/Call"
FROM INFORMATION_SCHEMA.PROFILING
WHERE QUERY_ID = @query_id
GROUP BY STATE
ORDER BY Total_R DESC;

MariaDB [(none)]> use sakila
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -ADatabase changed
MariaDB [sakila]> set @curr_cnt := 0,@prev_cnt := 0,@rank := 0;
Query OK, 0 rows affected (0.00 sec)MariaDB [sakila]> SELECT actor_id,@curr_cnt := cnt as cnt,@rank := IF(@prev_cnt <> @curr_cnt,@rank+1,@rank) AS rank,@prev_cnt := @curr_cnt AS dummy FROM (SELECT actor_id,COUNT(*) AS cnt FROM film_actor GROUP BY actor_id  ORDER BY cnt DESC LIMIT 10) AS der;
+----------+-----+------+-------+
| actor_id | cnt | rank | dummy |
+----------+-----+------+-------+
|      107 |  42 |    1 |    42 |
|      102 |  41 |    2 |    41 |
|      198 |  40 |    3 |    40 |
|      181 |  39 |    4 |    39 |
|       23 |  37 |    5 |    37 |
|       81 |  36 |    6 |    36 |
|       37 |  35 |    7 |    35 |
|      106 |  35 |    7 |    35 |
|       60 |  35 |    7 |    35 |
|       13 |  35 |    7 |    35 |
+----------+-----+------+-------+
10 rows in set (0.07 sec)
使用临时表算法实现视图
MariaDB [world]> CREATE ALGORITHM=TEMPTABLE VIEW Oceania AS SELECT * FROM country WHERE Continent = 'Oceania'\G;
Query OK, 0 rows affected (0.05 sec)ERROR: No query specifiedMariaDB [world]> EXPLAIN SELECT * FROM Oceania\G;
*************************** 1. row ***************************id: 1select_type: PRIMARYtable: <derived2>type: ALL
possible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: 239Extra:
*************************** 2. row ***************************id: 2select_type: DERIVEDtable: countrytype: ALL
possible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: 239Extra: Using where
2 rows in set (0.00 sec)ERROR: No query specified
使用合并算法实现视图
MariaDB [world]> CREATE OR REPLACE VIEW Oceania AS SELECT * FROM country WHERE Continent = 'Oceania'\G;
Query OK, 0 rows affected (0.00 sec)MariaDB [world]> CREATE OR REPLACE ALGORITHM=MERGE VIEW Oceania AS SELECT * FROM country WHERE Continent = 'Oceania'\G;
Query OK, 0 rows affected (0.01 sec)MariaDB [world]> EXPLAIN SELECT * FROM Oceania\G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: countrytype: ALL
possible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: 239Extra: Using where
1 row in set (0.00 sec)
字符集和校对规则
MariaDB [(none)]> CREATE DATABASE d CHARSET latin1;
Query OK, 1 row affected (0.00 sec)MariaDB [(none)]> CREATE TABLE d.t(col1 CHAR(1),col2 CHAR(1) CHARSET utf8,col3 CHAR(1) COLLATE latin1_bin) DEFAULT CHARSET=cp1251;
Query OK, 0 rows affected (0.07 sec)MariaDB [(none)]> SHOW FULL COLUMNS FROM d.t;
+-------+---------+-------------------+------+-----+---------+
| Field | Type    | Collation         | Null | Key | Default |
+-------+---------+-------------------+------+-----+---------+
| col1  | char(1) | cp1251_general_ci | YES  |     | NULL    |
| col2  | char(1) | utf8_general_ci   | YES  |     | NULL    |
| col3  | char(1) | latin1_bin        | YES  |     | NULL    |
+-------+---------+-------------------+------+-----+---------+
3 rows in set (0.01 sec)
MariaDB [(none)]> CREATE TABLE d.big_string(str VARCHAR(500),key(str)) DEFAULT CHARSET=utf8;
Query OK, 0 rows affected, 1 warning (0.07 sec)MariaDB [(none)]> SHOW WARNINGS;
+---------+------+---------------------------------------------------------+
| Level   | Code | Message                                                 |
+---------+------+---------------------------------------------------------+
| Warning | 1071 | Specified key was too long; max key length is 767 bytes |
+---------+------+---------------------------------------------------------+
1 row in set (0.00 sec)MariaDB [(none)]> SHOW CREATE TABLE d.big_string\G;
*************************** 1. row ***************************Table: big_string
Create Table: CREATE TABLE `big_string` (`str` varchar(500) DEFAULT NULL,KEY `str` (`str`(255))   -- 仅仅在该列的前缀上建立了索引
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
全文索引
MariaDB [sakila]> show create table film_text\G;
*************************** 1. row ***************************Table: film_text
Create Table: CREATE TABLE `film_text` (`film_id` smallint(6) NOT NULL,`title` varchar(255) NOT NULL,`description` text,PRIMARY KEY (`film_id`),FULLTEXT KEY `idx_title_description` (`title`,`description`) -- 创建全文索引
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)MariaDB [sakila]> show index from film_text;
+-----------+------------+-----------------------+--------------+-------------+------------+
| Table     | Non_unique | Key_name              | Seq_in_index | Column_name | Index_type |
+-----------+------------+-----------------------+--------------+-------------+------------+
| film_text |          0 | PRIMARY               |            1 | film_id     | BTREE      |
| film_text |          1 | idx_title_description |            1 | title       | FULLTEXT   |
| film_text |          1 | idx_title_description |            2 | description | FULLTEXT   |
+-----------+------------+-----------------------+--------------+-------------+------------+
3 rows in set (0.00 sec)-- 查询相关度大于5
MariaDB [(none)]> SELECT film_id,title,RIGHT(description,25),MATCH(title,description) AGAINST('factory casualties') as relevance FROM sakila.film_text WHERE MATCH(title,description) AGAINST('factory casualties') > 5;
+---------+-----------------------+---------------------------+-------------------+
| film_id | title                 | RIGHT(description,25)     | relevance         |
+---------+-----------------------+---------------------------+-------------------+
|     831 | SPIRITED CASUALTIES   | a Car in A Baloon Factory | 8.640907287597656 |
|     126 | CASUALTIES ENCINO     | Face a Boy in A Monastery | 6.364917278289795 |
|     193 | CROSSROADS CASUALTIES | a Composer in The Outback | 6.364917278289795 |
+---------+-----------------------+---------------------------+-------------------+-- 返回结果必须包含“factory 和 casualties”  布尔全文索引
MariaDB [sakila]> SELECT film_id,title,RIGHT(description,25) FROM sakila.film_text WHERE MATCH(title,description) AGAINST('+factory +casualties' IN BOOLEAN MODE);
+---------+---------------------+---------------------------+
| film_id | title               | RIGHT(description,25)     |
+---------+---------------------+---------------------------+
|     831 | SPIRITED CASUALTIES | a Car in A Baloon Factory |
+---------+---------------------+---------------------------+
1 row in set (0.01 sec)
优化count查询

count()是一个特殊的函数,有两个不同的作用:它可以统计某个列值的数量,另一个作用是统计结果值的行数。一个常见的错误是,在括号内指定了一个列却希望统计结果集的行数,如果希望知道的是结果集的行数,最好使用count(*).

通常来说,count()都需要扫描大量的行,才能获取精确的结果。优化措施包括索引覆盖或者增加汇总表

优化关联查询

确保ON或者USING子句中的列上有索引。

确保任何的group by和order by中的表达式只涉及到一个表中的列,这样mysql才可能使用索引来优化这个过程

优化子查询

尽可能使用关联查询替代,但是如果使用的是mysql 5.6或更新的版本或者mariadb,就可以忽略这个建议了

优化group by和distinct
  1. 使用索引来优化 这也是最有效的优化方法

  2. 使用索引表还是磁盘临时表 。在mysql中,无法使用索引的时候,group by使用两种策略来优化:使用临时表或者文件排序来做分组,对于任何查询语句,这两种策略的性能都有可以提升的地方,可以通过使用SQL_BIG_RESULT和SQL_SMALL_RESULT来让优化器按照你希望的方式运行。

  3. 在分组查询的SELECT中直接使用非分组列通常都不是好主意,将mysql的sql_mode设置为包含only_full_group_by,这时myslq会对这类查询直接返回一个错误,提醒你需要重写这个查询。

  4. 查询使用group by子句的时候,结果集会自动按照分组的字段进行排序,可以使用order by null,让mysql不再进行文件排序。

  5. 也可以在group by子句中直接使用desc或者asc关键字,使分组的结果集按需要的方向排序

优化limit分页
  1. 索引
  2. limit 10000,20这样的查询,mysql需要查询10020条记录然后只返回最后20条,前面的10000条都会被抛弃,这样的代价非常很高,优化这种查询,在分页中限制分页的数量,优化大偏移量的性能,可以考虑转化为范围扫描,比如where position between 50 and 54 order by position
  3. 尽可能地使用索引覆盖查询 通过索引覆盖扫描获取有效主键在根据关联列回表查询需要的行。

MariaDB [sakila]> EXPLAIN SELECT film.film_id,film.description FROM film ORDER BY title LIMIT 50,5\G;
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: filmtype: ALL
possible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: 949Extra: Using filesort
1 row in set (0.00 sec)MariaDB [sakila]> EXPLAIN SELECT film.film_id,film.description FROM film INNER JOIN ( SELECT film_id FROM film ORDER BY title LIMIT 50,5) AS lim USING(film_id)\G;
*************************** 1. row ***************************id: 1select_type: PRIMARYtable: <derived2>type: ALL
possible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: 55Extra:
*************************** 2. row ***************************id: 1select_type: PRIMARYtable: filmtype: eq_ref
possible_keys: PRIMARYkey: PRIMARYkey_len: 2ref: lim.film_idrows: 1Extra:
*************************** 3. row ***************************id: 2select_type: DERIVEDtable: filmtype: index
possible_keys: NULLkey: idx_titlekey_len: 767ref: NULLrows: 55Extra: Using index
3 rows in set (0.00 sec)
  1. 预先计算的汇总表
  2. 关联一个冗余表,冗余表只包含主键列和需要做排序的数据列
优化union查询
  1. mysql总是通过创建并填充临时表的方式来执行union查询,经常需要手工地将where、limit、order by等子句“下推”到union的各个子查询中以便优化器充分利用条件进行优化
  2. 除非确实需要服务器消除重复的行,否则就一定要使用UNION ALL,这一点很重要。如果没有all关键字,mysql会给临时表加上distinct选项,这会导致对整个临时表的数据做唯一性检查。
select for update优化

考虑cas的方式,用一个状态位,修改成功的再进行业务处理。尽可能快地完成需要做的事情,尽量使用update代替select fro update再update的写法,因为事务提交的速度越快,持有的锁时间就越短,可以大大减少竞争和加锁串行执行效率

外键

外键限制会将约束放在mysql中,这对于必须维护外键的场景,性能会更高。不过这也会带来额外的复杂性和额外的索引消耗,还会增加多表之间的交互,会导致系统中更多的锁和竞争,外键可以看作是一个确保系统完整性的额外的特性,但是如果设计的是一个高性能的系统,那么外键就显得很臃肿了,很多人在更在意系统的性能的时候不会使用外键,而是通过应用程序来维护。

字符集

字符集是一种字节到字符之间的映射,而校对规则是指一个字符集的排序方式,很多人都是Latin1(默认字符集,对于英语或者某些欧洲语言有效)或者UTF-8。如果使用的是UTF-8,那么在使用临时表和缓冲区的时候需要注意:mysql会按照每个字符三个字节的最大占用空间来分配存储空间,这可能消耗更多的内存或者磁盘空间。注意让字符集和mysql字符集配置相符,否则可能由于字符集转换让某些索引无法正常使用。

XA事务

很少有人用mysq的xa事务特性,除非你真正明白innodb_support_xa的意义,否则不要修改这个参数的值,并不是只有显式使用xa事务才需要设置这个参数,innodb和二进制日志也是需要使用xa事务来做协调的,从而确保在系统崩溃的时候,数据能够一致地恢复。

查询缓存

mysql判断查询缓存命中的方法很简单:缓存存放在一个引用表中,通过一个hash值引用,这个hash包括了如下因素,即查询本身、当前要查询的数据库、客户端协议的版本等一些其他可能会影响结果的信息。

  1. 查询语句包含一些不确定的数据时,则不会被缓存,比如函数now()或者current_date()、
  2. 根据不同的用户返回不同的结果的也不会被缓存,比如 connetion_id()
  3. 其他用户自定义函数、存储函数、用户变量、临时表、mysql库中的系统表,或者任何包含列级别权限的表,都不会被缓存

对于写密集型的应用来说,直接禁用查询缓存可能会提高系统的性能,关闭查询缓存可以移除所有的消耗,例如将query_cache_size设置为0,那么至少这本分就不再消耗任何内存了

查询缓存的替换方案:客户端缓存

完全相同的查询在重复执行的时候,查询缓存可以立即返回结果,而无须在数据库重复执行一次,根据我们的经验,在高并发压力环境中查询缓存会导致系统性能的下降,甚至僵死。如果你一定要使用查询缓存,那么不要设置太大内存,而且只有在明确的收益的时候才使用。查询缓存是一个非常方便的缓存,对应用程序完全透明,无须任何额外的编码,但是希望有更高的缓存效率,建议使用redis等类似的解决方案。

优化服务器配置
  1. mysql有大量可以修改的参数,通常只需要把基本的项配置正确,应该把更多的时间花在schema的优化,索引以及查询设计上、
  2. 如果在服务器运行时修改了变量的全局值,这个值对当前会话和其他任何已经存在的会话都不起作用,这是因为会话的变量值是在连接创建时从全局值初始化来的。
mysql配置文件
root@cb5fc9448a72:/# which mysqld
/usr/sbin/mysqld
root@cb5fc9448a72:/# /usr/sbin/mysqld --verbose --help | grep -A 1 'Default options'
2019-12-06  7:48:50 140359083845632 [Note] Plugin 'FEEDBACK' is disabled.
Default options are read from the following files in the given order:
/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf
[client]
port            = 3306
socket          = /var/run/mysqld/mysqld.sock[mysqld]
# GENERAL
user           = mysql
# 明确指定这些文件的存放地点,这样升级MYSQL时这些路径就不会改变
datadir         = /var/lib/mysql
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
port            = 3306
# InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/.
# Read the manual for more InnoDB related options. There are many!
default_storage_engine  = InnoDB# INNODB
# 缓冲池大小一般设置为服务器内存的75%-80% 内存总量-操作系统内存占用-msyql自身内存-mysql缓冲和缓存内存
# InnoDB缓冲池不仅仅缓存索引:它还会缓存行的数据、自适应哈希索引、插入缓冲、锁以及其他内存数据结构
# InnoDB严重依赖缓冲池
# 很大的缓冲池也会带来挑战,预热和关闭都会话费很长的时间,如果有很多脏页在缓冲池里,InnoDB关闭时会花费较长时# 间
innodb_buffer_pool_size = 1024M
# 事务日志文件
# InnoDB使用日志来减少提交事务的开销,因为日志中已经记录了事务,就无需在每个事务提交时把缓冲池的脏块刷新到磁# 盘中。因为事务修改的数据和索引通常会映射到表空间的随机位置,所以刷新这些变更到磁盘需要很多随机I/O.
# InnoDB用事务日志把随机I/O变成顺序I/O,一旦事务日志安全写到磁盘,事务就持久化了,即使变更没有写到数据文件
# 因为即使断电了,InnoDB可以重放日志并且恢复已提交的日志
# InnoDB的事务日志是环形方式写的,当写到日志的尾部,会重新跳转到开头继续写,但不会覆盖还没有应用到数据文件
# 的日志记录,因为这样会清掉已提交事务的唯一持久化记录
# 在日志缓冲满的时候、事务提交的时候或者每一秒钟,InnoDB都会刷新缓冲区的内容到磁盘日志文件
innodb_log_buffer_size  = 8M
# InnoDB为每张表使用一个文件 即使打开此选项 依然要为回滚资质和其他系统数据创建共享表空间
innodb_file_per_table   = 1
innodb_flush_method     = O_DIRECT# MYISAM
# MyISAM的键缓存也被称为键缓冲,默认只有一个键缓存,但也可以创建多个.MyISAM自身只缓存索引,不缓存数据,依赖# 操作系统缓存数据,如果大部分是MyISAM表,就应该为键缓存分配比较多的空间
# 即使没有任何MyISAM表,依然需要将key_buffer_size设置为较小的值,例如32M,MYSQL服务器内部使用MyISAM表
# 例如GROUP BY语句可能会使用MYISAM做临时表
key_buffer_size         = 64M#LOGGING
slow_query_log = 1
slow_query_log_file     = /var/log/mysql/mariadb-slow.log
long_query_time = 1
log_error               = /var/log/mysql/mysql-error.log#OTHER
# 1.BLOB值有几个限制使得服务器对它的处理跟其他类型不一样,服务器不能在内存临时表中存储BLOB值,因此一个查询
# 涉及到BLOB值,又需要使用临时表-不管它多小-它都会立即在磁盘上创建临时表,这样效率很低,尤其是对小而快的查询
# 2.如果一张表中有很多大字段,最好是把它们组合起来单独存在一个列里面,比如说用XML文档格式存储,这让所有的大字# 段共享一个扩展存储空间,这比每个字段用自己的页要好
# 3. MYSQL有两种排序算法,如果查询中所有的列和ORDER BY的列总大小超过max_length_for_sort_data字节,则
# 采用tow-pass算法,或者当任何需要的列-即使没有被ORDER BY使用的列-是BLOB或者TEXT,也会采用这个算法
# 当mysql必须排序BLOB或TEXT字段时,只会使用前缀,然后忽略剩下部分的值,这是因为缓冲只能分配固定大小的结构体# 来保存要排序的值,然后从扩展存储空间中复制前缀到这个结构体中,使用max_sort_length变量可以指定这个前缀
# 有多大
tmpdir          = /tmp
# 指定Memory引擎的内存临时表的大小 如果隐式内存临时表的大小超过了这两个设置的值 将会转换为磁盘MyISAM表
# 临时表最好呆在内存里,但是如果它们被撑得很大,实际上还是让它们使用磁盘比较好,否则可能会让服务器内存溢出
tmp_table_size          = 32M
max_heap_table_size     = 32M
query_cache_size                = 0
# for more write intensive setups, set to DEMAND or OFF
query_cache_type                = OFF
max_connections         = 100
connect_timeout         = 10
wait_timeout            = 28800
max_allowed_packet      = 16M
thread_cache_size       = 128
open-files-limit        = 65535innodb_open_files       = 400
innodb_io_capacity      = 400
basedir         = /usrlc_messages_dir = /usr/share/mysql
lc_messages     = en_US
# 设置key_buffer_size 参考如下返回值
select SUM(INDEX_LENGTH) from information_schema.`TABLES` WHERE `ENGINE` = 'MYISAM';
-- 2670592
操作系统与磁盘优化

配置大量内存最大的原因其实不是因为可以在内存中保存大量数据:最终目的是避免磁盘I/O,因为磁盘I/O比在内存中访问数据要慢得多,关键是在平衡内存和磁盘的大小、速度、成本和其他因素,以便为工作负载提供高性能的表现。

随机I/O和顺序I/O

数据库服务同时使用随机I/O和顺序I/O。随机I/O从缓存中受益良多,缓存这些数据有助于避免昂贵的磁盘寻道。相反,顺序读取一般只需要扫描一次数据,所以缓存对它是没用的,除非能完全在内存中缓存起来。

顺序读取不能从缓存中受益的另一个原因是它们比随机读快。

  1. 顺序I/O比随机I/O快 顺序读是随机读速度的5000倍,内存中顺序访问只有磁盘中顺序访问的10倍速度
  2. 存储引擎执行顺序读比随机快
  3. 随机读取通常只要查找特定的行,但不仅仅是只读取一行-而是要读取一整页的数据,其中大部分是不需要的,顺序读取数据,通常发生在想要的页面上的所有行,所以更符合成本效益。
缓存与读和写

缓存除了允许写入被延迟,还可以允许它们被集中操作

  1. 多次写入,一次刷新
  2. I/O合并

这也是为什么需要交易系统使用预写日志策略。预写日志采用在内存中变更页面,而不是马上刷新到磁盘上的策略,因为刷新磁盘需要随机I/O,这非常慢,相反,如果把变化的记录写到一个连续的日志文件,这就很快了。后台线程可以稍后把修改的页面刷新到磁盘,并在刷新过程中优化写操作。

写入从缓冲中大大受益,因为它把随机I/O更多地转换为连续I/O。异步缓冲写通常是由操作系统批量处理,使它们能以更优化的方式刷新到磁盘。同步无缓冲写必须在写入到磁盘之后才能完成。

多种类型文件

mysql创建了多种累心的文件

  1. 数据和索引文件
  2. 事务日志文件
  3. 二进制日志文件==》复制
  4. 常规日志==》错误日志 慢查询日志
  5. 临时文件和临时表
网络配置

就像延迟和吞吐量是硬盘驱动器的限制因素一样,延迟和带宽(实际上和吞吐量是同一回事)也是网络连接的限制因素,对于大多数应用程序来说,最大的问题是延迟。典型的应用程序都需要传输很多很小的网络包,并且每次传输的轻微延迟最终会被累加起来。

运行不正常的网络通常也是主要的性能瓶颈之一,丢包是一个普遍存在的问题。即使1%的丢包率也足以造成显著的性能下降,因为在协议栈的各个层次都会利用各种策略尝试修复问题,例如等待一段时间再重新发送数据包,这就增加了额外的时间,另一个常见的问题是域名解析系统(DNS)损坏或者变慢了。

DNS足以成为阿基里斯之踵,因此在生产服务器上启用skip_name_resolve是个好主意,损坏或缓慢的DNS解析对许多应用程序都是个问题,对MYSQL尤为严重。

如果启用skip_name_resolve选项,MySQL将不会做任何DNS查找的工作,然而,这也意味着,用户账户必须在host列使用具有唯一性的IP地址,“localhost”或者IP地址通配符。

典型的WEB应用的另一个常见的问题来源是TCP积压,可以通过MySQL的back_log选项来配置,这个选项控制MySQL的传入TCP连接队列的大小,在每秒有很多连接创建和销毁的环境中,默认值50是不够的。

# 改变本地端口的范围:
[root@self ~]# cat /proc/sys/net/ipv4/ip_local_port_range
9000    65500
[root@self ~]# echo 1024 65535 > /proc/sys/net/ipv4/ip_local_port_range
[root@self ~]# cat /proc/sys/net/ipv4/ip_local_port_range
1024    65535
# 允许更多的连接进入队列
[root@self ~]# cat /proc/sys/net/ipv4/tcp_max_syn_backlog
256
[root@self ~]# echo 4096 > /proc/sys/net/ipv4/tcp_max_syn_backlog
[root@self ~]# cat /proc/sys/net/ipv4/tcp_max_syn_backlog
4096
内存交换区

当操作系统因为没有足够的内存而将一些虚拟内存写到磁盘就会发生内存交换。内存交换对操作系统中运行的进程是透明的,只有操作系统知道特定的虚拟内存地址是在物理内存还是在磁盘。

内存交换对MySQL性能印象是很糟糕的。它破坏了缓存在内存中的目的,而且相对于使用很小的内存在缓存,使用交换区的性能更差,MySQL和存储引擎有很多算法来区别对待内存的数据磁盘上的数据,因为一般都假设内存数据访问代价更低。

因为内存交换对用户进程不可见,MYSQL并不知道数据实际上应移动到瓷片,还会以为在内存中。

在linux上可以用vmstate来监控内存交换,最好查看si和so列的值为0,并且一定要保证它们低于每秒10个块。绝不要让系统的虚拟内存溢出,对交换空间利用率做好监控和报警。

复制

mysql支持两种复制方式:基于行的复制和基于语句的复制(逻辑复制)。这两种方式都是通过在主库上记录二进制日志、在备库重放日志的方式来实现异步的数据复制。这意味着,在同一时间点备库上的数据可能与主库存在不一致,并且无法保证主备之间的延迟。一些大的语句可能导致备库产生几秒、几分钟甚至几个小时的延迟。

复制通常不会增加主库的开销,主要是启动二进制日志带来的开销,但出于备份或者及时从崩溃中恢复的目的,这点开销是必要的。除此之外,每个备库也会对主库增加一些负载(例如网络I/O开销),尤其当备库请求从主库读取旧的二进制文件时,可能会造成更高的I/O开销、另外锁竞争也会阻碍事务的提交。

复制解决的问题:
  1. 数据分布
  2. 负载均衡:通过mysql复制将读操作分布到多个服务器上,实现对读密集型应用的优化
  3. 备份
  4. 高可用性和故障切换
  5. mysql升级测试:使用一个更高版本的mysql作为备库,保证在升级全部实例前,查询能够在备库中按照预期执行
复制的步骤
  1. 主库上记录二进制文件,在每次准备提交事务完成数据更新前,主库将数据更新的事件记录到二进制日志中(按事务提交的顺序而非每条语句的执行顺序)
  2. 备库将主库的二进制日志复制到其本地的中继日志中。首先备库启动一个I/O线程跟主库建立一个普通的客户端连接,然后主库启动一个特殊的二进制转储线程,读取主库上二进制日志中的文件。备库I/O线程会将接受到的事件记录到中继日志中
  3. 备库的SQL线程执行最后一步,从中继日志中读取事件并在备库执行,从而实现备库数据的更新

这种复制的的架构实现了获取事件和重放事件的解耦。

可扩展的mysql
可扩展性

性能–》相应时间

可扩展性表明了当需要增加资源以执行更多工作时系统能够获得划算的等同提升(equal bang for the buck)的能力。缺乏扩展能力的系统在达到收益递减的转折点后,将无法进一步增长。

容量是一个和可扩展性相关的概念,系统容量表示在一定时间内能够完成的工作量,但容量必须是可以有效利用的。系统的最大吞吐量并不等同于容量。

容量和可扩展性并不依赖于性能。以高速公路上的汽车来类比的话:

  1. 性能是汽车的时速
  2. 容量时车道数乘以最大安全时速
  3. 可扩展性就是在不减少交通的情况下,能增加更多车和车道的程序

从较高层次看,可扩展性就是能够通过增加资源来提升容量的能力。

容量也可以简单地认为是处理负载的能力,从不同的角度来考虑负载很有帮助

  1. 数据量 : 应用所能累积的数据量是可扩展性最普遍的挑战
  2. 用户量:即使每个用户只有少量的数据,但是累积到一定数量的用户后,数据量也会开始不成比例增长
  3. 用户活跃度:用户突然变得活跃,负载可能会明显提升
  4. 相关数据集的大小:用户之间存在关系,可能在整个相关联用户群体上执行查询和计算
扩展mysql

对于许多类型的应用,传统的解决方法就是购买更多强悍的即期,也就是常说的垂直扩展或者向上扩展。另外一个与之相反的方法是将任务分配到多台计算机上,这通常被称为水平扩展或者向外扩展。

可以将向外扩展(也称横向扩展或水平扩展)策略划分为是三个部分:复制、拆分以及数据分片

Mysql Cluster ( NDB Cluster)

Mysql Cluster是两项技术的结合:NDB数据库,以及作为SQL前端的mysql存储引擎。NDB是一个分布式、具备容错性、非共享的数据库,提供同步复制以及节点间的数据自动分片。

负载均衡

负载均衡的基本思路很简单,在一个服务器集群中尽可能地平均负载量,通常的做法是在服务器前端设置一个负载均衡器(一般是专用的硬件设备)。然后负载均衡器将请求的连接路由到最空闲的可用服务器。

负载均衡有五个常见目的:

  1. 可扩展性
  2. 高效性:有利与更有效的使用资源
  3. 可用性
  4. 透明性
  5. 一致性

最普遍的策略是使用硬件负载均衡,大多是使用HAProxy,它看起来很流行工作得很好。还有一些人使用TCP代理,但Mysql Proxy用得并不多。

[root@self ~]# top
top - 16:48:57 up  7:33,  3 users,  load average: 0.83, 0.54, 0.27
Tasks: 160 total,   1 running, 159 sleeping,   0 stopped,   0 zombie
%Cpu(s):  3.8 us,  5.4 sy,  0.0 ni, 79.9 id,  1.7 wa,  0.0 hi,  9.2 si,  0.0 st
KiB Mem :  7910932 total,  3675540 free,   718728 used,  3516664 buff/cache
KiB Swap:  4063228 total,  4063228 free,        0 used.  5905208 avail MemPID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
27313 polkitd   20   0 1797036 261344  11096 S  37.5  3.3   1:24.90 mysqld
27792 root      20   0       0      0      0 S   7.6  0.0   0:01.64 kworker/0:01326 root       0 -20       0      0      0 S   3.7  0.0   0:37.88 kworker/0:1H
select SUM(INDEX_LENGTH) from information_schema.`TABLES` WHERE `ENGINE` = 'MYISAM';
-- 2670592

高性能msyql读书日志相关推荐

  1. android log耗性能吗,一个高性能的Android日志库

    clue 一个高性能的Android日志库. 为什么性能高 通常的Android日志库, 为了获取到class名, 方法名, 行号, 都是通过以下API实现的: StackTraceElement[] ...

  2. 微信 日志服务器 并发大,微信高性能线上日志系统xlog剖析

    微信高性能线上日志系统xlog剖析 做移动开发的同学经常会遇到一个头疼的问题,就是当用户反馈一些问题,又比较冷僻难以复现的时候(不是Crash),常常就会陷入一筹莫展的境地.因此,很多人就研发了相关的 ...

  3. 美团高性能终端实时日志系统建设实践

    你是否经常遇到线上需要日志排查问题但迟迟联系不上用户上报日志的情况?或者是否经常陷入由于存储空间不足而导致日志写不进去的囧境?本文介绍了美团是如何从0到1搭建高性能终端实时日志系统,从此彻底解决日志丢 ...

  4. php date 毫秒_高性能的PHP日志系统 SeasLog 使用

    简介使用SeasLog好处 log日志,通常是系统或软件.应用的运行记录.通过log的分析,可以方便用户了解系统或软件.应用的运行情况:如果你的应用log足够丰富,也可以分析以往用户的操作行为.类型喜 ...

  5. 日志 php_高性能的PHP日志系统 SeasLog 使用

    简介使用SeasLog好处 log日志,通常是系统或软件.应用的运行记录.通过log的分析,可以方便用户了解系统或软件.应用的运行情况:如果你的应用log足够丰富,也可以分析以往用户的操作行为.类型喜 ...

  6. 高性能MySQL读书笔记 (五)

    1. 优化服务器设置 MySQL有大量的可以修改的参数,但不应该随便修改.应该将更多时间花在schema的优化,索引,查询设计上 配置文件路径: 通常在/etc/my.cnf 不建议动态修改变量,因为 ...

  7. php 日期转毫秒_高性能的PHP日志系统 SeasLog 使用

    简介使用SeasLog好处 log日志,通常是系统或软件.应用的运行记录.通过log的分析,可以方便用户了解系统或软件.应用的运行情况:如果你的应用log足够丰富,也可以分析以往用户的操作行为.类型喜 ...

  8. 高性能的PHP日志系统 SeasLog

    参考文档:http://neeke.github.io/SeasLog/ 什么是SeasLog SeasLog是一个C语言编写的PHP扩展,提供一组规范标准的功能函数,在PHP项目中方便.规范.高效地 ...

  9. 高性能Go语言日志模块dlog使用说明

    介绍 dlog是用GO语言实现的一个简单高效.支持文件轮换以及日志分级的日志SDK.其特征如下: 采用文件日志类型采用了内存缓存,满足高性能输出日志. 支持日志分级,具体分级如下: fatal (lo ...

最新文章

  1. insert语句让我学会的两个MySQL函数
  2. BigPipe:高性能的“流水线技术”网页
  3. 可以卸载什么程序来对计算机进行瘦身,电脑越来越卡了,教你一分钟让电脑瘦身(C盘哪些文件可以删除)-怎么清理电脑内存...
  4. cordova 插件开发
  5. Tomcat集群快速入门:Nginx+Tomcat搭建集群
  6. 分布式系统理论之租约机制学习
  7. Java 7:HashMap与ConcurrentHashMap
  8. c语言程序设计臧,清华大学出版社-图书详情-《C程序设计基础(第2版)》
  9. 51nod--1212 最小生成树
  10. js实现(可实现局部打印)
  11. 微软ASP.NET官方网站MVC教程实际操作中的部分问题
  12. 《Windows via C/C++》学习笔记 —— 内核对象的“线程同步”之“信号量”
  13. st计算机编程语言,SoMAChineST编程语言介绍.pdf
  14. 快递API接口对接分析
  15. 集合查询和查询结果处理
  16. 通过EXCEL中的FILTERXML函数实现批量翻译
  17. 想圆科幻船长梦么?这台PC想必能让你爱不释手
  18. 错过两个时代的IBM,能否用区块链抢占下个时代
  19. Mysql配置文件my.cnf配置及配置参数详解
  20. Centos修改IP

热门文章

  1. 微信小程序学习笔记(4) -- 页面间的跳转和传值
  2. 计算机怎样辅助英语教学,英语教学的有效模式--计算机辅助教学
  3. 双显卡电脑安装Linux教程
  4. secureCRT中解决按小键盘上的数字出现字母和换行问题
  5. 软众数据安全专家DSE源代码加密解决方案
  6. 离线地图瓦片下载 高德地图 谷歌地图 天地图 Mapbox地图 矢量 卫星地图下载
  7. keycode键盘 按键 - 键码 对应表
  8. CentOS postfix 邮件服务配置问题局域网pc telnet 25端口失败
  9. android实现 仿iphoneDialog实现类似于iphone对话框样式
  10. Python分析csv文件及可视化绘图