Redis的使用(二)
一、redis简单应用
其实在写这个redis专题时我想了很久,我觉得redis没什么好说的,因为现在是个人都会用redis,但是我在写netty专题时发现,netty里面很多东西和概念有很多跟redis的很多应用和底层很相似和可以借鉴的地方,所以后来想想,还是写个专题来简单聊聊。按照我以前的习惯在写应用前我是要写一篇中间件的安装,但redis的安装这次不写了,因为安装过于简单,但是看这专题的朋友记得,我后面所写的所有内容是基于redis6版本的基础上进行写的。如果看过官网的朋友可以知道,redis6和以往版本最大的区别在于他引入了多线程IO,对于6以前的单线程redis来说,性能瓶颈主要在于网络的 IO 消耗, 所以新版本优化主要有两个方向:
- 提高网络 IO 性能,典型的实现像使用 DPDK 来替代内核网络栈的方式
- 使用多线程充分利用多核,典型的实现像 Memcached
官网:https://spring.io/projects/spring-data-redis
具体底层实现我会在后面篇幅会写,这里就不过多说明,下面就将springboot项目集成redis作一个简单的过程演示。
引入pom文件
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
修改application.yml文件
- spring:
- redis:
- # redis数据库索引(默认为0),我们使用索引为3的数据库,避免和其他数据库冲突
- database: 0
- host: 192.168.0.23
- port: 6379
- password: 123456
- # redis连接超时时间(单位毫秒)
- timeout: 0
- # redis连接池配置
- jedis:
- pool:
- # 最大可用连接数(默认为8,负数表示无限)
- max-active: -1
- # 最大空闲连接数(默认为8,负数表示无限)
- max-idle: 2000
- # 最小空闲连接数(默认为0,该值只有为正数才有用)
- min-idle: 1
- # 从连接池中获取连接最大等待时间(默认为-1,单位为毫秒,负数表示无限)
- max-wait: -1
- # 配置空闲连接回收间隔时间,min-idle才会生效,否则不生效
- time-between-eviction-runs: 5000
RedisTemplate
- @SpringBootTest
- class SpringRedisApplicationTests {
- // 注入 RedisTemplate
- @Autowired
- private RedisTemplate redisTemplate;
- // String 类型
- @Test
- void testString () {
- redisTemplate.opsForValue().set("name", "ljx");
- Object name = redisTemplate.opsForValue().get("name");
- System.out.println(name);
- }
- // Hash 类型
- @Test
- public void testHash () {
- redisTemplate.opsForHash().put("user1", "name", "clarence");
- redisTemplate.opsForHash().put("user1", "age", "25");
- Map map = redisTemplate.opsForHash().entries("user1");
- System.out.println(map);
- }
- // List 类型
- @Test
- public void testList () {
- redisTemplate.opsForList().leftPushAll("names", "xiaobai", "xiaohei", "xiaolan");
- List<String> names = redisTemplate.opsForList().range("names", 0, 3);
- System.out.println(names);
- }
- // Set 类型
- @Test
- public void testSet () {
- redisTemplate.opsForSet().add("set", "a", "b", "c");
- Set<String> set = redisTemplate.opsForSet().members("set");
- System.out.println(set);
- }
- // SortedSet 类型
- @Test
- public void testSortedSet () {
- redisTemplate.opsForZSet().add("class", "xiaobai", 90);
- Set aClass = redisTemplate.opsForZSet().rangeByScore("class", 90, 100);
- System.out.println(aClass);
- Set<ZSetOperations.TypedTuple<String>> set = new HashSet<> ();
- set.add(new DefaultTypedTuple<> ("xiaohei", 88.0));
- set.add(new DefaultTypedTuple<>("xiaohui", 94.0));
- set.add(new DefaultTypedTuple<>("xiaolan", 84.0));
- set.add(new DefaultTypedTuple<>("xiaolv", 82.0));
- set.add(new DefaultTypedTuple<>("xiaohong", 99.0));
- redisTemplate.opsForZSet().add("class", set);
- Set aClass1 = redisTemplate.opsForZSet().range("class", 0, 6);
- System.out.println(aClass1);
- }
- }
二、序列化
1、默认是 JdkSerializationRedisSerializer
- RedisTemplate 可以接收任意 Object 作为值写入 Redis,不过在写入前会把 Object 序列化为字节形式,默认是采用 JDK 序列化,上面的 demo 运行后得到的结果如下:
- 缺点:可读性差;内存占用较大
2.1、添加配置文件,使用 String 序列化、Json 序列化
Redis 配置文件
- @Configuration
- public class RedisConfig {
- @Bean
- public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
- // 创建 RedisTemplate 对象
- RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
- // 设置连接工厂
- redisTemplate.setConnectionFactory(connectionFactory);
- // 设置 Key 的序列化 - String 序列化 RedisSerializer.string() => StringRedisSerializer.UTF_8
- redisTemplate.setKeySerializer( RedisSerializer.string());
- redisTemplate.setHashKeySerializer(RedisSerializer.string());
- // 设置 Value 的序列化 - JSON 序列化 RedisSerializer.json() => GenericJackson2JsonRedisSerializer
- redisTemplate.setValueSerializer(RedisSerializer.json());
- redisTemplate.setHashValueSerializer(RedisSerializer.json());
- // 返回
- return redisTemplate;
- }
- }
引入 Jackson 依赖
- <!--Jackson依赖-->
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- <version>2.11.4</version>
- </dependency>
添加 User 实体类
- @Data
- @NoArgsConstructor
- @AllArgsConstructor
- public class User {
- private String name;
- private Integer age;
- }
案例
- @SpringBootTest
- public class RedisDemoApplicationTest2 {
- // 注入 RedisTemplate
- @Autowired
- private RedisTemplate<String, Object> redisTemplate;
- @Test
- void testString() {
- redisTemplate.opsForValue().set("name", "小白");
- Object name = redisTemplate.opsForValue().get("name");
- System.out.println(name);
- }
- @Test
- void testSaveUser() {
- redisTemplate.opsForValue().set("user", new User("小白", 23));
- User user = (User) redisTemplate.opsForValue().get("user");
- System.out.println(user);
- }
- }
运行结果

StringRedisTemplate
- 从上述 Demo 的运行结果可以看到,为了在反序列化时知道对象的类型,Json 序列化会将类的 class 类型写入 json 结果中存入 Redis,会带来额外的内存开销
- 为了节省内存空间,我们并不会使用 json 序列化器来处理 value,而是统一使用 String 序列化器,要求只能存储 String 类型的 key 和 value。当需要存储 Java 对象时,手动完成对象的序列化和反序列化
- spring 提供了一个 StringRedisTemplate 类,其 key 和 value 的序列化方式默认为 String 方式
引入 fastjson 依赖
- <!--fastjson依赖-->
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>fastjson</artifactId>
- <version>1.2.62</version>
- </dependency>
案例
- @SpringBootTest
- public class RedisDemoApplicationTest2 {
- // 注入 RedisTemplate
- @Autowired
- private RedisTemplate<String, Object> redisTemplate;
- @Test
- void testString() {
- redisTemplate.opsForValue().set("name", "小白");
- Object name = redisTemplate.opsForValue().get("name");
- System.out.println(name);
- }
- @Test
- void testSaveUser() {
- redisTemplate.opsForValue().set("user", new User("小白", 23));
- User user = (User) redisTemplate.opsForValue().get("user");
- System.out.println(user);
- }
- }
三、redis的请求通信协议
上面简单的演示了下redis的操作,接下来首先详细了解一下Redis Serialization Protocol(Redis序列化协议),这个是Redis提供的一种,客户端和Redis服务端通信传输的编码协议,服务端收到后,会基于这个约定编码进行解码。首先打开Wireshark工具,对VMnet8这个网络进行抓包(没有这工具可以自己下个),先在连接工具加一个假数据
打开Wireshark工具,对VMnet8这个网络进行抓包
增加过滤条件
ip.dst_host==ip and tcp.port in {6379}
使用RDM工具连接到Redis Server进行key-value操作,比如执行 set name ljx通过Wireshark工具监控数据包内容,可以通过上图看到实际发出的数据包是:*3\r\n$3\r\nSET\r\n$4\r\nname\r\n$3\r\nljx
其中开头的*3这个数字中代表参数个数,我是set name ljx,所以表示三个参数;接着就是$3表示属性长度,$表示包含了3个字符。客户端和服务器发送的命令或数据一律以 \r\n (CRLF回车+换行)结尾。了解了这格式的意思接下来自己实现一个java客户端就非常容易了。
3.1、客户端实现
在手写之前先看下Jedis源码是怎么实现的手写客户端代码,在Jedis中就有的,先看一下Jedis内部的实现源码:
- protected Connection sendCommand(Protocol.Command cmd, byte[]... args) {
- try {
- this.connect();//建立Socket连接
- Protocol.sendCommand(this.outputStream, cmd, args);//封装报文并将报文写入流中
- ++this.pipelinedCommands;
- return this;
- } catch (JedisConnectionException var6) {
- JedisConnectionException ex = var6;
- try {
- String errorMessage = Protocol.readErrorLineIfPossible(this.inputStream);
- if (errorMessage != null && errorMessage.length() > 0) {
- ex = new JedisConnectionException(errorMessage, ex.getCause());
- }
- } catch (Exception var5) {
- }
- this.broken = true;
- throw ex;
- }
- }
这段源码并不难找,使用Jedis的set方法,然后一直跟进去就可以。最终方法的位置是redis.clients.jedis.Connection.sebdCommand()。
从这个方法的内部实现就可以看出来其实就是通过Socket建立tcp连接,然后将命令和数据转换成RESP协议规范的报文格式,最后通过Socket将数据传入过去。知道这些对于自己写一个Jedis客户端是不是就有思路啦。基于对源码的借鉴,简易的Jedis实现如下:
- public class CustomJedis {
- public static void main(String[] args) throws IOException {
- //建立socket连接
- Socket socket = new Socket();
- InetSocketAddress socketAddress = new InetSocketAddress("106.12.75.86", 6379);
- socket.connect(socketAddress, 10000);
- //获取scoket输出流,将报文转换成byte[]传入流中
- OutputStream outputStream = socket.getOutputStream();
- outputStream.write(command());
- //获取返回的输出流,并打印输出数据
- InputStream inputStream = socket.getInputStream();
- byte[] buffer = new byte[1024];
- inputStream.read(buffer);
- System.out.println("返回执行结果:" + new String(buffer));
- }
- //组装报文信息
- private static byte[] command() {
- return "*3\r\n$3\r\nSET\r\n$9\r\nuser:name\r\n$6\r\nitcrud\r\n".getBytes();
- }
- }
但是这里需要注意,上面的实现方式是直接建立socket连接,Redis很多时候是设置密码认证的,如果这样的话上面的代码就需要改动啦。
改动后如下:
- public class CustomJedisProd {
- public static void main(String[] args) throws IOException {
- Socket socket = new Socket();
- InetSocketAddress socketAddress = new InetSocketAddress("106.12.75.86", 6379);
- socket.connect(socketAddress, 10000);
- OutputStream outputStream = socket.getOutputStream();
- //验证密码
- outputStream.write(auth());
- InputStream inputStream = socket.getInputStream();
- byte[] buffer = new byte[1024];
- inputStream.read(buffer);
- System.out.println("返回执行结果:" + new String(buffer));
- //发送数据
- outputStream.write(command());
- inputStream.read(buffer);
- System.out.println("返回执行结果:" + new String(buffer));
- inputStream.close();
- outputStream.close();
- }
- //验证
- private static byte[] auth(){
- return "*2\r\n$4\r\nAUTH\r\n$6\r\n123456\r\n".getBytes();
- }
- //组装报文信息
- private static byte[] command() {
- return "*3\r\n$3\r\nSET\r\n$9\r\nuser:name\r\n$6\r\nitcrud\r\n".getBytes();
- }
- }
运行结果
这样一个最简单版本就实现了,但是这里面的编码是写死的,每次报问要自己组装太麻烦,下面来进一步优化下:
定义常量池
- public class CommandConstant {
- public static final String START="*";
- public static final String LENGTH="$";
- public static final String LINE="\r\n";
- public enum CommandEnum{
- SET,
- GET
- }
- }
CustomClientSocket用来建立网络通信连接,并且发送数据指定到RedisServer。
- public class CustomerRedisClientSocket {
- private Socket socket;
- private InputStream inputStream;
- private OutputStream outputStream;
- public CustomerRedisClientSocket(String ip,int port,String password){
- try {
- socket=new Socket(ip,port);
- inputStream=socket.getInputStream();
- outputStream=socket.getOutputStream();
- outputStream.write ( password.getBytes ());
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- public void send(String cmd){
- try {
- outputStream.write(cmd.getBytes());
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- public String read(){
- byte[] bytes=new byte[1024];
- int count=0;
- try {
- count=inputStream.read(bytes);
- } catch (IOException e) {
- e.printStackTrace();
- }
- return new String(bytes,0,count);
- }
- }
封装客户端
- public class CustomerRedisClient {
- private CustomerRedisClientSocket customerRedisClientSocket;
- public CustomerRedisClient(String host,int port,String password) {
- customerRedisClientSocket=new CustomerRedisClientSocket(host,port,password ("AUTH",password));
- }
- public String password(String key,String value){
- convertToCommand(null,key.getBytes(),value.getBytes());
- return convertToCommand(null,key.getBytes(),value.getBytes());
- }
- public String set(String key,String value){
- customerRedisClientSocket.send(convertToCommand(CommandConstant.CommandEnum.SET,key.getBytes(),value.getBytes()));
- return customerRedisClientSocket.read(); //在等待返回结果的时候,是阻塞的
- }
- public String get(String key){
- customerRedisClientSocket.send(convertToCommand(CommandConstant.CommandEnum.GET,key.getBytes()));
- return customerRedisClientSocket.read();
- }
- public static String convertToCommand(CommandConstant.CommandEnum commandEnum,byte[]... bytes){
- StringBuilder stringBuilder=new StringBuilder();
- if (commandEnum==null){
- stringBuilder.append(CommandConstant.START).append(bytes.length).append(CommandConstant.LINE);
- }else{
- stringBuilder.append(CommandConstant.START).append(bytes.length+1).append(CommandConstant.LINE);
- stringBuilder.append(CommandConstant.LENGTH).append(commandEnum.toString().length()).append(CommandConstant.LINE);
- stringBuilder.append(commandEnum.toString()).append(CommandConstant.LINE);
- }
- for (byte[] by:bytes){
- stringBuilder.append(CommandConstant.LENGTH).append(by.length).append(CommandConstant.LINE);
- stringBuilder.append(new String(by)).append(CommandConstant.LINE);
- }
- return stringBuilder.toString();
- }
- }
测试方法
- public class MainClient {
- public static void main(String[] args) {
- CustomerRedisClient customerRedisClient=new CustomerRedisClient("124.71.33.75",6379,"ghy20200707redis");
- System.out.println(customerRedisClient.set("name","ljx"));
- System.out.println(customerRedisClient.get("ljx"));
- }
- }
结果
所有事物理解了本质后,实现起来其实一点都不难,通过上面两次优化,就实现了一个自己版本的客户端,但是实际开发过程中,客户端我们不用自己开发,官方推荐了以下三种客户端
四、Reids的java客户端
4.1、客户端对比

Jedis api 在线网址:http://tool.oschina.net/uploads/apidocs/redis/clients/jedis/Jedis.html
redisson 官网地址:https://redisson.org/
redisson git项目地址:https://github.com/redisson/redisson
lettuce 官网地址:https://lettuce.io/
lettuce git项目地址:https://github.com/lettuce-io/lettuce-core
首先,在spring boot2之后,对redis连接的支持,默认就采用了lettuce。这就一定程度说明了lettuce 和Jedis的优劣。
4.2、各种客户端对比
4.2.1、概念
- Jedis:是老牌的Redis的Java实现客户端,提供了比较全面的Redis命令的支持,
- Redisson:实现了分布式和可扩展的Java数据结构。
- Lettuce:高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。
4.2.2、优点
- Jedis:比较全面的提供了Redis的操作特性
- Redisson:促使使用者对Redis的关注分离,提供很多分布式相关操作服务,例如,分布式锁,分布式集合,可通过Redis支持延迟队列
- Lettuce:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Lettuce的API是线程安全的,所以可以操作单个Lettuce连接来完成各种操作
4.2.3、可伸缩
- Jedis:使用阻塞的I/O,且其方法调用都是同步的,程序流需要等到sockets处理完I/O才能执行,不支持异步。Jedis客户端实例不是线程安全的,所以需要通过连接池来使用Jedis。
- Redisson:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Redisson的API是线程安全的,所以可以操作单个Redisson连接来完成各种操作
- Lettuce:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Lettuce的API是线程安全的,所以可以操作单个Lettuce连接来完成各种操作
- lettuce能够支持redis4,需要java8及以上。
- lettuce是基于netty实现的与redis进行同步和异步的通信。
4.2.4、lettuce和jedis比较
- jedis使直接连接redis server,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个jedis实例增加物理连接 ;
- lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问,StatefulRedisConnection是线程安全的,所以一个连接实例可以满足多线程环境下的并发访问,当然这也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。
- Redisson实现了分布式和可扩展的Java数据结构,和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。Redisson的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。
- <dependency>
- <groupId>redis.clients</groupId>
- <artifactId>jedis</artifactId>
- <version>3.7.0</version>
- </dependency>
- <dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter</artifactId>
- <version>5.7.0</version>
- <scope>test</scope>
- </dependency>
- public class JedisTest {
- private Jedis jedis;
- @BeforeEach
- void setUp(){
- // 1、建立连接
- jedis = new Jedis("ip", 6379);
- // 2、设置密码
- jedis.auth("123456");
- // 3、选择库
- jedis.select(0);
- }
- @Test
- public void testString(){
- // 存入数据
- String result = jedis.set("name", "张三");
- System.out.println("result = " + result);
- // 获取数据
- String name = jedis.get("name");
- System.out.println(name);
- }
- @Test
- public void testHash(){
- // 插入 hash 数据
- jedis.hset("user:1", "name", "lisi");
- jedis.hset("user:1", "age", "21");
- // 获取
- Map<String, String> map = jedis.hgetAll("user:1");
- System.out.println(map);
- }
- @AfterEach
- void closeJedis(){
- if(jedis != null){
- jedis.close();
- }
- }
- }
4.2.6、集成Lettuce
引入pom
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-pool2</artifactId>
- </dependency>
配置yml
- #Redis配置
- spring:
- redis:
- database: 6 #Redis索引0~15,默认为0
- host: 127.0.0.1
- port: 6379
- password: #密码(默认为空)
- lettuce: # 这里标明使用lettuce配置
- pool:
- max-active: 8 #连接池最大连接数(使用负值表示没有限制)
- max-wait: -1ms #连接池最大阻塞等待时间(使用负值表示没有限制)
- max-idle: 5 #连接池中的最大空闲连接
- min-idle: 0 #连接池中的最小空闲连接
- timeout: 10000ms #连接超时时间(毫秒)
添加Redisson的配置参数读取类RedisConfig
- @Configuration
- @EnableCaching
- public class RedisConfig extends CachingConfigurerSupport {
- /**
- * RedisTemplate配置
- * @param connectionFactory
- * @return
- */
- @Bean
- public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
- // 配置redisTemplate
- RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
- redisTemplate.setConnectionFactory(connectionFactory);
- redisTemplate.setKeySerializer(new StringRedisSerializer ());//key序列化
- redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer ());//value序列化
- redisTemplate.afterPropertiesSet();
- return redisTemplate;
- }
- }
编写测试类RedisTest
- @SpringBootTest
- class SpringRedisApplicationTests {
- // 注入 RedisTemplate
- @Autowired
- private RedisTemplate redisTemplate;
- // String 类型
- @Test
- void testString () {
- redisTemplate.opsForValue().set("name", "ljx");
- Object name = redisTemplate.opsForValue().get("name");
- System.out.println(name);
- }
- // Hash 类型
- @Test
- public void testHash () {
- redisTemplate.opsForHash().put("user1", "name", "clarence");
- redisTemplate.opsForHash().put("user1", "age", "25");
- Map map = redisTemplate.opsForHash().entries("user1");
- System.out.println(map);
- }
- // List 类型
- @Test
- public void testList () {
- redisTemplate.opsForList().leftPushAll("names", "xiaobai", "xiaohei", "xiaolan");
- List<String> names = redisTemplate.opsForList().range("names", 0, 3);
- System.out.println(names);
- }
- // Set 类型
- @Test
- public void testSet () {
- redisTemplate.opsForSet().add("set", "a", "b", "c");
- Set<String> set = redisTemplate.opsForSet().members("set");
- System.out.println(set);
- }
- // SortedSet 类型
- @Test
- public void testSortedSet () {
- redisTemplate.opsForZSet().add("class", "xiaobai", 90);
- Set aClass = redisTemplate.opsForZSet().rangeByScore("class", 90, 100);
- System.out.println(aClass);
- Set<ZSetOperations.TypedTuple<String>> set = new HashSet<> ();
- set.add(new DefaultTypedTuple<> ("xiaohei", 88.0));
- set.add(new DefaultTypedTuple<>("xiaohui", 94.0));
- set.add(new DefaultTypedTuple<>("xiaolan", 84.0));
- set.add(new DefaultTypedTuple<>("xiaolv", 82.0));
- set.add(new DefaultTypedTuple<>("xiaohong", 99.0));
- redisTemplate.opsForZSet().add("class", set);
- Set aClass1 = redisTemplate.opsForZSet().range("class", 0, 6);
- System.out.println(aClass1);
- }
- }
4.2.7、集成Redisson
引入pom
- <dependency>
- <groupId>org.redisson</groupId>
- <artifactId>redisson-spring-boot-starter</artifactId>
- <version>3.16.0</version>
- </dependency>
yml文件我就不写了,和上面一样,下面简单写下测试类
- @Configuration
- public class RedissonConfig {
- @Autowired
- private RedisProperties redisProperties;
- @Bean
- public RedissonClient redissonClient() {
- Config config = new Config();
- String redisUrl = String.format("redis://%s:%s", redisProperties.getHost() + "", redisProperties.getPort() + "");
- config.useSingleServer().setAddress(redisUrl).setPassword(redisProperties.getPassword());
- config.useSingleServer().setDatabase(3);
- return Redisson.create(config);
- }
- }
- @RestController
- @RequestMapping("/redisson")
- public class RedissonController {
- @Autowired
- private StringRedisTemplate stringRedisTemplate;
- @GetMapping("/save")
- public String save(){
- stringRedisTemplate.opsForValue().set("key","redisson");
- return "save ok";
- }
- @GetMapping("/get")
- public String get(){
- return stringRedisTemplate.opsForValue().get("key");
- }
- }
Redis的使用(二)的更多相关文章
- 从零开始搭建框架SSM+Redis+Mysql(二)之MAVEN项目搭建
从零开始搭建框架SSM+Redis+Mysql(二)之MAVEN项目搭建 废话不说,直接撸步骤!!! 1.创建主项目:ncc-parent 选择maven创建项目,注意在创建项目中,packing选择 ...
- redis一主二从加哨兵
redis版本:redis-3.0.6.tar.gz master:192.168.3.180 slave:192.168.3.184 (机器原因,两从都在这上面) 一.redis安装 cd /roo ...
- Redis学习笔记二 (BitMap算法分析与BitCount语法)
Redis学习笔记二 一.BitMap是什么 就是通过一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身.我们知道8个bit可以组成一个Byte,所以bitmap本身会极大的节省 ...
- redis学习 (key)键,Python操作redis 键 (二)
# -*- coding: utf-8 -*- import redis #这个redis 连接不能用,请根据自己的需要修改 r =redis.Redis(host=") 1. delete ...
- Redis 集群二
[Redis 集群二] 集群的客户端 Redis 集群现阶段的一个问题是客户端实现很少. 以下是一些我知道的实现: redis-rb-cluster 是我(@antirez)编写的 Ruby 实现, ...
- docker-compose一键部署redis一主二从三哨兵模式(含密码,数据持久化)
本篇基于centos7服务器进行部署开发 一.拉取redis镜像,使用如下命令 docker pull redis 1.查看镜像是否拉取成功,使用如下命令 docker images 显示如下则证明拉 ...
- springboot 集成Redis一主二从三哨兵
1.Centos7 Redis一主二从三哨兵配置 Redis一主二从三哨兵环境搭建 2.接入过程 与集成redis单机不同的是jedis相关的配置做了修改,JedisPool换成了JedisSenti ...
- redis 学习(二)-- 通用命令
redis 学习(二)-- 通用命令 1. keys pattern 含义:查找所有符合给定模式(pattern)的key 命令 含义 keys * 遍历所有 key keys he[h-l]* 遍历 ...
- redis命令学习(二) · THIS SPACE
列表(Lists)操作命令 Redis列表是简单的字符串列表,按照插入顺序排序. 你可以添加一个元素导列表的头部(左边)或者尾部(右边)LPUSH命令插入一个新的元素导头部,而RPUSH插入一个新元素 ...
- redis学习教程二《四大数据类型》
redis学习教程二<四大数据类型> 四大数据类型包括:字符串 哈希 列表 集合一 : Redis字符串 Redis字符串命令用于管理Redis中的字符串 ...
随机推荐
- scrapy爬虫简单案例(简单易懂 适合新手)
爬取所有的电影名字,类型,时间等信息 1.准备工作 爬取的网页 https://www.ddoutv.com/f/27-1.html 创建项目 win + R 打开cmd输入 scrapy start ...
- matplotlib---legend图例
import numpy as np import matplotlib.pyplot as plt x = np.linspace(-3, 3, 50) y1 = 2 * x + 1 y2 = x ...
- python---希尔排序的实现
def shell_sort(alist): """希尔排序""" n = len(alist) gap = n // 2 # 插入算法执行 ...
- socket套接字模块及黏包现象
一.socket套接字模块 socket概念 socket层 理解socket Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模 ...
- js模块系统 - amd|cmd|commonjs|esm|umd
写过前端代码大概率听说过amd cmd umd commonjs esm这些名词, 想当初我第一次看到这些的时候, 人都麻了, 都是些啥啊. 后来我知道了, 这些都是js的模块规范. amd - 浏览 ...
- mount挂载命令
了解命令mount,这是一个挂载命令,挂载的意思是指的就是将设备文件中的顶级目录连接到 Linux 根目录下的某一目录(最好是空目录),访问此目录就等同于访问设备文件.当在 Linux 系统中使用硬件 ...
- 论文阅读 Continuous-Time Dynamic Network Embeddings
1 Continuous-Time Dynamic Network Embeddings Abstract 描述一种将时间信息纳入网络嵌入的通用框架,该框架提出了从CTDG中学习时间相关嵌入 Co ...
- Educational Codeforces Round 121 (Rated for Div. 2)——A - Equidistant Letters
A - Equidistant Letters 题源:https://codeforces.com/contest/1626/problem/A 今天上午VP了这场CF,很遗憾的是一道题也没写出来,原 ...
- 简单手写一个jqurey
1 /** 2 * @description 手写jquery 3 * @author ddxldxl 4 */ 5 class Jquery { 6 constructor(selector) { ...
- 创建第一个c程序
创建,组织,生成 ,生成. 1.我们先创建一个win32项目. 文件->新建->项目->Visual C++ ->Win32 输入项目名称 选择项目保存位置 很重要的一 ...