数据库之Redis
介绍
redis是一个key-value存储系统,线程安全。 和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。 这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。 在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。 区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。 redis的存储分布在内存、磁盘、log文件三部分
安装Redis
1.下载、安装 wget https://codeload.github.com/antirez/redis/tar.gz/2.8.21 mv 2.8.21 redis-2.8.21.tar.gz tar zxvf redis-2.8.21.tar.gz cd redis-2.8.21 make make PREFIX=/usr/local/redis install 2.配置 mkdir /usr/local/redis/etc/ vim /usr/local/redis/etc/redis.conf //写入如下内容 daemonize yes pidfile /usr/local/redis/var/redis.pid port 6379 timeout 300 loglevel debug logfile /usr/local/redis/var/redis.log databases 16 save 900 1 save 300 10 save 60 10000 rdbcompression yes dbfilename dump.rdb dir /usr/local/redis/var/ appendonly no appendfsync always #下面是redis.conf的主 要配置参数的意义: #daemonize:是否以后台daemon方式运行 #pidfile:pid文件位置 #port:监听的端口号 #timeout:请求超时时间 #loglevel:log信息级别 #logfile:log文件位置 #databases:开启数据库的数量 #save * *:保存快照的频率,第一个*表示多长时间,第三个*表示执行多少次写操作。在> 一定时间内执行一定数量的写操作时,自动保存快照。可设置多个条件。 #rdbcompression:是否使用压缩 #dbfilename:数据快照文件名(只是文件名,不包括目录) #dir:数据快照的保存目录(这个是目录) #appendonly:是否开启appendonlylog,开启的话每次写操作会记一条log,这会提高数据> 抗风险能力,但影响效率。 #appendfsync:appendonlylog如何同步到磁盘(三个选项,分别是每次写都强制调用fsync、每秒启用一次fsync、不调用fsync等待系统自己同步) mkdir /usr/local/redis/var/ chmod 777 !$ 启动redis的命令是: /usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf 或者 我们也可以编写一个redis启动脚本。 vi /etc/init.d/redis //加入如下内容: #!/bin/sh # # redis init file for starting up the redis daemon # # chkconfig: - 20 80 # description: Starts and stops the redis daemon. # Source function library. . /etc/rc.d/init.d/functions name="redis-server" basedir="/usr/local/redis" exec="$basedir/bin/$name" pidfile="$basedir/var/redis.pid" REDIS_CONFIG="$basedir/etc/redis.conf" [ -e /etc/sysconfig/redis ] && . /etc/sysconfig/redis lockfile=/var/lock/subsys/redis start() { [ -f $REDIS_CONFIG ] || exit 6 [ -x $exec ] || exit 5 echo -n $"Starting $name: " daemon --user ${REDIS_USER-redis} "$exec $REDIS_CONFIG" retval=$? echo [ $retval -eq 0 ] && touch $lockfile return $retval } stop() { echo -n $"Stopping $name: " killproc -p $pidfile $name retval=$? echo [ $retval -eq 0 ] && rm -f $lockfile return $retval } restart() { stop start } reload() { false } rh_status() { status -p $pidfile $name } rh_status_q() { rh_status >/dev/null 2>&1 } case "$1" in start) rh_status_q && exit 0 $1 ;; stop) rh_status_q || exit 0 $1 ;; restart) $1 ;; reload) rh_status_q || exit 7 $1 ;; force-reload) force_reload ;; status) rh_status ;; condrestart|try-restart) rh_status_q || exit 0 restart ;; *) echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart}" exit 2 esac exit $? 因为脚本启动时以redis用户启动的,所以需要增加redis用户 useradd -s /sbin/nologin redis #增加redis用户 mkdir /usr/local/redis/var chmod 777 !$ chmod 755 /etc/init.d/redis chkconfig --add redis #添加到系统服务 chkconfig redis on #设置开机启动 service redis start #启动redis服务
新版本3.x安装
wget http://download.redis.io/releases/redis-3.0.6.tar.gz tar zxvf redis-3.0.6.tar.gz cd redis-3.0.6 make make install PREFIX=/usr/local/redis
配置
mkdir /usr/local/redis/etc
cp redis.conf !$
vim /usr/local/redis/etc/redis.conf #修改
daemonize yes pidfile /usr/local/redis/var/redis.pid timeout 300 loglevel debug logfile /usr/local/redis/var/redis.log dir /usr/local/redis/var/ appendfsync always
daemonize:是否以后台daemon方式运行 pidfile:pid文件位置 port:监听的端口号 timeout:请求超时时间 loglevel:log信息级别 logfile:log文件位置 databases:开启数据库的数量 save * *:保存快照的频率,第一个*表示多长时间,第三个*表示执行多少次写操作。在一定时间内执行一定数量的写操作时,自动保存快照。可设置多个条件。 rdbcompression:是否使用压缩 dbfilename:数据快照文件名(只是文件名,不包括目录) dir:数据快照的保存目录(这个是目录) appendonly:是否开启appendonlylog,开启的话每次写操作会记一条log,这会提高数据抗风险能力,但影响效率。 appendfsync:appendonlylog如何同步到磁盘(三个选项,分别是每次写都强制调用fsync、每秒启用一次fsync、不调用fsync等待系统自己同步)
启动服务
cd /usr/local/redis ./bin/redis-server
进入redis命令行界面
为了方便,首先把redis的bin目录加入环境变量
vim /etc/profile.d/redis.sh #加入 export PATH=$PATH:/usr/local/redis/bin/ source /etc/profile
redis-cli
设置临时密码 #服务重启失效,永久设置需改配置文件
config set requirepass redis
string类型 127.0.0.1:6379> keys * #显示所有类型键 1) "key1" 2) "key2" 127.0.0.1:6379> get key1 "hy" 127.0.0.1:6379> get key2 "hyit" 127.0.0.1:6379> set key1 com OK 127.0.0.1:6379> get key1 "com" 127.0.0.1:6379> setnx key3 cs #setnx若键存在则赋值失败,否则赋值成功 (integer) 1 127.0.0.1:6379> get key3 "cs" 127.0.0.1:6379> setex key1 300 aaa #setex可设置过期时间(时间参数不可少) OK 127.0.0.1:6379> get key1 "aaa" 127.0.0.1:6379> ttl key1 #ttl获得过期时间 (integer) 205 127.0.0.1:6379> ttl key1 (integer) 203 127.0.0.1:6379> mset key1 1 key2 2 key3 3 #批量设置键、值 OK 127.0.0.1:6379> mget key1 key3 #批量读取键、值 1) "1"mget 3) "3" hash类型 127.0.0.1:6379> hset h1 name hy #hget设置某个hash键内某个键、值 (integer) 1 127.0.0.1:6379> hset h1 age 20 (integer) 1 127.0.0.1:6379> hset h1 sex man (integer) 1 127.0.0.1:6379> hget h1 name #hget读取某个hash键内某个键、值 "hy" 127.0.0.1:6379> hgetall h1 #hgetall批量读取某个hash键内所有键、值 1) "name" 2) "hy" 3) "age" 4) "20" 5) "sex" 6) "man" 127.0.0.1:6379> hmset h2 name gini age 21 job yunwei #批量设置某个hash键内的键、值 OK 127.0.0.1:6379> hmget h2 name age #hmget 批量读取某个hash键内的键、值 1) "gini" 2) "21" 127.0.0.1:6379> keys * #显示所有类型键 1) "key2" 2) "key1" 3) "h1" 4) "key3" 5) "h2" 127.0.0.1:6379> hdel h2 age #hdel 删除某个hash键内的某个键值对 (integer) 1 127.0.0.1:6379> hgetall h2 1) "name" 2) "gini" 3) "job" 4) "yunwei" 127.0.0.1:6379> hkeys h2 #hkeys 读取某个hash键内内的所有键 1) "name" 2) "job" 127.0.0.1:6379> hvals h2 #hvals读取某个hash键内的所有值 1) "gini" 2) "yunwei" 127.0.0.1:6379> hlen h1 #读取某个hash键内的 (integer) 3 127.0.0.1:6379> hlen h2 (integer) 2 list类型 127.0.0.1:6379> lpush list1 a #从做送入一个值 (integer) 1 127.0.0.1:6379> lpush list1 b (integer) 2 127.0.0.1:6379> lpush list1 c (integer) 3 127.0.0.1:6379> lrange list1 0 -1 #读取list1所有值 1) "c" 2) "b" 3) "a" 127.0.0.1:6379> lpop list1 #送出左边一个值 "c" 127.0.0.1:6379> lrange list1 0 -1 1) "b" 2) "a" 127.0.0.1:6379> rpush list1 1 #右边送入一个值 (integer) 3 127.0.0.1:6379> rpush list1 2 (integer) 4 127.0.0.1:6379> lrange list1 0 -1 1) "b" 2) "a" 3) "1" 4) "2" 127.0.0.1:6379> rpop list1 #rpop 右边送出一个值 "2" 127.0.0.1:6379> lrange list1 0 -1 1) "b" 2) "a" 3) "1" 127.0.0.1:6379> rrange list1 0 -1 #没有rrange这个命令 (error) ERR unknown command 'rrange' 127.0.0.1:6379> linsert list1 before a c #linsert 插入一个值(没有rinsert) (integer) 4 127.0.0.1:6379> lrange list1 0 -1 1) "b" 2) "c" 3) "a" 4) "1" 127.0.0.1:6379> lset list1 1 cc #lset通过索引值重新设置list值 OK 127.0.0.1:6379> lrange list1 0 -1 1) "b" 2) "cc" 3) "a" 4) "1" 127.0.0.1:6379> lindex list1 2 #lindex 通过索引值读取值 "a" 127.0.0.1:6379> lindex list1 3 "1" 127.0.0.1:6379> llen list1 #llen 读取list值的个数 (integer) 4 set类型(无序集合) 127.0.0.1:6379> sadd set1 1 #添加值 (integer) 1 127.0.0.1:6379> sadd set1 2 (integer) 1 127.0.0.1:6379> sadd set1 3 (integer) 1 127.0.0.1:6379> smembers set1 #读取set所有值 1) "1" 2) "2" 3) "3" 127.0.0.1:6379> srem set1 2 #删除set内某个值 (integer) 1 127.0.0.1:6379> smembers set1 1) "1" 2) "3" 127.0.0.1:6379> spop set1 #随机送出set内一个值 "3" 127.0.0.1:6379> smembers set1 1) "1" 127.0.0.1:6379> spop set1 "1" 127.0.0.1:6379> smembers set1 (empty list or set) 127.0.0.1:6379> sadd set1 1 3 123 333 #批量设置 (integer) 4 127.0.0.1:6379> sadd set2 hy 1 333 linux (integer) 4 127.0.0.1:6379> smembers set1 #批量读取 1) "1" 2) "3" 3) "123" 4) "333" 127.0.0.1:6379> smembers set2 1) "333" 2) "1" 3) "linux" 4) "hy" 127.0.0.1:6379> sdiff set1 set2 #以第一个set为标准,读取两个值的差值即(set1等效于set1减去set1和set2的交集) 1) "3" 2) "123" 127.0.0.1:6379> sdiff set2 set1 1) "hy" 2) "linux" 127.0.0.1:6379> sdiffstore setres1 set2 set1 #sdiffstore 把结果存到setres1 (integer) 2 127.0.0.1:6379> smembers setres1 1) "hy" 2) "linux" 127.0.0.1:6379> sinter set1 set2 #sinter读取两个集合的交集 1) "1" 2) "333" 127.0.0.1:6379> sinterstore setres2 set1 set2#sinterstore把结两个集合的交集果存到setres2 (integer) 2 127.0.0.1:6379> smembers setres2 #sinter读取两个集合的并集 1) "1" 2) "333" 127.0.0.1:6379> sunion set1 set2 1) "123" 2) "3" 3) "linux" 4) "1" 5) "333" 6) "hy" 127.0.0.1:6379> sunionstore setres3 set1 set2 (integer) 6 127.0.0.1:6379> smembers setres3 1) "123" 2) "3" 3) "linux" 4) "1" 5) "333" 6) "hy" 127.0.0.1:6379> sismember set2 linux #sismember 判断某个值是否在集合里(返回非0说明在) (integer) 0 127.0.0.1:6379> sismember set2 linux (integer) 1 127.0.0.1:6379> srandmemberset2 #随机取出一个值 "hy" 127.0.0.1:6379> srandmemberset2 "1" 127.0.0.1:6379> srandmemberset2 "hy" zset类型(有序集合,默认按score从小到大排序) 127.0.0.1:6379> zadd zsert1 1 abc (integer) 1 127.0.0.1:6379> zadd zsert1 10 1abc (integer) 1 127.0.0.1:6379> zadd zsert1 5 bbb (integer) 1 127.0.0.1:6379> zadd zsert1 666 hy (integer) 1 127.0.0.1:6379> zrange zsert1 0 -1 #排序 1) "abc" 2) "bbb" 3) "1abc" 4) "hy" 127.0.0.1:6379> zrevrange zsert1 0 -1 #反向排序 1) "hy" 2) "1abc" 3) "bbb" 127.0.0.1:6379> zrange zsert1 0 -1 withscores 1) "abc" 2) "1" 3) "bbb" 4) "5" 5) "1abc" 6) "10" 7) "hy" 8) "666" 127.0.0.1:6379> zrem zsert1 abc #删除某元素 (integer) 1 127.0.0.1:6379> zrange zsert1 0 -1 1) "bbb" 2) "1abc" 3) "hy 127.0.0.1:6379> zrange zsert1 0 -1 withscores 1) "bbb" 2) "5" 3) "1abc" 4) "10" 5) "hy" 6) "666" 127.0.0.1:6379> zrevrank zsert1 hy #读取索引值 (integer) 0 127.0.0.1:6379> zrank zsert1 hy #反向读取索引值 (integer) 2 127.0.0.1:6379> zcard zsert1 #返回集合中元素的个数 (integer) 3 127.0.0.1:6379> zcount zsert1 10 666 #score在10-666之间的元素 个数 (integer) 2 127.0.0.1:6379> zrangebyscore zsert1 0 20 #返回分值在某范围的元素 1) "bbb" 2) "1abc" 127.0.0.1:6379> ZREMRANGEBYSCORE zsert1 0 20 #删除某score范围内的元素 (integer) 2 127.0.0.1:6379> zrange zsert1 0 -1 1) "hy" 127.0.0.1:6379> ZREMRANGEBYRANK zsert1 0 1 #删除某索引值范围内的元素 (integer) 1 127.0.0.1:6379> zrange zsert1 0 -1 (empty list or set)
持久化
redis支持两种持久化:RDB(Redis DataBase)和AOF(Append ONLY File) RDB:在不同的时间点,讲redis存储的数据生成快照存储到磁盘等介质上 AOF:将redis所有写指令记录下来,下次redis重启时,把这些指令执行一遍就可以恢复数据了 RDB和AOF两种方式可同时使用,如果redis重启,优先采用AOF方式来恢复数据,因为AOF数据恢复完整度更高。 如果没有数据持久化的需求,可以关闭RDB和AOF方式,此时redis变成一个纯内存数据库,和memcache一样 vim /usr/local/redis/etc/redis.conf #redis默认配置文件,默认开启RDB,关闭AOF daemonize yes pidfile /usr/local/redis/var/redis.pid port 6379 timeout 300 loglevel debug logfile /usr/local/redis/var/redis.log databases 16 save 900 1 save 300 10 save 60 10000 rdbcompression yes dbfilename dump.rdb dir /usr/local/redis/var/ appendonly no appendfsync always #关闭RDB save 900 1 save 300 10 save 60 10000 rdbcompression yes dbfilename dump.rdb 开启AOF(定义 aof 文件名,定义同步磁盘规则为每秒强制同步) appendonly yes appendfilename appendonly.aof appendfsync everysec no-appendfsync-on-rewrite yes auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb ## 由于 AOF 的特性是记录每次操作,当客户端执行相同的操作时,aof 也会记录这些相同的每次操作,那么 aof 文件会不断增加。对于这个问题,可以使用 aof 文件的重写功能解决 aof 文件的增长。当 aof 文件满足一定条件时就对 aof 进行重写,重写是根据当前内存数据库中的数据进行遍历到一个临时的 aof 文件,写完后替换原来的 aof 文件。 ## no-appendfsync-on-rewrite 参数表示在重写期间对新接收到的写操作不同步,暂时存在内存中,等重写完后再写入,防止与磁盘 IO 冲突。 ## auto-aof-rewrite-percentage 参数表示 aof 文件增长率大于此值同时 aof 文件大于下面参数值时,触发 aof rewrite ## auto-aof-rewrite-min-size 参数表示当前 aof 文件大小大于这个值时 谢谢
主从
环境: 假设我有两台机器: master: 192.168.11.160 slave: 192.168.11.190 两台机器已经安装并开启了redis服务 [root@master ~]# redis-cli -a hy #主上redis密码:hy 127.0.0.1:6379> get key1 (nil) 从上配置: vim /usr/local/redis/etc/redis.conf #加上两行 slaveof 192.168.11.160 6379 #主机器的ip和redis服务占用的端口 masterauth hy #主redis密码 重启redis service network restart #出现下面的提示说明配置成功 [root@slave ~]# tail /usr/local/redis/var/redis.log [5327] 21 Dec 02:46:32.398 * Connecting to MASTER 192.168.11.105:6379 [5327] 21 Dec 02:46:32.398 * MASTER <-> SLAVE sync started [5327] 21 Dec 02:46:32.401 * Non blocking connect for SYNC fired the event. [5327] 21 Dec 02:46:32.402 * Master replied to PING, replication can continue... [5327] 21 Dec 02:46:32.403 * Partial resynchronization not possible (no cached master) [5327] 21 Dec 02:46:32.404 * Full resync from master: ad2e692811aa95324d1e461cb7173ec4f2dda396:1 [5327] 21 Dec 02:46:32.574 * MASTER <-> SLAVE sync: receiving 40 bytes from master [5327] 21 Dec 02:46:32.575 * MASTER <-> SLAVE sync: Flushing old data [5327] 21 Dec 02:46:32.575 * MASTER <-> SLAVE sync: Loading DB in memory [5327] 21 Dec 02:46:32.575 * MASTER <-> SLAVE sync: Finished with success 测试: 主上: redis-cli -a hy 127.0.0.1:6379>set key1 hi 从上: redis-cli -a hy 127.0.0.1:6379> get key1 "hi" 谢谢
谢谢