本文根据黑马程序员的课程资料与百度搜索的资料共同整理所得,仅用于学习使用,如有侵权,请联系删除
1.搭建gcc依赖
# 若yum被packagekit(目前不需要)占用,用该命令解决:
sudo rm -f /var/run/yum.pid
yum install -y gcc tcl
2.上传安装包并解压:采用虚拟机共享文件夹(VMware虚拟机设置-选项)
3.解压后进行编译
make && make install
默认安装路径:/usr/local/bin
在任意目录中采用
redis-server
即可启动,但需要一直挂着命令端
1.如想要后台启动,修改Redis配置文件(redis.conf)(详见2.3)
bind 0.0.0.0 # 监听地址,任意IP可访问(默认127.0.0.1,只能在本地访问)
daemonize yes # 守护进程,yes即可后台运行
requirepass xxx # 密码
port 6379 # 监听端口
dir . # 工作目录,运行redis-server时的命令、日志、持久化保存目录(默认当前目录)
databases 1 # 数据库使用数量
maxmemory 512mb # 可使用的最大内存
logfile "" # 日志文件(默认为空,不记录日志)
2.启动Redis
cd /usr/local/src/redis-6.2.7
redis-server redis.conf # 启动
3.查看服务是否启动
ps -ef | grep redis
4.停止服务
x.x.x.x:xxxx > shutdown
1.创建系统服务文件
vi /etc/systemd/system/redis.service
内容如下:
[Unit]
Description=redis-server
After=network.target
[Service]
Type=forking
# 这行配置内容要根据redis的安装目录自定义路径
ExecStart=/usr/local/bin/redis-server /usr/local/src/redis-6.2.7/redis.conf
PrivateTmp=true
[Install]
WantedBy=multi-user.target
保存系统服务文件
systemctl daemon-reload
实现开机自启
systemctl enable redis
查看服务状态
systemctl status redis
停止开机自启
systemctl stop redis
(在基础篇仅需了解 用docker如何安装软件 即可)
Docker 快速安装软件 - Docker 快速入门 - 易文档 (easydoc.net)
Docker 1小时快速上手教程,无废话纯干货_哔哩哔哩_bilibili
docker自带linux子系统,直接安装上redis相应版本即可
Releases · lework/RedisDesktopManager-Windows (github.com)(rdm-2021.9.zip)
(releases下载慢的话,复制相应的下载链接用该网站下载即可:下载 (serctl.com))
SQL(关系型数据库) | NoSQL(非关系型数据库) | |
---|---|---|
结构化(预先设置、强约束) | 非结构化(约束小) | 数据结构 |
关联的(表与表联系) | 无关联的(嵌套形式) | 数据关联 |
SQL查询 | 非SQL查询 | 查询方式 |
ACID:原子性(atomicity)、一致性(consistency)、隔离性(isolation)、持久性(durability) | BASE(基本一致/无事务) | 事务特性 |
磁盘(查询性能低) | 内存(查询性能高) | 存储方式 |
垂直(存储总量固定不变) | 水平(数据拆分) | 扩展性 |
数据结构固定 / 相关业务对数据安全性、一致性要求较高 | 数据结构不固定 / 对数据安全性、一致性要求不高 / 对性能要求较高 | 使用场景 |
Redis(Remote Dictionary Server),基于内存的健值型NoSQL数据库
特征:键值、单线程(原子性)、低延迟(基于内存…)
原子性
Redis每个单个命令都是原子操作,要么都成功,要么都失败,所谓原子操作是指不会被线程调度机制打断的操作;
这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。
(1)在单线程中, 能够在单条指令中完成的操作都可以认为是"原子操作",因为中断只能发生于指令之间。
(2)在多线程中,不能被其它进程(线程)打断的操作就叫原子操作,Redis单命令的原子性主要得益于Redis的单线程。
案例:
java中的i++是否是原子操作?
不是
i=0;两个线程分别对i进行++100次,值是多少?
最小值为:2
最大值为:200
在次范围内所有的结果都是正确的;
具体分析如下:
1.最小值的情况
线程A执行第一次i++操作,取出内存中的i,值为0;放到cpu1寄存器中执行加1操作(不写回内存),寄存器中的值为1,内存中的值为0;
线程B执行第一次i++操作,取出内存中的i,值为0;放到cpu2寄存器中执行加1操作(不写回内存),寄存器中的值为1,内存中的值为0;
线程A继续执行第99次i++,每执行一次都将其值写回内存,此时cpu1寄存器中的值为99,内存中的值为99.
线程B由于未写回内存,继续执行第一次i++,将其值放入内存,此时cpu2寄存器中的值为1,内存中的值为1(线程B写回时覆盖了原来的99);
线程A执行第100次i++,此时cpu1寄存器中的值为2(不写入内存),内存中的值为1;
线程B继续执行完所有的操作,此时cpu2寄存器中的值为100,内存中的值为100;
此时A线程进行最后一次操作,将cpu1寄存器中的值2放入内存,此时内存中的值为2;
即此操作的最小值为2;
2.最大值200 即两个线程交替进行互不干扰
多路IO复用:多个进程像Redis服务发起请求,不会一直阻塞着等待Redis服务返回数据,而是先释放资源,让CPU去干别的事情,不占用资源。
Units:配置大小单位,开头定义了一些基本的度量单位,只支持bytes,不支持bit,大小写不敏感
includes: 类似jsp中的include,多实例的情况可以把公用的配置文件提取出来
bind:是绑定本机的IP地址,如果指定了bind,则说明只允许来自指定网卡的Redis请求。如果没有指定,就说明可以接受来自任意一个网卡的Redis请求。
port:端口
protected-mode:就是只有【本机】可以访问redis,其他任何都不可以访问redis。
这个安全层开启必须满足三个条件,不然安全层处于关闭状态:(1)protected-mode yes(处于开启)(2)没有bind指令。(3)没有设置密码。
timeout:一个空闲的客户端维持多少秒会关闭,0表示关闭该功能。即永不关闭。
tcp-keepalive:对访问客户端的一种心跳检测,每个n秒检测一次,单位为秒(如果设置为0,则不会进行Keepalive检测),建议设置成60。
daemonize:是否设置为后台进程,即挂在后台,yes为开启,no为关闭。
pidfile:存放pid文件的位置,每个实例会产生一个不同的pid文件。
loglevel:指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为notice(生产环境选择notice 或者warning)
logfile:日志文件输出路径
databases:设定库的数量 默认16
requirepass:永久设置密码(临时设置:config get requirepass config set requirepass xxx)
maxclients:设置redis同时可以与多少个客户端进行连接。
maxmemory:建议必须设置,否则,将内存占满,造成服务器宕机(bytes)
maxmemory-policy(移除规则):
redis-cli -h [host] -p [port] -a [password]
redis-cli -h 127.0.0.1 -p 6379 -a 123
# 切换数据库(0为开始)
select [数据库名称]
基本类型:String、Hash(哈希表)、List(链表)、Set(无序不重复表)、SortedSet(有序不重复表)
help @[group]
help @generic
通用命令 | 示例 |
---|---|
KEYS:查看符合模板的所有key (数据量较大、在主节点时不要使用,避免阻塞其他请求) | keys k* |
DEL:删除指定key (即使缺少某些key或都缺少也能执行) | del k1 k2 k3 k4 |
EXISTS:判断是否存在 | exists k1 |
EXPIRE:给key设置有效期(s) | expire k1 20 |
TTL:查看key剩余有效期(-1永久有效、-2已失效、x还剩x秒) | ttl k1 |
String命令 | 示例 |
---|---|
SET | set key value set key value nx set key value ex 10 |
GET | get key |
MSET | mset k1 v1 k2 v2 |
MGET | mget k1 k2 |
INCR | incr key |
INCRBY | incrby key -1 |
INCRNYFLOAT | incrby key 0.1 |
SETNX(键不存在时才会操作) | setnx key value |
SETEX(设置过期时间) | setex key seconds value |
Hash命令(String用的JSON格式,不利于单个字段的修改,hash可将每个字段独立存储,从而能针对单个字段做CRUD) | 实例 |
---|---|
HSET | hset key field value |
HGET | hget key field |
HMSET | hmset key field1 v1 field2 v2… |
HMGET | hmget key field1 field2 … |
HGETALL(一个key所有的field与value) | hgetall key |
HKEYS(一个key所有的field) | hkeys key |
HVALS(一个key所有的value) | hvals key |
HINCRBY | hincrby key field increment |
HSETNX (添加一个key的field值,若field已存在则不执行) | hsetnx key field value |
List命令(有序、可重复、插入删除快) | 实例 |
---|---|
LPUSH 左侧插入一个或多个元素 | lpush key element |
LPOP 移除并返回左侧第一个元素,没有返回nil | lpop key |
RPUSH | rpush key element |
RPOP | rpop key |
LRANGE 返回一段角标范围的所有元素 | lrange key start end |
BLPOP、BRPOP 等待指定时间,而不是直接返回nil(阻塞队列) | lpop key timeout |
Set命令(可看作无value的HashMap)不可重复、查找快、支持交并差集 | 示例 |
---|---|
SADD | sadd key member[member2] |
SREM | srem key member[member2] |
SCARD 返回元素个数 | scard key |
SISMEMBER 判断是否存在于set中 | sismember key memeber |
SMEMBERS 获取set所有的元素 | smembers key |
SINTER 交集 | sinter key1 key2 |
SDIFF 差集 | sdiff key1 key2 |
SUNION 并集 | sunion key1 key2 |
SortedSet命令 可排序、不重复、查询快 | 示例 |
---|---|
ZADD 添加,若已存在则更新score值 | zadd key score member |
ZREM 删除 | zrem key member |
ZSCORE 获取score值 | zscore key member |
ZRANK 获取排名 | zrank key member |
ZCARD 获取元素个数 | zcard key |
ZCOUNT 统计score给定范围内所有元素个数 | zcount key min max |
ZINCRBY 自增 | zincrby key increment member |
ZRANGE score排序后,获取指定排名范围的元素 | zrange key min max |
ZRANDGEBYSCORE score排序后,获取指定score范围的元素 | zrangebyscore key min max |
ZDIFF、ZINTER、ZUNION 差集、交集、并集 |
Commands | Redis
1.引入依赖
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>4.3.0version>
dependency>
<dependency>
<groupId>org.junit.jupitergroupId>
<artifactId>junit-jupiterartifactId>
<version>5.7.0version>
<scope>testscope>
dependency>
2.建立连接
private Jedis jedis;
@BeforeEach
void setUp(){
jedis = new Jedis("127.0.0.1",6379);
//jedis.auth("123");
jedis.select(0);
}
3.测试
4.释放资源
@AfterEach
void tearDown(){
if(jedis != null){
jedis.close();
}
}
解决Jedis线程不安全的问题
public class JedisConnectionFactory {
private static final JedisPool jedisPool;
static{
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(8);//最大连接
poolConfig.setMaxIdle(8);//最大空闲连接
poolConfig.setMinIdle(0);//最小空闲连接
poolConfig.setMaxWaitMillis(200);//最长等待时间
jedisPool = new JedisPool(poolConfig,"127.0.0.1",6379,1000,"123");
}
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
GitHub - redis/jedis.
1.引入依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
dependency>
2.配置文件
spring:
redis:
host: 127.0.0.1
port: 6379
#password: 123
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 100ms
3.注入RedisTemplate + 测试
@SpringBootTest
class RedisDemoApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void testString() {
redisTemplate.opsForValue().set("name","cui");
Object name = redisTemplate.opsForValue().get("name");
System.out.println("name = " + name);
}
}
默认jdk序列化(可读性差)
应该改为string序列化(key)与JSON序列化(value)
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory){
RedisTemplate<String,Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); //JSON序列化工具
template.setKeySerializer(RedisSerializer.string());
template.setHashKeySerializer(RedisSerializer.string());//key序列化
template.setValueSerializer(jsonRedisSerializer);
template.setHashValueSerializer(jsonRedisSerializer);//value序列化
return template;
}
}
注意引入json依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
在Redis记录中不记录@class路径,解决内存占用的问题
用特定操作手动(反)序列化,直接记录为json类型
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testSaveUser() throws JsonProcessingException {
User user = new User("崔",18);
String json = mapper.writeValueAsString(user);//手动序列化
stringRedisTemplate.opsForValue().set("user:200",json);
String jsonUser = stringRedisTemplate.opsForValue().get("user:200");
User user1 = mapper.readValue(jsonUser,User.class);//手动反序列化
System.out.println("user1 = " + user1);
}
参考:
Spring Data Redis