Remote Dictionary Server, 翻译为远程字典服务, Redis是一个彻底开源的基于Key-Value的NoSQL存储系统,他是一个使用ANSIC语言编写的,遵照BSD协议,支持网络、可基于内存的可持久化的日志型、Key-Value数据库,并提供多种语言的API.python
它一般被称为数据结构服务器,由于值(value)能够是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。mysql
# Redis架构主要有两个程序: # Redis客户端 redis-cli # Redis服务器 redis-server
Redis做者是Salvatore Sanfilippo,来自意大利的西西里岛.
git
2008年他在开发一个LLOOGG的网站时,须要一个高性能的队列功能,最开始使用MySQL来实现,无奈性能老是提不上去,因此决定本身实现一个专属于LLOOGG的数据库,他就是Redis的前身。后台 Sanfilippoj将 Redis1.0放到Github上大受欢迎。程序员
Redis基于BSD开源协议,BSD开源协议是一个给于使用者很大自由的协议。能够自由的使用,修改源代码,也能够将修改后的代码做为开源或者专有软件再发布。当你发布使用了BSD协议的代码,或者以BSD协议代码为基础作二次开发本身的产品时,须要知足三个条件:github
BSD代码鼓励代码共享,但须要尊重代码做者的著做权。BSD因为容许使用者修改和从新发布代码,也容许使用或在BSD代码上开发商业软件发布和销售,所以是对商业集成很友好的协议。golang
不少的公司企业在选用开源产品的时候都首选BSD协议,由于能够彻底控制这些第三方的代码,在必要的时候能够修改或者 二次开发。redis
命令执行结构
sql
# 1. 客户端发送命令后,Redis服务器将为这个客户端连接创造一个'输入缓存',将命令放到里面 # 2. 再由Redis服务器进行分配挨个执行,顺序是随机的,这将不会产生并发冲突问题,也就不须要事务了. # 3. 再将结果返回到客户端的'输出缓存'中,输出缓存先存到'固定缓冲区',若是存满了,就放入'动态缓冲区',客户端再得到信息结果 # 若是数据时写入命令,例如set name:1 zhangsan 方式添加一个字符串. # Redis将根据策略,将这对key:value来用内部编码格式存储,好处是改变内部编码不会对外有影响,正常操做便可, # 同时不一样状况下存储格式不同,发挥优点.
传统数据库在存储数据时,须要先定义schema,以肯定类型(字节宽度),并以行存储,因此每行所占的字节宽度是一致的(便于索引数据)。数据库内部数据分为多个datapage(通常是4kb)存储,随着数据量的增大,数据库查询速度会愈来愈慢,其主要瓶颈在于磁盘I/O:数据库
# 寻址时间(ms)级别 # 带宽(G/M)
因为数据量增大查找datapage的时间也会变长,因此索引出现了。索引是一个B+T,存储在内存中,根据索引记录的信息,能够快速定位到datapage的位置。索引虽然会大大加快查询速度,可是由于增删改须要维护索引的B+T,因此会把增删改的速度拖慢,因此索引不适合频繁写的表。vim
此外,当数据库高并发查询的状况下,单位时间内所需的数据量也是很大的,此时可能会受到磁盘带宽的影响,影响磁盘的查询速度。
在I/O上,内存相比较于磁盘,拥有较好的性能;
# 寻址时间(ns级别,磁盘是其10w倍) # 带宽(双通道DDR400的宽带为6.4GBps)
因此,出现了一批基于内存的关系型数据库,好比SAP HAHA数据库,其物理机器内存2T,包含软件以及服务,购买须要1亿元,因为内存关系型数据库的昂贵价格,因此大部分公司采用了折中的方案,使用磁盘关系型数据库+内存缓存,好比 Oracle+Memcached,Mysql+Redis
为何使用Redis?
我的以为项目中使用redis,主要从两个角度去考虑性能和并发,固然,redis还具有能够作分布式锁等其余功能,可是若是只是为了分布式锁这些其余功能,彻底还有其余中间件(如zookeeper等)代替,并非非要使用redis,所以,这个问题主要从性能和并发两个角度去答.
性能
以下图所示,我摩恩碰到须要执行耗时特别久,且结果不频繁变更的SQL,就特别适合将运行结果放入缓存,这样,后面的请求就去缓存中读取,使得请求可以迅速响应.
迅速响应的标准,根据交互效果的不一样,这个响应时间没有固定标准。不过曾经有人这么告诉我:"在理想状态下,咱们的页面跳转须要在瞬间解决,对于页内操做则须要在刹那间解决。另外,超过一弹指的耗时操做要有进度提示,而且能够随时停止或取消,这样才能给用户最好的体验。"
那么瞬间、刹那、一弹指具体是多少时间呢?
一刹那者为一念,二十念为一瞬,二十瞬为一弹指,二十弹指为一罗预,二十罗预为一须臾,一日一晚上有三十须臾。
通过周密的计算,一瞬间为0.36秒,一刹那有0.018秒,一弹指changda7.2秒
并发
以下图所示,在大并发的状况下,全部的请求直接访问数据库,数据库会出现链接异常,这个时候,就须要使用redis作一个缓冲操做,让请求先访问到redis,而不是直接访问数据库.
# 1. 基于内存的访问,非阻塞I/O,Redis使用事件驱动模型epoll多路复用实现,链接,读写,关闭都转换为事件不在网络I/O上浪费过多的时间. # 2. 单线程避免高并发的时候,多线程有锁的问题和线程切换的CPU开销问题.《虽然是单线程,但能够开多实例弥补》 # 3. 使用C语言编写,更好的发挥服务器性能,而且代码简介,性能高.
合理的使用 缓存 可以明显加快访问的速度,同时下降数据源的压力。这也是
Redis
最经常使用的功能。Redis
提供了 键值过时时间(EXPIRE key seconds
)设置,而且也提供了灵活控制 最大内存 和 内存溢出 后的 淘汰策略。
每一个网站都有本身的排行榜,例如按照 热度排名 的排行榜,发布时间 的排行榜,答题排行榜 等等。
Redis
提供了 列表(list
)和 有序集合(zset
)数据结构,合理的使用这些数据结构,能够很方便的构建各类排行榜系统。
计数器 在网站应用中很是重要。例如:点赞数加
1
,浏览数 加1
。还有经常使用的 限流操做,限制每一个用户每秒 访问系统的次数 等等。Redis
支持 计数功能(INCR key
),并且计数的 性能 也很是好,计数的同时也能够设置 超时时间,这样就能够 实现限流。
赞/踩,粉丝,共同好友/喜爱,推送,下拉刷新等是社交网站必备的功能。因为社交网站 访问量一般比较大,并且 传统的数据库 不太适合保存这类数据,
Redis
提供的 数据结构 能够相对比较容易实现这些功能。
Redis
提供的 发布订阅(PUB/SUB
)和 阻塞队列 的功能,虽然和专业的消息队列比,还 不够强大,但对于通常的消息队列功能基本知足。
此时只作介绍,数据类型具体介绍请看后面
一组动做,要么所有执行,要么所有不执行,例如在社交网站上用户A关注了
用户B, 那么须要在用户A的关注表中加入用户B, 而且在用户B的粉丝表中
添加用户A, 这两个行为要么所有执行, 要么所有不执行, 不然会出现数据
不一致的状况。Redis 提供了简单的事务功能, 将一组须要一块儿执行的命令放到
multi
和
exec
两个命令之间。multi
命令表明事务开始,exec
命令表明事务结束, 它们
之间的命令是原子顺序执行的, 例以下面操做实现了上述用户关注问题。
之间执行命令将不执行,在缓冲中,等exec后裁真正开始执行
若是其中有语法错误,命令打错了,那整个事务将结束.
若是把值写错了,多个字母,但语法正确,那事务是正确的,要手动恢复,不支持回滚.
在事务开始前,用watch key能够检要操做的key,若是key在事务开始后有变化,例如multi开始修改时,这个key被其余客户端修改,事务将不进行操做.
一个Redis从开始到执行会经历如下阶段
# 1. 事务开始 # 2. 命令入队 # 3. 执行事务
命令 | 描述 |
---|---|
DISCARD | 取消事物,放弃执行事物内的全部命令 |
EXEC | 执行全部事物块内的命令 EXEC{执行事务} |
MULTI | 标志一个事务块的开始 MULTI{启动一个事务} |
UNWATCH | 取消WATCH命令多全部key的监视 |
| WAHCH key [key …] | 监视一个(或多个)key,若是在事务执行以前这个(或这些) key 被其余命令所改动,那么事务将被打断。
# 1. Redis不支持回滚,即一条命令当作事务执行时,当有一个中间的命令发生错误,mysql将会把以前的操做取消并结束事务. # 2. 可是Redis不会,Redis还会继续把剩下的命令执行下去,忽略发生错误的命令.
好比Redis只能存5G数据,但是你写了10G,那会删5G的数据。怎么删的,这个问题思考过么?还有,你的数据已经设置了过时时间,可是时间到了,内存占用率仍是比较高,有思考过缘由么?
Redis采用的是按期删除 + 惰性删除策略
定时删除,用一个定时器来负责监视key,过时则自动删除,虽然内存及时释放,可是十分消耗CPU资源,大并发状况下,CPU要将时间应用在处理请求,而不是删除key,所以没有采用这一策略.
按期删除+惰性删除是如何工做的?
按期删除,redis默认每一个100ms检查,是否有过时的key,有过时key则删除。须要说明的是,redis不是每一个100ms将全部的key检查一次,而是随机抽取进行检查(若是每隔100ms,所有key进行检查,redis岂不是卡死)。所以,若是只采用按期删除策略,会致使不少key到时间没有删除。
因而,惰性删除派上用场,也就是说你获取某个key的时候,redis会检查一下,这个key若是设置了过时时间那么是否过时了?若是过时了此时就会删除.
采用按期删除+惰性删除就没其余问题了吗?
并非,若是按期删除删除没删除key。而后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会愈来愈高。那么就应该采用内存淘汰机制.
在redis.conf中有一行配置
# maxmemory-policy volatile-lru #该配置就是配内存淘汰策略的 # 1. noeviction: 当内存不足以容纳写入数据时,新写入会报错,应该没人用. # 2. allkeys-lru: 当内存不足以容纳新写入数据时,在键空间中,移除最少使用的那个key.推荐使用 # 3. allkeys-random: 当内存不足以容纳新写入数据时,在键空间中,随机移除某个key,应该也没人用,不删最少使用,随机删? # 4. volatile-lru: 当内存不足以容纳写入数据时,在设置了过时时间的键空间中,移除最少使用的key, # 通常是吧redis既当缓存又当持久化存储才用,不推荐. # 5. volatile-random: 当内存不足以容纳新写入数据时,在设置了过时时间的键空间中,随机移除某个key,依然不推荐. # 6. volatile-ttl: 当内存不足以容纳新写入数据时,在设置了过时时间的键空间中,有更早过时的key优先移除,不推荐. # 若是没有设置expire的key,不知足先决条件(prerequisites); # 那么volatile-lru,volatile-random和volatile-ttl策略行为和noeviction(不删)基本一致
此篇文章只作单机服务器搭建,高可用哨兵和集群请看下一篇
[Redis-Server] 主机名 = redis-master-1 系统 = CentOS7.6.1810 地址 = 121.36.43.223 软件 = redis-4.0.14 # 版本 # Redis的奇数版本为非稳定版本,例如2.7.3.1,若是为偶数则为稳定版本,例如3.2,3.4;
节点名 | IP | 软件版本 | 硬件 | 网络 | 说明 |
---|---|---|---|---|---|
redis-master | 192.168.171.136 | list 里面都有 | 2C4G | Nat,内网 | 测试环境 |
yum -y install gcc wget http://download.redis.io/releases/redis-4.0.14.tar.gz tar xvf redis-4.0.14.tar.gz -C /opt/ cd /opt/redis-4.0.14
# Redis的编译,只将命令文件编译,将会在当前目录生成bin目录 make && make install PREFIX=/usr/local/redis cd .. mv redis-4.0.14/* /usr/local/redis/ # 建立环境变量 echo 'PATH=$PATH:/usr/local/redis/src/' >> /etc/profile source /etc/profile # 此时在任何目录位置均可以是用redis-server等相关命令 [root@redis1 ~]# redis- redis-benchmark redis-check-rdb redis-sentinel redis-trib.rb redis-check-aof redis-cli redis-server
可执行文件 | 做用 |
---|---|
redis-server | 启动redis服务 |
redis-cli redis | 命令行客户端 |
redis-benchmark | Redis基准测试工具 |
redis-check-aof | redis AOF持久化文件检测和修复工具 |
redis-check-dump | redis RDB持久化文件检测和修复工具 |
redis-sentinel | 启动redis sentinel |
# redis进程是否以守护进程的方式运行,yes为是,no为否(不以守护进程的方式运行会占用一个终端) daemonize no # 指定redis进程的PID文件存放位置 pidfile /var/run/redis.pid # redis进程的端口号 port 6379 # 绑定的主机地址 bind 127.0.0.1 # 客户端闲置多长时间后关闭链接,默认此参数为0即关闭此功能 timeout 300 # redis日志级别,可用的级别有debug.verbose.notice.warning loglevel verbose # log文件输出位置,若是进程以守护进程的方式运行,此处又将输出文件设置为stdout的话,就会将日志信息输出到/dev/null里面去了 logfile stdout # 设置数据库的数量,默认为0可使用select <dbid>命令在链接上指定数据库id databases 16 # 指定在多少时间内刷新次数达到多少的时候会将数据同步到数据文件 save <seconds> <changes> # 指定存储至本地数据库时是否压缩文件,默认为yes即启用存储 rdbcompression yes # 指定本地数据库文件名 dbfilename dump.db # 指定本地数据问就按存放位置 dir ./ # 指定当本机为slave服务时,设置master服务的IP地址及端口,在redis启动的时候他会自动跟master进行数据同步 slaveof <masterip> <masterport> # 当master设置了密码保护时,slave服务链接master的密码 masterauth <master-password> # 设置redis链接密码,若是配置了链接密码,客户端在链接redis是须要经过AUTH<password>命令提供密码,默认关闭 requirepass footbared # 设置同一时间最大客户链接数,默认无限制。redis能够同时链接的客户端数为redis程序能够打开的最大文件描述符,若是设置 maxclients 0,表示不做限制。当客户端链接数到达限制时,Redis会关闭新的链接并向客户端返回 max number of clients reached 错误信息 maxclients 128 # 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key。当此方法处理后,仍然到达最大内存设置,将没法再进行写入操做,但仍然能够进行读取操做。Redis新的vm机制,会把Key存放内存,Value会存放在swap区 maxmemory<bytes> # 指定是否在每次更新操做后进行日志记录,Redis在默认状况下是异步的把数据写入磁盘,若是不开启,可能会在断电时致使一段时间内的数据丢失。由于redis自己同步数据文件是按上面save条件来同步的,因此有的数据会在一段时间内只存在于内存中。默认为no。 appendonly no # 指定跟新日志文件名默认为appendonly.aof appendfilename appendonly.aof # 指定更新日志的条件,有三个可选参数 - no:表示等操做系统进行数据缓存同步到磁盘(快),always:表示每次更新操做后手动调用fsync()将数据写到磁盘(慢,安全), everysec:表示每秒同步一次(折衷,默认值); appendfsync everysec
咱们须要修改的配置
# 设置后台启动 # 因为Redis默认是前台启动,不建议使用.能够修改成后台 daemonize yes # 禁止protected-mode yes/no(保护模式,是否只容许本地访问) protected-mode # 设置远程访问 # Redis默认只容许本机访问,把bind修改成bind 0.0.0.0 此设置会变成容许全部远程访问,若是指定限制访问,可设置对应IP。 # bind指定是redis所在服务器网卡的IP,不指定本机网卡IP,可能致使你的Redis实例没法启动 # 若是想限制IP访问,内网的话经过网络接口(网卡限定),让客户端访问固定网卡连接redis # 若是是公网,经过iptables指定某个IP容许访问 bind 0.0.0.0 # 配置Redis日志记录 # 找到logfile,默认为logfile "",改成自定义日志格式 logfile /var/log/redis_6379.log # 把requirepass修改成123456,修改以后重启下服务 requirepass "123456" # 不重启Redis设置密码 # 在配置文件中配置requirepass的密码(当Redis重启时密码依然生效) 127.0.0.1:6379> config set requirepass test123 # 查询密码 127.0.0.1:6379> config get requirepass 1) "requirepass" 2) "test123" # 密码验证 127.0.0.1:6379> auth test123 OK 127.0.0.1:6379> set name flying OK 127.0.0.1:6379> get name "flying" # 远程主机链接 # redis-cli -h redis_ip -p redis_port -a password
# 放到后台输出,redis自带日志了,能够输出到黑洞 nohup redis-server /usr/local/redis/redis.conf &> /usr/local/redis/redis.log & # 关闭命令 redis-cli -h 127.0.0.1 -p 6379 -a 123456 shutdown # 注意:不建议使用 kill -9,这种方式不但不会作持久化操做,还会形成缓冲区等资源不能优雅关闭。极端状况下形成 AOF 和 复制丢失数据 的状况。 # shutdown 还有一个参数,表明是否在关闭 redis 前,生成 持久化文件,命令为 redis-cli shutdown nosave|save。 # 设置开机自启动 echo "redis-server /usr/local/redis.conf" >> /etc/rc.local
在/etc/init.d目录添加Redis服务的启动,暂停和重启脚本
vim /etc/init.d/redis #!/usr/bin/env bash # chkconfig: 2345 10 90 # description: Start and Stop redis PORT=6379 EXEC=/usr/local/redis/src/redis-server CLIEXEC=/usr/local/redis/src/redis-cli PIDFILE=/var/run/redis_${PORT}.pid CONF="/etc/redis/${PORT}.conf" case "$1" in start) if [ -f $PIDFILE ] then echo "$PIDFILE exists, process is already running or crashed" else echo "Starting Redis server..." $EXEC $CONF &>/dev/null & fi ;; stop) PID=$(cat $PIDFILE) if [ ! -f $PIDFILE ] then echo "$PIDFILE does not exist, process is not running" else echo "Stopping ..." $CLIEXEC -p $PORT shutdown while [ -d /proc/${PID} ] do echo "Waiting for Redis to shutdown ..." sleep 1 done echo "Redis stopped" fi ;; restart) "$0" stop sleep 3 "$0" start ;; *) echo "Please use start or stop as first argument" ;; esac chmod +x /etc/init.d/redis mkdir /etc/redis cp /usr/local/redis/redis.conf /etc/redis/6379.conf chkconfig --add redis chkconfig redis on service redis start service redis restart
此处Redis五种数据类型具体操做先不演示,只作一个简单的key操做,具体操做等Python操做Redis再写详细,前面主要以运维为主
# 添加一个key[name]为youmen 127.0.0.1:6379> set name youmen OK # 获取key 127.0.0.1:6379> get name "youmen" # 查看当前数据库里面的key 127.0.0.1:6379> keys * 1) "name" # 判断key是否存在 127.0.0.1:6379> exists name (integer) 1 # 删除key值,也就删除了这条数据 127.0.0.1:6379> del name (integer) 1 # 查找不到对应的key返回值就会使(integer)0 127.0.0.1:6379> exists name (integer) 0 # 设置key的过时时间,过时后key自动删除 127.0.0.1:6379> set name youmen OK 127.0.0.1:6379> set name youmen ex 2 127.0.0.1:6379> expire name 10 (integer) 1 127.0.0.1:6379> ttl name (integer) 6 127.0.0.1:6379> ttl name (integer) 2 127.0.0.1:6379> ttl name (integer) -2 127.0.0.1:6379> exists name (integer) 0
# save:将数据同步保存到磁盘 # bgsave:将数据异步保存到磁盘 # lastsave:返回上次成功将数据保存到磁盘的Unix时戳 # shundown:将数据同步保存到磁盘,而后关闭服务
# info:提供服务器的信息和统计 # info clients: 查看客户端信息. # monitor:实时转储收到的请求 # slaveof:改变复制策略设置 # config:在运行时配置Redis服务器 # client list: 查看连接的客户端有哪些 # chient kill 127.0.0.1:50390: 杀掉客户端连接 # config get dir: 查看存储文件目录 # config get *: 查看全部配置 # config set requirepass 123 : 修改配置,即时生效 # config get bind : 查看配置文件中的监听地址
# 集群每一个节点获取的信息不同 127.0.0.1:6379> info # Server redis_version:4.0.14 redis_git_sha1:00000000 redis_git_dirty:0 redis_build_id:5ad4d17d599d7e92 redis_mode:standalone # 运行模式,单机或集群 os:Linux 3.10.0-1062.1.1.el7.x86_64 x86_64 # 服务器的宿主操做系统 arch_bits:64 # 架构 multiplexing_api:epoll # redis所使用的事件循环机制 atomicvar_api:atomic-builtin # 原子处理api gcc_version:4.8.5 # 编译Redis时所使用的GCC版本 process_id:19955 # 服务器进程的PID run_id:3cd8b85e4c852fc93adbbb51eaee051a0a6a788d # 标识redis server的随机值 tcp_port:6379 uptime_in_seconds:4272 # redis server启动的时间(单位s) uptime_in_days:0 # redis server启动的时间(单位d) hz:10 # hz:10 edis内部调度(进行关闭timeout的客户端,删除过时key等)频率,程序规定serverCron每秒运行十次. lru_clock:1935465 # 自增的时钟,用于LRU管理,该时钟ms(hz=10,所以每1000ms/10=100ms执行一次定时任务)更新一次 executable:/usr/local/redis/src/redis-server # 执行文件 config_file:/etc/redis/6379.conf # 配置文件路径 # Clients connected_clients:1 # 已链接客户端的数量(不包括经过从属服务器链接的客户端) client_longest_output_list:0 # 当前链接的客户端中,最长的输出列表 client_biggest_input_buf:0 # 当前链接的客户端中,最大输入缓存 blocked_clients:0 # 正在等待阻塞命令(BLPOP、BRPOP、Brpoplpush)的客户端的数量 # Memory used_memory:849400 # 由Redis分配器分配的内存总量,以字节(byte)为单位 used_memory_human:829.49K # 以人类可读的格式返回Redis分配的内存总量 used_memory_rss:8278016 # 从操做系统的角度,返回Redis已分配的内存总量(俗称常驻集大小),这个值和top,ps等命令输出一致 used_memory_rss_human:7.89M # 以人类可读的格式,从操做系统角度,返回Redis已分配的内存总量(俗称常驻集大小),这个值和top,ps等命令输出一致 used_memory_peak:849472 # redis的内存消耗峰值(以字节为单位) used_memory_peak_human:829.56K # 以人类可读的格式返回redis的内存消耗峰值 used_memory_peak_perc:99.99% # (used_memory/ used_memory_peak) *100% used_memory_overhead:836622 # Redis为了维护数据集的内部机制所需的内存开销, # 包括全部客户端输出缓冲区、查询缓冲区、AOF重写缓冲区和主从复制的backlog used_memory_startup:786608 # Redis服务器启动时消耗的内存 used_memory_dataset:12778 # used_memory—used_memory_overhead used_memory_dataset_perc:20.35% # 100%*(used_memory_dataset/(used_memory—used_memory_startup)) total_system_memory:1926860800 # 整个系统内存 total_system_memory_human:1.79G # 以人类可读格式,显示整个系统内存 used_memory_lua:37888 # lua脚本存储占用的内存 used_memory_lua_human:37.00K # 以人类可读的格式,显示lua脚本占用的内存 maxmemory:0 # Redis实例的最大内存配置 maxmemory_human:0B # 以人类可读格式,显示Redis实例的最大内存配置 maxmemory_policy:noeviction # 当达到maxmemory时的淘汰策略 mem_fragmentation_ratio:9.74 # used_memory_rss/used_memory mem_allocator:jemalloc-4.0.3 # 内存分配器 active_defrag_running:0 # 表示没有活动的defrag任务正在运行(defrag: 表示内存碎片整理) lazyfree_pending_objects:0 # 0表示不存在延迟释放(也有资料翻译末惰性删除)的挂起对象 # Persistence loading:0 # 服务器是否正在载入持久化文件 rdb_changes_since_last_save:0 # 离最近一次成功生成rdb文件,写入命令的个数,即有多少个写入命令没有持久化 rdb_bgsave_in_progress:0 # 服务器是否正在建立rdb文件 rdb_last_save_time:1578992739 # 离最近一次成功建立rdb文件的时间戳,当前时间戳-rdb_last_save_time=多少秒未成功生成rdb文件 rdb_last_bgsave_status:ok # 最近一次rdb持久化是否成功 rdb_last_bgsave_time_sec:0 # 最近一次成功生成rdb文件耗时总数 rdb_current_bgsave_time_sec:-1 # 若是服务器正在建立rdb文件,那么这个域记录的就是当前建立操做已经耗费的秒数 rdb_last_cow_size:6537216 # RDB过程当中父进程与子进程相比执行了多少修改(包括读缓冲区,写缓冲区,数据修改等) aof_enabled:0 # 是否开启了aof aof_rewrite_in_progress:0 # 标识aof的rewrite操做是否正在进行中 aof_rewrite_scheduled:0 # rewrite任务计划,当客户端发送bgrewriteaof指令,若是当前rewrite子进程正在执行, # 那么将客户端请求的bgrewriteaof变为计划任务,待aof子进程结束后执行rewrite aof_last_rewrite_time_sec:-1 # 最近一次aof rewrite耗费的时长 aof_current_rewrite_time_sec:-1 # 若是rewrite操做正在进行,则记录所使用的时间,单位秒 aof_last_bgrewrite_status:ok # 上次bgrewriteaof操做的状态 aof_last_write_status:ok # 上次aof写入状态 aof_last_cow_size:0 # AOF过程当中父进程与子进程相比执行了多少次修改(包括读缓冲区,写缓冲区,数据修改等) # Stats total_connections_received:4 # 新建立链接个数,若是新建立链接过多,过分地建立和销毁链接对性能有影响, # 说明短链接严重或链接池使用有问题,需调研代码的链接设置 total_commands_processed:87 # Redis处理的命令数 instantaneous_ops_per_sec:0 # redis当前的qps,redis内部较实时的每秒执行的命令数 total_net_input_bytes:2757 # redis网络入口流量字节数 total_net_output_bytes:53214 # redis网络出口流量字节数 instantaneous_input_kbps:0.00 # redis网络入口kps instantaneous_output_kbps:0.00 # redis网络入口kps rejected_connections:0 # 拒绝的链接的个数,redis链接个数达到maxclients限制,拒绝新链接个数 sync_full:0 # 主从彻底同步成功次数 sync_partial_ok:0 # 主从部分同步成功次数 sync_partial_err:0 # 主从部分失败次数 expired_keys:1 # 运行以来过时的key的数量 expired_stale_perc:0.00 # 过时的比率 expired_time_cap_reached_count:0 # 过时计数 evicted_keys:0 # 运行以来剔除(超过maxmemory后)的key的数量 keyspace_hits:26 # 命中次数 keyspace_misses:10 # 未命中次数 pubsub_channels:0 # 当前使用中的频道数量 pubsub_patterns:0 # 当前使用的模式的数量 latest_fork_usec:578 # 最近一次fork操做阻塞redis进程的耗时数,单位微妙 migrate_cached_sockets:0 # 是否已经缓存到了改地址的连接 slave_expires_tracked_keys:0 # 从实例到期key的数量 active_defrag_hits:0 # 主动碎片整理命中次数 active_defrag_misses:0 # 主动碎片整理未命中次数 active_defrag_key_hits:0 # 主动碎片整理key命中次数 active_defrag_key_misses:0 # 主动碎片整理key未命中次数. # Replication role:master # 实例的角色,是master or slave connected_slaves:0 # 链接的slave实例个数 master_replid:54da017499c5257de9b00d168dd49c04b8bbe7ef # 主实例启动随机字符串 master_replid2:0000000000000000000000000000000000000000 # 主实例启动随机字符串 master_repl_offset:0 # 主从同步偏移量,此值若是和上面的offset相同说明主从一致没延迟,与master_replid可被用来标识主实例复制流的位置. second_repl_offset:-1 # 主从同步偏移量2,此值若是和上面的offset相同说明主从一致没延迟 repl_backlog_active:0 # 复制积压缓冲区是否开启 repl_backlog_size:1048576 # 复制积压缓冲大小 repl_backlog_first_byte_offset:0 # 复制缓冲区偏移量的大小 repl_backlog_histlen:0 # 此值等于 master_repl_offset - repl_backlog_first_byte_offset,该值不会超过repl_backlog_size的大小 # CPU used_cpu_sys:1.90 # 将全部redis主进程在核心态所占用的CPU时求和累积起来 used_cpu_user:1.14 # 将全部redis主进程在用户态所占用CPU时求和累计起来 used_cpu_sys_children:0.01 # 将后台进程在核心态所占用的CPU时求和累计起来 used_cpu_user_children:0.00 # 将后台进程在用户态所占用CPU时求和累计起来 # Cluster cluster_enabled:0 # Keyspace db0:keys=7,expires=0,avg_ttl=0
golang操做redis的客户端包有多个好比redigo、go-redis,github上Star最多的莫属redigo。
github地址:https://github.com/garyburd/redigo 目前已经迁移到:https://github.com/gomodule/redigo
go get github.com/garyburd/redigo/redis import "github.com/garyburd/redigo/redis"
Conn接口是与Redis协做的主要接口,可使用Dial,DialWithTimeout或者NewConn函数来建立链接,当任务完成时,应用程序必须调用Close函数来完成操做。
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { conn,err := redis.Dial("tcp","121.36.43.223:6379") if err != nil{ fmt.Println("connect redis server:",err) return } fmt.Println(conn) defer conn.Close() }
经过使用Conn接口中的do方法执行redis命令,redis命令大全参考:http://doc.redisfans.com/
go中发送与响应对应类型:
Do函数会必要时将参数转化为二进制字符串
Go Type | Conversion |
---|---|
[]byte | Sent as is |
string | Sent as is |
int, int64 | strconv.FormatInt(v) |
float64 | strconv.FormatFloat(v, 'g', -1, 64) |
bool | true -> "1", false -> "0" |
nil | "" |
all other types | fmt.Print(v) |
Redis命令响应会用一下Go类型表示
Redis type | Go type |
---|---|
error | redis.Error |
integer | int64 |
simple string | string |
bulk string | []byte or nil if value not present. |
array | []interface{} or nil if value not present |
可使用GO的类型断言或者reply辅助函数将返回的interface{}转换为对应类型
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { conn,err := redis.Dial("tcp","121.36.43.223:6379") if err != nil{ fmt.Println("connect redis error:",err) return } defer conn.Close() _,err = conn.Do("SET","youmen","18") if err != nil{ fmt.Println("redis set error:",err) } name, err := redis.String(conn.Do("GET","youmen")) if err != nil{ fmt.Println("redis get error:",err) }else { fmt.Printf("Get name: %s \n",name) } }
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { conn,err := redis.Dial("tcp","121.36.43.223:6379") if err != nil{ fmt.Println("connect redis error:",err) return } defer conn.Close() _, err = conn.Do("SET", "name", "youmen") if err != nil { fmt.Println("redis set error:", err) } _,err = conn.Do("expire","name",10) if err != nil{ fmt.Println("set expire error:",err) return } name,err := redis.String(conn.Do("GET","name")) if err != nil{ fmt.Println("redis get error:",err) } else { fmt.Printf("GET name: %s \n",name) } }
package main import ( "fmt" "github.com/garyburd/redigo/redis" "reflect" ) func main() { conn,err := redis.Dial("tcp","121.36.43.223:6379") if err != nil{ fmt.Println("connect redis error:",err) return } defer conn.Close() _,err = conn.Do("MSET","name","youmen","age","22") if err != nil{ fmt.Println("redis mset error:",err) } res,err := redis.Strings(conn.Do("MGET","name","age")) if err != nil{ fmt.Println("redis get error",err) } else { res_type := reflect.TypeOf(recover()) fmt.Printf("res type : %s \n", res_type) fmt.Printf("MGET name: %s \n", res) fmt.Println(len(res)) } }
package main import ( "fmt" "github.com/garyburd/redigo/redis" "reflect" ) func main() { conn,err := redis.Dial("tcp","121.36.43.223:6379") if err != nil{ fmt.Println("connect redis error:",err) return } defer conn.Close() _, err = conn.Do("LPUSH", "list1", "l1","l2","l3") if err != nil { fmt.Println("redis mset error:", err) } res, err := redis.String(conn.Do("LPOP", "list1")) if err != nil { fmt.Println("redis POP error:", err) } else { res_type := reflect.TypeOf(res) fmt.Printf("res type : %s \n", res_type) fmt.Printf("res : %s \n", res) } }
package main import ( "fmt" "github.com/garyburd/redigo/redis" "reflect" ) func main() { conn,err := redis.Dial("tcp","121.36.43.223:6379") if err != nil{ fmt.Println("connect redis error:",err) return } defer conn.Close() _, err = conn.Do("HSET", "student","name", "wd","age",22) if err != nil { fmt.Println("redis mset error:", err) } res, err := redis.Int64(conn.Do("HGET", "student","age")) if err != nil { fmt.Println("redis HGET error:", err) } else { res_type := reflect.TypeOf(res) fmt.Printf("res type : %s \n", res_type) fmt.Printf("res : %d \n", res) } }