redis虽然有着卓越的性能,但我们仍然可以通过master/slave这种简单架构,进行读写分离,进一步挖掘redis的性能,提高系统的可用性。
redis怎么进行主从复制呢?redis复制主要是通过master server持久化的rdb文件实现的。master server 先dump出内存快照文件,然后将rdb文件传给slave server,slave server 根据rdb文件重建内存表。redis复制过程如下:
1、slave server启动连接到master server之后,salve server主动发送SYNC命令给master server
2、master server接受SYNC命令之后,判断,是否有正在进行内存快照的子进程,如果有,则等待其结束,否则,fork一个子进程,子进程把内存数据保存为文件,并发送给slave server
3、master server子进程进程做数据快照时,父进程可以继续接收client端请求写数据,此时,父进程把新写入的数据放到待发送缓存队列中
4、slave server 接收内存快照文件之后,清空内存数据,根据接收的快照文件,重建内存表数据结构
5、master server把快照文件发送完毕之后,发送缓存队列中保存的子进程快照期间改变的数据给slave server,slave server做相同处理,保存数据一致性
6、master server 后续接收的数据,都会通过步骤1建立的连接,把数据发送到slave server
需要注意:slave server如果因为网络或其他原因断与master server的连接,当slave server重新连接时,需要重新获取master server的内存快照文件,slave server的数据会自动全部清空,然后再重新建立内存表,这样会让slave server 启动恢复服务比较慢,同时也给master server带来较大压力,可以看出redis的复制没有增量复制的概念,这是redis主从复制的一个主要弊端,在实际环境中,尽量规避中途增加从库。
通过上面的介绍,我们对redis的主从复制有了个大概了解,下面讲讲具体怎么配置master/slave。由于条件限制,这里用一台机器,启动两个进程来进行主从复制。 master server 配置文件master.conf, 主要配置如下:
daemonize yes pidfile /var/run/redis_6379.pid port 6379 #save 900 1 #save 300 10 #save 60 10000 dbfilename dump.rdb dir /var/lib/redis/6379slave server 配置文件slave.conf ,主要配置如下:
daemonize yes pidfile /var/run/redis_6379.pid port 6380 save 900 1 save 300 10 save 60 10000 dbfilename dump.rdb dir /var/lib/redis/6380 slaveof 127.0.0.1 6379slave.conf中,主要通过slaveof指定其master的ip和端口。启动master和slave:
/usr/local/bin/redis-server /etc/redis/master.conf /usr/local/bin/redis-server /etc/redis/slave.conf连接master server,写入数据:
[root@xsf001 ~]# redis-cli -p 6379 redis 127.0.0.1:6379> set user.1.name zhangsan OK
连接 slave server,读数据:
[root@xsf001 ~]# redis-cli -p 6380 redis 127.0.0.1:6380> get user.1.name "zhangsan"
从测试结果看,数据确实自动复制了。通过复制功能,我们可以在master上只写数据,在slave上读数据,关闭master的持久化(或aof)功能(在salve上开启),从而分担master服务器读压力,提高master服务器性能,同时,master挂了,可以快速地用slave server来替换master server,提高系统的可用性。
在实际环境中,我们可以根据redis复制特点,定制适合我们自己的复制架构。比如,采用master server ->slave server ->slave server ->slave server这种一拖一【或一拖一再拖多】的方式,和常规的一拖多方式相比,这种方式更能减少master server在复制数据时的压力。当然,由于redis复制的天然缺陷,我们也可以采用主动复制的方式【通过redis代理层,client在写master时,写多个master】来改造优化redis自带的复制策略,不过,主动复制,怎么保持数据的一致性也是个不小的挑战。
概述
企业应用软件中,在数据存储方面选择为DBMS(数据库管理系统)。当数据量增多后,对数据的查询和分析在速度上会有很大的影响。原因如下:
1、数据文件存储在磁盘上,每次读取会有I/O消耗。
2、I/O性能始终是数据读取的瓶颈。数据随机存放,每一次的I/O操作消耗大量的CPU时间。为了降低I/O操作对DBMS查询速度的限制,DBMS引入了索引的概念。
下面会以MySQL为例来说明,例子如下:
假设数据库中一个表有10^6条记录,DBMS的页面大小为4K,并存储100条记录。如果没有索引,查询将对整个表进行扫描,最坏的情况下,如果所有数据页都不在内存,需要读取10^4个页面,如果这10^4个页面在磁盘上随机分布,需要进行10^4次I/O,假设磁盘每次I/O时间为10ms(忽略数据传输时间),则总共需要100s(但实际上要好很多很多)。如果对之建立B-Tree索引,则只需要进行log100(10^6)=3次页面读取,最坏情况下耗时30ms。这就是索引带来的效果,很多时候,当你的应用程序进行SQL查询速度很慢时,应该想想是否可以建索引。
索引
索引类型
MySQL的数据库引擎有两种,一是MyISAM另一种是INNODB。对于这两种的区别可以参看以下内容:
http://www.cnblogs.com/villion/archive/2009/07/09/1893762.html。
BTree是INNODB的索引算法,m阶的BTree树,满足如下内容
1、所有节点最多有m个子节点
2、根节点至少有两个子节点
3、除根节点和叶子节点外,其它节点至少有m/2取上整个节点
4、所有叶子节点在同一层
5、有k个节点的非叶子节点恰好有k-1个关键字。
其结构如下:
MySql执行计划
使用explain关键字,通过该关键字可以查看mysql的执行计划。执行计划的参数如下:
查询优化经验
(1)越小的数据类型通常更好:越小的数据类型通常在磁盘、内存和CPU缓存中都需要更少的空间,处理起来更快。
(2)简单的数据类型更好:整型数据比起字符,处理开销更小,因为字符串的比较更复杂。在MySQL中,应该用内置的日期和时间数据类型,而不是用字符串来存储时间;以及用整型数据类型存储IP地址。
(3)尽量避免NULL:应该指定列为NOT NULL,除非你想存储NULL。在MySQL中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值。这个在实际开发中应该要注意到,避免出现空值。
(4)如果对多列进行索引(组合索引),列的顺序非常重要,MySQL仅能对索引最左边的前缀进行有效的查找。
例如:
存在组合索引index_name(name,pwd),
查询语句select * from t5 where name='1' and pwd='2'能够使用该索引。
查询语句select * from t5 where name='1' 也能够使用该索引。
但是,查询语句select * from t1 where pwd='2'不能够使用该索引,
因为没有组合索引的引导列,即,要想使用pwd列进行查找,必需出现name等于某值。
例子如下:mysql> desc t5;
+---------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(20) | NO | MUL | NULL | |
| pwd | varchar(20) | NO | | NULL | |
| sex | int(11) | YES | | 0 | |
| age | int(11) | YES | MUL | 0 | |
| address | varchar(30) | YES | | NULL | |
+---------+-------------+------+-----+---------+----------------+
6 rows in set (0.01 sec)
mysql> explain select * from t5 where name='n1'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t5
type: ref
possible_keys: index_name
key: index_name
key_len: 62
ref: const
rows: 1
Extra: Using where; Using index
1 row in set (0.00 sec)
mysql> explain select * from t5 where pwd='n1'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t5
type: index
possible_keys: NULL
key: index_name
key_len: 124
ref: NULL
rows: 5
Extra: Using where; Using index
1 row in set (0.00 sec)
mysql> explain select * from t5 where name='n1' and pwd='n1'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t5
type: ref
possible_keys: index_name
key: index_name
key_len: 124
ref: const,const
rows: 1
Extra: Using where; Using index
1 row in set (0.00 sec)
(5)like查询
mysql> explain select * from t5 where name like '%p1'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t5
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 5
Extra: Using where
1 row in set (0.00 sec)
mysql> explain select * from t5 where name like 'p1%'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t5
type: range
possible_keys: index_name
key: index_nam