redis 是什么
redis是缓存数据库(key-value类型),和关系数据库(比如mysql)不同的是,redis的数据都是放在内存中,而不是硬盘里,虽然redis也有用到硬盘,但只是作为持久化。
为什么要用redis
- 快,因为redis存放在内存中,所以相比mysql,访问起来要快很多。
- 除了做缓存用以外,redis还可以用作排行榜,简单的消息队列,session,计算器,共同好友等
redis和mysql的区别
- redis的数据主要存放在内存中,而mysql主要存放在硬盘里,所以redis查起数据要比mysql快很多
- redis采用的是key-value的存储方式,而mysql采用的是关系表的存储方式;对于redis而言,我们只能根据key找到它的value,而不能像mysql那样子,select * from XXX where colunm = xx
redis的总数据结构
redis是一个key-value类型的数据库,key一般指字符串,而value可以分为很多数据结构,比如string,bytes,set, list,hash,zset,bitmap等。
我们可以把redis看做是一张大的哈希表,因此学习redis的时候我们可以类比Java中的HashMap。
在Redis中,hash表被称为字典(dictionary),采用了典型的链式解决冲突方法,即:当有多个key/value的key的映射值(每对key/value保存之前,会先通过类似HASH(key) MOD N的方法计算一个值,以便确定其对应的hash table的位置)相同时,会将这些value以单链表的形式保存;同时为了控制哈希表所占内存大小,redis采用了双哈希表(ht[2])结构,并逐步扩大哈希表容量(桶的大小)的策略,即:刚开始,哈希表ht[0]的桶大小为4,哈希表ht[1]的桶大小为0,待冲突严重(redis有一定的判断条件)后,ht[1]中桶的大小增为ht[0]的两倍,并逐步(注意这个词:”逐步”)将哈希表ht[0]中元素迁移(称为“再次Hash”)到ht[1],待ht[0]中所有元素全部迁移到ht[1]后,再将ht[1]交给ht[0](这里仅仅是C语言地址交换),之后重复上面的过程。
单线程
redis采用单线程架构和IO多路复用模型来实现高性能,多个客户端访问redis,redis会按照接收顺序把命令存放到一个队列里面,然后再按先到先得的顺序依次一个一个的执行这些命令。
单线程为什么还那么快
- 基于内存的查询,不涉及硬盘IO
- 数据结构简单,对数据操作也简单
- 使用多路I/O复用模型,非阻塞IO
- 多线程主要用于对cpu耗时比较高的操作,但是redis的每个CPU操作都比较简单,不存在CPU瓶颈,所以没有必要采用多线程
单线程的好处
- 代码简单
- 避免加锁
- 避免上下文切换
redis的value的数据结构
一 字符串 STRING
字符串是redis最基本的数据结构,其实value的值不止可以是字符串,还可以是复杂字符串(比如json),或者数字,二进制(比如把一个对象序列化),但是它的大小不能超过512M。
常用命令
- 设置值
set key value [ex seconds] [px milliseconds] [nx | xx]
ex seconds 设置有效期 以秒为单位
px milliseconds 设置有效期 以毫秒为单位
nx 只有键值不存在,才能设置成功,否则返回失败
xx 只有键值存在,才能设置成功,否则返回失败
- 获取值
get key
- 批量
mget key1 key2
mset key1 value1 key2 value2
- 计数
incr key
自增
decr key
自减
incrby key increment
自增一个值
decrby key decrement
自减一个值
典型使用场景
- 利用
setnx key value
可用来开发分布式锁 - 可用于对象缓存,如session共享
- 计数器
二 哈希表 HASH
对于对象,比如一个user,user有name,age等字段;如果用string作为数据结构来存储user(转成json),这样子,我们修改name的时候,要把整个user拿出来,然后再修改name字段,即使我们不需要修改age字段;如果用哈希表的话(跟我们Java中的map极为相似),我们只需要修改name字段即可。
常用命令
- 设置值
hset key field value
- 获取值
hget key field
获取某个字段的值
hkeys key
获取key的所有字段
hlen key
获取key中字段的个数
三 列表 LIST
list(列表),用来存储多个有序的字符串,每个列表一共可以存储2^32 -1个元素。
redis中列表类似于上图的一个东西;lpush为向左添加元素,rpush为向右添加元素,lpop为向左取出元素,rpop为向右取出元素;如果我们想要队列(先进先出的效果),我们可以lpush+rpop/rpush+lpop;如果我们想要栈的效果,我们可以lpush + lpop/rpush + rpop。可以用llen来获取列表的长度。
使用场景
- 队列和栈
- 消息队列
四 集合 SET
set(集合),用以保存多个不重复元素的集合,相当于Java的set。
常用命令
- 添加元素
sadd key value
- 删除元素
srem key value
集合操作
- 求两个集合的共同元素
sinter key1 key2
- 求两个集合的和
sunion key1 key2
- 求两个集合的不同元素
sdiff key1 key2
这里是指key1集合除1去和key2相同元素以外,还剩下什么元素
使用场景
- 利用
sinter
,可以做到和朋友圈相同的效果,即只能看到共同好友的评论。
五 有序集合 ZSET
有序集合常用于排行榜。由图可以看出,它的每个元素分为两个字段,一个score,一个member,member我们可以对应排行榜里面的某个人,score可以对应这个人的分数。说它有序,不是说元素按先到先得排序,而是说元素按照score的大小排序。
跳表
实现元素可以按大小排序的数据结构有很多种,比较复杂的有红黑树等,而简单的也有普通的链表,在ZSet中,它是根据跳表来实现的。为什么redis要用跳表来实现zset呢?因为红黑树太复杂,每次增删耗时较大;普通链表又太简单,查询效率比较低;跳表就是为了弥补普通链表查询效率较低而开发的。
普通的链表的每个元素有且只有一个指向下一个元素的指针,但是在跳表中的每个元素可能有1-32个指针指向不同的下一个元素。比如下面:
元素 3 可能只有一个指针,但是元素7吧4个指针,元素19有2个指针。
为什么跳表的查询效率要比普通链表高呢? 先看看跳表是怎么查数据的,比如我们要查23这个元素,首节点最高层的指针找到了7这个节点,然后7的最高层指针找不到元素了,所以换回第二高的指针,这次找到37这个元素,但是因为37比23大,所以重新返回7元素,换回倒数第三高的指针,这次找到19,19比23要小,所以从19最高层指针找到37元素,37比23大,返回,以此类推,最后在最底层指针找到23位于22和26之间。
Linux安装redis
- 先查询Linux是否安装了GCC,因为redis是用c写的,需要GCC做c的编译器
gcc -v
查询是否安装
yum install gcc
安装gcc - 下载redis
wget wget http://download.redis.io/releases/redis-3.2.6.tar.gz
- 解压
tar -zxvf redis-3.2.6.tar.gz
- 进入redis主目录
cd redis-3.2.6
- 编译
make
如果编译失败需要把主目录删掉重新解压 - 安装
cd src & make install
- 运行redis服务器
./src/redis-server redis.conf
这里运行了服务器以后会发现redis只在前台运行,一旦Ctrl+c或者退出Linux,redis程序就会消失,我们需要修改redis.conf 里面的daemonize no
改为daemonize yes
- 运行redis客户端
./src/redis-cli -p 6379
./src/redis-cli [-h host] [-p port] [-a passport]
这里host表示主机ip,port表示端口,passport表示密码;新安装的redis一般都是用的6379的端口,我们可以在redis.conf里面修改端口。