redis是由C语言编写的高性能键值对数据库,支持的键值数据类型为:
- 字符串(String)
- 列表(List)
- 有序集合(sorted set)
- 集合(set)
- 散列 (hash)
这些数据类型都支持 push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,Redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
redis的优势: - 性能极高: Redis能读的速度是110000次/s,写的速度是81000次/s。
- 丰富的数据类型
- 原子性:Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
- 丰富的特性:Redis还支持 publish/subscribe, 通知, key过期等等特性。
主要进行内容缓存,用于处理大量数据的高访问负载。
一 、redis数据类型及操作
1.1 string
string中的一个key对应一个value,value最大可达512Mb
常用命令如下:
#赋值
set key value
#取值
get key
#取值后赋值
getset key value
#删除
del key
#value追加内容
append key string
示例如下:
1.2 list
list的顺序是按照插入的顺序,可以在头部跟尾部插入数据,如果是在list的两头进行操作,那么效率是很高的,但是如果在list中,则会耗费一定时间。
list常用命令
#两端添加 可以加入多个value
lpush key value [value ...]
rpush key value [value ...]
#查看列表
lrange key start stop
#指定位置的push index是从0开始的
lset key index value
# 两端弹出
lpop key
rpop key
#从source列表右边删除一个value,并把这个value存储进入distination列表中
#适用于消息发布过程中的备份操作
rpoplpush source distination
#获取元素列表个数
llen key
#count>0,从列表的左端开始删除 值等于 value,一共删除count个
#count<0,从列表的右端开始删除 值等于 value,一共删除count个
#count=0,删除 整个列表中所有 值等于 value
lrem key count value
示例如下:1.3 sorted set
sorted set跟set是比较类似的,集合中不允许出现重复的元素,那么有啥区别呢?sorted set有顺序,从小到大排序,更新操作非常快,访问数据也非常高效。
应用场景:游戏排名、微博热点
常用命令
#获得元素
zscore key member
获取有序集合中的某个元素的score值
zrange key start stop [withscores]
zrangebyscore key mim max [withscores] [limit offset count]
#添加元素
zadd key score member [score member ...]
#删除元素
zrem key member [member...]
#删除score在指定范围内的元素
zremrangebyscore key min max
#将元素的score只增加count
zincrby key count member
#查看元素的score
zscore key member
#指定score值内的元素数量
zcount key min max
示例如下:
1.4 set
和list类型不同的是,set集合中不允许出现重复的元素,set最大可以包含的元素是 4294967295 。注意,set中是没有顺序的。
用于维护用户对象的唯一性,以及处理数据对象之间的关联关系,可以进行并集交集差集运算。比如购买A产品的用户ID,放在一个set中,购买另外一个B产品的用户ID,放在另外一个set中,这样就很方便计算同时购买两个产品的用户等。
常用命令:
#添加删除元素
sadd key member [member ...]
srem key member [member ...]
#获取集合中的元素
smembers key
#差集运算
sdiff key1 [key ...]
#交集运算
sinter key [key...]
#将多个集合的交集,并把结果存储在destination集合中
sinterstore destination key [key...]
#并集运算
sunion key [key...]
#查看member在key中是否存在
sismember key member
#查看集合元素个数
scard key
示例如下:1.5 hash
hash可以存储多个键值对之间的映射,可以看做是一个小型的redis
常用命令
#赋值
hset key field value
#多个field赋值,若field存在,则修改数据
hmset key field value [field value ... ]
#取值
hget key field
#获取多个field值
hmget key field [field ...]
#获取同一field 下的所有field跟value
hgetall key
#删除
hdel key field[field ...]
#查看key中的field是否存在,存在返回1,不存在返回0
hexists key field
#获取key中field的个数
hlen key
#获取keys中的所有field
hkeys key
#获取key中的所有value
hvals key
示例如下:二、keys的通用操作
- keys * :查看所有的keys
- keys test? : 查看test开头,后面只有一个字符的key
- keys test* :查看test开头的keys
- del key[key ...] : 删除多个key
- exists key :判断key是否存在,存在返回1,不存在返回0
- rename key newkey: 重命名
- expire key seconds:设置某个key的生命期
- ttl key:查看该key的生命还剩下多少
- type key:查看该key的类型
三、Redis的特性
3.1 多数据库
redis最多支持16个数据库,下标0-15表示第几个数据库。默认是在0号数据库。切换数据库可以通过select dbnumber 来切换,也可以通过move 来移动key从当前数据到指定的数据库。3.2 事务
事务的指令:multi、exec、discard。redis中,如果某个命令执行失败,后面的命令还会继续执行。multi,开启事务,这个指令后的指令默认为在同一个事务内,exec等同于提交,discard等同于回滚。3.3订阅发布
Redis 的pub/sub机制与23种设计模式中的观察者设计模式极为类似。但Redis对于这个机制的实现更为轻便和简结,没有观察者模式的那么复杂的逻辑考虑而仅仅需要通过两个Redis客户端配置channel即可实现,因此它也仅仅做了消息的”发布”和”订阅”的实现,而在实际处理这类场景时遇到的情况根本没有考虑到。
-
数据可靠性无法保证
一个redis-cli发布消息n个redis-cli接受消息。消息的发布是无状态的,即发布完消息后该redis-cli不在理会该消息是否被接受到,是否在传输过程中丢失,即对于发布者来说,消息是”即发即失”的。 -
扩展性太差
不能通过增加消费者来加快消耗发布者的写入的数据,如果发布者发布的消息很多,则数据阻塞在通道中已等待被消费着来消耗。阻塞时间越久,数据丢失的风险越大(网络或者服务器的一个不稳定就会导致数据的丢失) -
资源消耗过高
在pub/sub中消息发布者不需要独占一个Redis的链接,而消费者则需要单独占用一个Redis的链接,在java中便不得独立出分出一个线程来处理消费者。这种场景一般对应这多个消费者,此时则有着过高的资源消耗。
所以我们一般用其它消息队列来进行订阅和发布功能,例如rabbitMQ
四、 Redis的持久化
redis的高性能是因为数据都在内存中,如果数据库重启,则所有数据都会丢失,redis支持以下持久化方法
- RDB持久化
默认支持,在指定的时间内,把内存的数据写入磁盘 - AOF持久化
以日志的形式记录每一个操作,启动的时候,重新执行所有log
当然若我们的redis只用来作缓存使用,则不用进行持久化。
4.1 RDB持久化
- 优势
- redis数据库仅包含一个文件,对于文件备份是非常方便的,如果系统出现灾难时,较容易恢复
- 数据量很大的时候,启动速度快
- 劣势
- 不能保证数据无丢失
- 子进程完成持久化工作,如果数据集很大的时候,可能造成短时间内redis服务器停止对外服务
-
配置
通过查看redis的配置文件,我们可以看到一些声明的作用save 900 1 //900秒内至少有1个数据变化,则进行持久化 save 300 10//300秒内至少有10个数据发生变化,则进行持久化 save 60 10000//60秒内至少有1w个数据发生变化,则进行持久化 stop-writes-on-bgsave-error yes//当持久化出现问题时保证redis正常工作 rdbcompression yes//压缩文件 dbfilename dump.rdb //持久化到指定文件 dir ./ //指定持久化的目录,默认为当前文件夹
4.2 AOF持久化
- 优势
- 更高的数据安全性,每秒同步,最高丢失1s数据;每操作数同步,每次发生数据的变化都会立即记录到磁盘中,性能最低
2.append追加文件备份,备份过程中出现问题,不会破坏之前的日志备份,如果写入了一半数据,然后出现系奔溃的问题,在redis下一次启动之前,可以通过redis_check_aof工具解决数据一致性问题 - 如果日志备份过大,redis会自动启动日志重写机制,append过程中,会把备份数据写入到老的备份文件中,并且会用一个新文件,记录此期间的修改数据语句
劣势
1.相同数量的数据集文件,比RDB的要打
2.AOF效率低于RDB
3.需要人员配置,非默认配置-
配置
在redis.conf中进行配置appendonly yes #启动appendonly,开启AOF备份 appendfilename "appendonly.aof" #AOF备份的文件名 appendfsync always #每个修改操作同步备份一次 appendfsync everysec#1s同步备份一次 appendfsync no #不同步
五、java使用redis
5.1 导入redis的依赖
<!--redis核心包-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.0</version>
</dependency>
5.2 编写redis的工具类
public class RedisCache {
private static JedisPool jedisPool; // 池化管理jedis链接池
static {
JedisPoolConfig config = new JedisPoolConfig();
config.setBlockWhenExhausted(true);
config.setMaxIdle(64);//最大空闲数:空闲链接数大于maxIdle时,将进行回收
config.setMaxTotal(64); //设置最大连接数
config.setMaxWaitMillis(10000);//最大等待时间:单位ms
config.setTestOnBorrow(false); //使用连接时,检测连接是否成功
config.setTestOnReturn(true); //返回连接时,检测连接是否成功
//TODO 初始化连接池
/**
* 其他主机远程访问redis配置:redis.conf文件
* 1.注释 bind 127.0.0.1 该配置限制本地访问
* 2. protect model no 不保护模式
*3. 60000为过期时间
*/
jedisPool = new JedisPool(config, "127.0.0.1", 6379, 60000);
}
//向redis写字典数据
public void setRedisData(String userKey, String empData, int seconds) {
System.out.println("将userKey:" + userKey + ",empData:" + empData + "写入Redis");
try {
Jedis jedis = jedisPool.getResource();
try {
// 主键为:userKey
jedis.setex(userKey, seconds, empData);//60*60*24*7 7天过期
} finally {
jedis.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
//获取redis字典数据
public String getRedisData(String userKey) {
try {
Jedis jedis = jedisPool.getResource();
try {
String data = jedis.get(userKey);
System.out.println("redis信息获取:" + userKey + ",的值为:" + data);
return data;
} finally {
jedis.close();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//用户登出,删除redis
public void delRedisKey(String userKey) {
try {
Jedis jedis = jedisPool.getResource();
try {
System.out.println("删除redis的key:" + userKey);
jedis.del(userKey);
} finally {
jedis.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}