Redis基本知识
redis-server #启动redis服务
shutdown #停止redis服务
redis-cli -p 6379 #使用redis连接客户端
使用redis客户命令
keys * #查看所有key
flushdb ##清空当前数据库的内容
flushall ##清空所有数据库的内容
select 3 #切换数据库
DBSIZE #查看DB大小
redis-benchmark性能测试
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
Redis是单线程!
官方表示,Redis基于内存操作,CPU不是Redis的性能瓶颈
Redis是C语言写的,官方数据是1万 的QPS,完全不同于同样的使用key-value的Memecache差!
1.误区1:高性能服务器必须多线程?
2.误区2:多线程(CPU一定比单线程效率高!
先去CPU>内存>了解硬盘的速度!
EXISTS name #判断当前情况Key是否存在
move name 1 #移除当前Key
EXPIRE name 10 #设置Key过期时间为10秒
ttl name #查看当前key的过期时间
type name #查看当前key的类型
String(字符串)
set key1 v1 #设置值
get key1 #获得值
EXISTS name #判断当前情况Key是否存在
APPEND key1 "hello" #添加字符串,如果目前key不存在,相当于setkey
STRLEN key1 #获取字符串的长度
set views 0 #初始浏览量为0
incr views #自增1 浏览量变为1
decr views #自减1 浏览量变为0
INCRBY views 10 #可设置步长,指定增量!
#字符串范围 range GETRANGE key1 0 3 #截取字符串 [0,3] GETRANGE key1 0 -1 #截取所有字符串 (与get key1一样)
#替换 SETRANGE key2 1 xx #替换指定位置开始的字符串!
setex(set with expire) #设置过期时间 setnx(set if exist) #没有再设置 (常用于分布式
锁) setex key3 30 "hello" #设置key的值为hello,30秒后过期 setnx mykey "redis" #如果mykey不存在,创造mykey setnx mykey "MongoDB" #如果mykey存在,创建m失败
#mset mget mset k1 v1 k2 v2 k3 v3 #同时创建多个值 mget k1 v1 k2 v2 k3 v3 #同时获得多个值 msetnx k1 v1 k4 k4 #msetnx是原子操作,要么一起成功,要么一起失败
#对象 set user:1 {name:zhangsan,age:3} #设置一个user:1 对象 值为json保存对象的字符! mset user:1:name zhangsan user:1:age 2 #设置多个对象值 mget user:1:name user:1:age #获取多个对象值
#getset getset db redis #如果不存在,则返回nil,并设置值 getset db mongodb #如果存在值,获得原始值并设置新值
List(列表)
LPUSH list one #将一个或多个值插入列表头部(左) RPUSH list one #一个或多个值,插入列表头(右) LRANGE list 0 -1 #获取list中值 LRANGE list 0 1 #获取list中具体的值 ######################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################## LPOP list #移除list第一个元素(左) RPOP list #移除list第一个元素(右) LINDEX list 0 #通过下标获得list一个值! Llen list #返回列表长度 lrem list 1 three #移除list集合中指定个数的value,精确匹配
#trim ltrim mylist 1 2 #通过下标截取指定长度,此list已经改变,其他元素只被截取!
#rpoplpush 将列表的最后一个元素移动到新列表中 rpoplpush mylist myotherlist #将列表的最后一个元素移动到新列表中
lset #用另一个值替换列表中指定的标的值,更新操作 lset list 0 item 如果没有列表,我们会更新它们 lset list 0 item #如果存在,更新当前下表值
linsert #特定的value插入列把你的元素的前面或后面! linsert mylist before "world" "other" (前面) linsert mylist after "world" "new" (后面)
Set(无序无重复集合)
sadd myse "hello" #set集合中添加元素
SMEMBERS myset #查看指定set的所有值
SISMEMBER myset hello #判断某一个值是不是在set集合中
scard myset #获取set集合中内容元素个数
srem myset hello #移除set集合中的指定元素
srandmember myset #随机抽选出一个元素
srandmember myset 2 #随机抽选出指定个数的元素
spop myset #随机删除一些set集合中的元素
smove myset myset2 "kuangshen" #将一个指定的值移动到另外一个set集合中
SDIFF key1 key2 #差集
SINTER key1 key2 #交集 共同好友就可以这样实现
SUNION key1 key2 #并集
Hash
hset myhash field1 change #set一个具体key-value
hget myhash field1 #获取一个字段值
hmset myhash field1 change field2 hello #set多个key-value,如果value存在会被覆盖,与String同理
hmget myhash field1 field2 #获取多个字段值
hgetall myhash #获取全部的数据
hdel myhash field1 #删除hash指定的key字段,对应的value值也会随之消失
hlen myhash #获取hash表的字段数量
hexists myhash field1 #判断hash里的指定字段是否存在
hkeys myhash #只获得所有field
hvals myhash #只获得所有value
hset myhash field3 5 #指定增量
HINCRBY myhash field3 1 #为hash指定的字段增加数量
HINCRBY myhash field3 -1 #为hash指定的字段减少数量
hsetnx myhash field4 hello #如果不存在则可以设置
hsetnx myhash field4 world #如果存在则不能设置
hset user:1:name zhangsan user:1:age 2 #设置多个对象值
hget user:1:name user:1:age #获取多个对象值
Zset (有序集合)
zadd myset 1 one #添加一个值
zadd myset 1 one 2 two #添加多个值
zrange myset 0 -1 #获取所有的值
zrangebyscore salary -inf +inf #显示全部的用户 从小到大
zrevrange salary 0 -1 #显示全部的用户 从大到小
zrangebyscore salary -inf +inf withscores #显示全部的用户 并且附带工资
zrangebyscore salary -inf 2500 withscores #显示工资小于2500员工的升序排序
zrem salary change #移除有序集合中的指定元素
zcard salary #获取有序集合的个数
zcount myset 1 2 #获取指定区间的成员数量
三种特殊数据类型
geospatial 地理位置
GEOADD
# getadd 添加地理位置
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 150.45 22.23 chongqing
(integer) 1
GEOPOS
127.0.0.1:6379> geopos china:city beijing #获取指定的城市的经度和纬度
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
GEODIST
两人之间的距离!
单位:
· m 表示单位为米
· km 表示单位为千米
· mi 表示单位为英里
· ft 表示单位为英尺
127.0.0.1:6379> geodist china:city beijing shanghai km #查看上海到北京的直线距离
"1067.3788"
GEORADIUS
GEORADIUS china:city 121 31 1000 km #以121,31为中心,寻找方圆1000km内的城市
127.0.0.1:6379> georadius china:city 121 31 500 km
1) "shanghai"
georadius china:city 121 31 500 km withdist #显示到中间距离的位置
127.0.0.1:6379> georadius china:city 121 31 500 km withdist
1) 1) "shanghai"
2) "51.5509"
georadius china:city 121 31 500 km withcoord #显示他人的定位信息
127.0.0.1:6379> georadius china:city 121 31 500 km withcoord
1) 1) "shanghai"
2) 1) "121.47000163793563843"
2) "31.22999903975783553"
georadius china:city 121 31 500 km withdist withcoord count 1
#筛选出指定的结果!
127.0.0.1:6379> georadius china:city 121 31 500 km withdist withcoord count 1
1) 1) "shanghai"
2) "51.5509"
3) 1) "121.47000163793563843"
2) "31.22999903975783553"
GEORADIUSBYMEMBER
#找出位于指定元素周围的其他元素!
georadiusbymember china:city beijing 1000 km
127.0.0.1:6379> georadiusbymember china:city beijing 1000 km
1) "beijing"
GEOHASH 命令 -返回一个或多个位置元素的GEOHASH表示
#将二维的经纬度转换为一维的字符串,如果两个字符串越接近,那么距离越近
geohash china:city beijing
127.0.0.1:6379> geohash china:city beijing
1) "wx4fbxxfke0"
GEO底层的实现原理其实就是Zset 我们可以使用Zset命令来操作GEO
zrange china:city 0 -1
127.0.0.1:6379> zrange china:city 0 -1
1) "shanghai"
2) "beijing"
3) "chongqing"
zrem china:city beijing #移除指定元素
Hypeloglog 统计不重复
PFADD mykey
PFADD mykey a b c d e f g h i j #创建第一组元素 mykey
PFCOUNT mykey #统计mykey并集数量
#因为第一组不存在重复的值,因此统计出的并集数量为10
PFADD mykey2 i j z x c v b n m #创建第二组元素 mykey2
PFCOUNT mykey2 #统计mykey2并集数量
PFNERGE mykey3 mykey mykey2 #使用mykey3将mykey和mykey2整合在一起并统计
PFOUNT mykey3 #再次统计并集数量
如果允许容错,那么一定可以使用Hypeloglog!
如果不允许容错,就使用set或者自己的数据类型即可!
Bitmap
位存储
统计用户信息,活跃,不活跃!登录、未登录!打卡、365打卡!两个状态的,都可以使用Bitmap!
Bitmap 位图,数据结构!都是操作二进制来进行记录,就只有0和1两个状态!
365 天 = 365 bit 1字节 = 8bit 46个字节左右!
使用bitmap来记录 周一到周日的打卡!
周一:1 周二:0 周三:0 周四:1 ...
127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 0
(integer) 0
127.0.0.1:6379> setbit sign 2 0
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 0
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0
查看某一天是否有打卡
127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379> getbit sign 6
(integer) 0
统计操作,统计打卡的天数
127.0.0.1:6379> bitcount sign #统计这周的打卡记录,就可以看到是否有全勤!
(integer) 3
事务
执行事务
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec #执行事务
1) OK
2) OK
3) OK
放弃事务
127.0.0.1:6379> multi #开启事务
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> discard #取消事务
OK
127.0.0.1:6379> get k4 #事务队列中命令都不会被执行!
(nil)
编译型异常(代码有问题!命令有错!),事务中所有的命令都不会被执行!
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> getset k3 #错误的命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> set k5 v5
QUEUED
127.0.0.1:6379> exec #执行事务,发现事务中有错误!
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5 #所有的命令都没有被执行!
(nil)
运行时异常(1/0),如果事务队列中存在语法性,那么执行命令的时候,其它命令仍可以正常执行的,错误命令抛出异常
127.0.0.1:6379> set k1 "v1"
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1 #会执行的时候失败!
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range #虽然第一条命令报错了,但是依旧正常执行成功了!
2) OK
3) OK
4) "v3"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> get k3
"v3"
监控
悲观锁:
· 很悲观,认为什么时候都会出问题,无论做什么都会加锁!
乐观锁:
· 很乐观,认为什么时候都不会出问题,所以不会上锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据,
· 获取version
· 更新的时候比较 version
Redis测监视测试
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money #监视 money对象
OK
127.0.0.1:6379> multi #事务正常结束,数据期间没有发生变动,这个时候就正常执行成功!
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20
测试多线程修改值,使用watch当做redis的乐观锁操作!
127.0.0.1:6379> watch money #监视 money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> exec #执行之前另外一个线程修改了我们的值,这个时候,就会导致事务执行失败
(nil)
如果修改失败,获取最新的值就好
127.0.0.1:6379> unwatch #如果大仙事务执行失败,就先解锁
OK
127.0.0.1:6379> watch money #获取最新的值,再次监视,select version
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 1
QUEUED
127.0.0.1:6379> incrby money 1
QUEUED
127.0.0.1:6379> exec #对比监视的值是否发生了变化,如果没有变化,那么可以执行成功,如果变量就执行失败!
1) (integer) 999
2) (integer) 1000
Jedis (使用java来操作Redis)
测试
1.导入对应的依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
2.编码测试
· 连接数据库
· 操作命令
· 断开连接
package com.change;
import redis.clients.jedis.Jedis;
public class TestPing {
public static void main(String[] args) {
Jedis jedis = new Jedis("127.0.0.1",6379);
System.out.println(jedis.ping());
}
}
输出:
PONG
常用的API
String
List
Set
Hash
Zset
SpringBoot整合
整合测试
1.导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.配置连接
spring.redis.host=127.0.0.1
spring.redis.port=6379
3.代码测试
@SpringBootTest
class Redis02SpringbootApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
//操作字符串
redisTemplate.opsForValue().set("myk","change");
System.out.println(redisTemplate.opsForValue().get("myk"));
}
}
源码分析:
@Bean
@ConditionalOnMissingBean(
name = {
"redisTemplate"}
)
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
return new StringRedisTemplate(redisConnectionFactory);
}
关于对象的保存:
@Test
void test() throws JsonProcessingException {
User user = new User("change",18);
String json = new ObjectMapper().writeValueAsString(user); //这条语句的意思是将对象序列化,没有这条语句会导致运行报错,
redisTemplate.opsForValue().set("user",json);
System.out.println(redisTemplate.opsForValue().get("user"));
}
我们自己编写的RedisTempleat
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<String,Object>();
template.setConnectionFactory(redisConnectionFactory);
//配置具体的序列化方式
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
Redis.conf详解
单位
1.配置文件 unit单位 对大小写不敏感
包含
可以使用多个配置文件
网络
bind 127.0.0.1 #绑定的ip
protected-mode yes #保护模式
port 6379 #端口
通用 GENERAL
daemonize yes #以守护进程的方式运行,默认是no,我们需要自己手动改为yes
pidfile /var/run/redis_6379.pid #如果以后台的方式运行,我们就需要指定一个pid文件
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel notice #日志级别
logfile "" #日志的文件位置名
databases 16 #数据库的数量
always-show-logo yes #是否总是显示log
快照
持久化,在规定的时间内,执行了多少次操作,则会持久化到文件.rdb .aof
redis是内存数据库,如果没有持久化,那么数据断电即失!
#如果900内,如果至少一个key进行了修改,我们将持久化操作
save 900 1
#如果300内,如果至少十个key进行了修改,我们将持久化操作
save 300 10
#如果60内,如果至少一万个key进行了修改,我们将持久化操作
save 60 10000
stop-writes-on-bgsave-error yes #持久化如果出错,是否还需要继续工作!
rdbcompression yes #是否压缩rdb文件,需要消耗一些cpu资源!
rdbchecksum yes #保存rdb文件的时候,进行错误的检查校验!
dir ./ #rdb文件保存的目录!
REPLICATION 复制
SECURITY 安全
可以在这里设置redis的密码,默认是没有密码!
127.0.0.1:6379> config set requirepass "123456" #设置redis的密码
ok
127.0.0.1:6379> config get requirepass #发现所有的命令都没有权限了
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth 123456 #使用密码进行登录
ok
127.0.0.1:6379> config get requirepass
1)"requirepass"
2)"123456"
CLIENTS 限制
maxclients 10000 #设置能连接上redis的最大客户端的数量
maxmemory <bytes> #redis 配置最大的内存容量
maxmemory-policy noeviction #内存到达上限之后的处理策略
1、volatile-lru:只对设置了过期时间的key进行LRU(默认值)
2、allkeys-lru : 删除lru算法的key
3、volatile-random:随机删除即将过期key
4、allkeys-random:随机删除
5、volatile-ttl : 删除即将过期的
6、noeviction : 永不过期,返回错误
APPEND ONLY 模式 aof配置
appendonly no #默认是不开启aof模式的,默认是使用rdb方式持久化的,在大部分的情况下,rdb完全够用!
appendfilename "appendonly.aof" #持久化的文件的名字
# appendfsync always #每次修改都会 sync 消耗性能
appendfsync everysec #每秒执行一次 sync 可能会丢失1s的数据!
# appendfsync no #不执行sync,这个时候操