# linux安装redis

#注意,要使用gcc8, centos7.9默认的gcc4.8.5编译会报错
wget http://download.redis.io/releases/redis-6.0.9.tar.gz
tar -zxvf redis-6.0.9.tar.gz
cd redis-6.0.9
make 
make install
nohup redis-server > out.log &

# 无网环境安装

  • centos7.9默认使用 gcc4.8.5
  • 可以基于redis5.x版本进行编译.
wget http://download.redis.io/releases/redis-5.0.6.tar.gz
tar -zxvf redis-5.0.6.tar.gz
cd redis-5.0.6
make install -j4 PREFIX=/u01/redis
# 此外,当前用户需要有 /usr/local/bin的读写权限
make install
nohup redis-server > out.log &
  • 除此外,还可以通过有网环境先安装,然后在迁移的方案;

# redis概述

# redis的由来

REmote DIctionary Server(Redis) 是一个由 Salvatore Sanfilippo 写的 key-value 存储系统,是跨平台的非关系型数据库。

# redis与其它k-v产品的特点

  • 支持数据持久化
  • 支持丰富的数据类型
  • 支持主从备份

# redis支持的数据类型

  • 字符串
  • hash字典
  • 集合
  • 列表
  • 有序集合

# redis数据结构操作命令

# redis字符串操作

  • 增: set key value
  • 查: get key
  • 批量查: mget key1 [key2...]

# redis字典操作

  • 单数据
    • 增: hset key field value
    • 删: hdel key field1 [field2...]
    • 查: hget key field
  • 批量操作
    • 增:hmset key field1 value1 [field2 value2...]
    • 查:hgetall key, hkeys key, hmget key field [field1...]

# redis列表操作

  • 增: lpush key value1 [value2...], rpush key value1 [valu2...]
  • 查: lpop key, rpop key,llen key, lrange key start stop
  • 改: lset key index value

# redis集合操作

  • 增: sadd key member1 [member2...]
  • 查: smembers key, scard key
  • 应用操作:
    • 取并集: sunion key1 key2
    • 取交集: sinter key1 key2

# redis有序集合操作

  • 增: zadd key score1 member [score2 member2...]
  • 查: zcard key, zscore key member, zrank key member

# redis通用操作

# 登录数据库

redis-cli.exe -h host -p port
auth password

# key的通用操作

  • key是否存在: exists key
  • 删除key: del key
  • 获取key的类型: type key
  • 查询key: keys pattern
  • 设置有效期: expire key seconds
  • 获取有效期: ttl key
  • 清空缓存: flushdb

# redis集群

# 搭建

  • 集群模式启动redis-server

    port 7000
    cluster-enabled yes
    cluster-config-file nodes.conf
    cluster-node-timeout 5000
    appendonly yes
    bind 0.0.0.0
    
      ..\redis-server.exe .\redis.conf
    
    redisCluster架构
  • 安装ruby脚本语言环境并验证

ruby -version
  • 安装ruby-redis插件
gem install redis -v 3.2.1
  • 下载redis-trib脚本并执行集群命令
wget https://raw.githubusercontent.com/MSOpenTech/redis/3.0/src/redis-trib.rb

redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005

# 注意事项

  • 整个过程中,不要使用中文路径;
  • 搭建集群时,最好不要使用127.0.0.1作为集群单机ip,在局域网环境下,可能会出现反常现象;

# 客户端连接测试

redis-cli.exe -c -h 127.0.0.1 -p 7000

# redis集群代理

# redis-cluster-proxy

  • 官方推出,与java不兼容。redis6.0开始支持,不建议在生产使用;

# twemproxy(nutcraker)

  • twitter开源的,可代理redis,memcached. 与java不兼容;

# redis高级运维

# 获取连接信息(超高并发导致服务不可用)

#查看活跃连接数客户端地址(虚拟机层面): 
lsof -i:port | awk -F '->' '{print $2}' | awk -F ':' '{print $1}' | awk '{sum[$1]+=1} END {for(k in sum) print k ":" sum[k]}' | sort -n -r -k 2 -t ':'

#查看连接数(redis层面)
infos clients

#查看连接数详细信息
client list  #age 和 idle 表示存活的时间,和已经空闲的时间;


#查看配置过的最大连接数
config get maxclients    #默认情况下是 10000

# redisJava客户端与模板模式

# 架构设计

public interface RedisOperations<K, V> {
 	<T> T execute(RedisCallback<T> action);
    
    Boolean hasKey(K key);
    
    Boolean delete(K key);
    
    void rename(K oldKey, K newKey);
    
    Boolean expire(K key, long timeout, TimeUnit unit);
    
    Boolean persist(K key);
    
    ListOperations<K, V> opsForList();
    
    SetOperations<K, V> opsForSet();
    
    //xxx: 省略其它抽象..., 模板所有的实际方法,都是委派给子类execute方法执行的
    
}
public class RedisAccessor implements InitializingBean {
 	private @Nullable RedisConnectionFactory connectionFactory;
    
    @Nullable
	public RedisConnectionFactory getConnectionFactory() {
		return connectionFactory;
	}
}
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
 	   public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
           try{
               RedisConnectionFactory factory = getRequiredConnectionFactory();
           
           //xxx: 从工厂获取连接
             RedisConnection  conn = RedisConnectionUtils.getConnection(factory);
           }finally{
               //释放连接(物理层面)
           RedisConnectionUtils.releaseConnection(conn, factory, enableTransactionSupport);
           }
           
       }
}

# 执行流程

public abstract class RedisConnectionUtils {
 	   public static RedisConnection getConnection(RedisConnectionFactory factory) {
		return getConnection(factory, false);
	}
    
    public static RedisConnection getConnection(RedisConnectionFactory factory, boolean transactionSupport) {
		return doGetConnection(factory, true, false, transactionSupport);
	}
    
    public static RedisConnection doGetConnection(RedisConnectionFactory factory, boolean allowCreate, boolean bind,
			boolean transactionSupport) {
        //xxx: 从工厂中获取连接,省略其它抽象...
        //xxx: 工厂本身的缓存机制,由各自厂商实现...略
     	RedisConnection conn = factory.getConnection();
    }
}

# 总结

  • redis的关键在于connectionFactory,spring默认采用lettuce实现

  • spring-data-redis默认提供了两个bean,分别为按照名称注入的redisTemplate以及按照类型注入的StringRedisTemplate

# 实现自动重连配置(jedis)

# 重定义工厂

public class AutomannnJedisConnectionFactory extends JedisConnectionFactory {

        public AutomannnJedisConnectionFactory(RedisClusterConfiguration clusterConfig, JedisClientConfiguration clientConfig) {
            super(clusterConfig,clientConfig);
        }

        @Override
        public RedisConnection getConnection() {
            try{
                RedisConnection connection= super.getConnection();
                connection.hashCommands().hGetAll(NO_MEANING_TAG.getBytes());
                return connection;
            }catch (Exception e){
                System.out.println("redis集群异常,尝试进行灾备切换....");
                this.afterPropertiesSet();
                return super.getConnection();
            }
        };

}

# 替换默认redisSession存储

class Configuration{
    @Bean
    public BeanPostProcessor customerSessionRepository() {
        BeanPostProcessor beanPostProcessor = new BeanPostProcessor() {

            public RedisOperationsSessionRepository sessionRepository(ApplicationEventPublisher publisher,
                                                                      RedisConnectionFactory connectionFactory) {

                RedisTemplate<Object, Object> redisTemplate = this.createRedisTemplate(connectionFactory);

                RedisOperationsSessionRepository sessionRepository =
                        new RedisOperationsSessionRepository(redisTemplate);
                sessionRepository.setApplicationEventPublisher(publisher);

                sessionRepository.setDefaultMaxInactiveInterval(1800);

                sessionRepository.setRedisKeyNamespace("spring:session");


                sessionRepository.setRedisFlushMode(RedisFlushMode.ON_SAVE);
                sessionRepository.setDatabase(0);
                return sessionRepository;
            }


            private RedisTemplate<Object, Object> createRedisTemplate(RedisConnectionFactory connectionFactory) {
                RedisTemplate<Object, Object> redisTemplate = new RedisTemplate();
                redisTemplate.setKeySerializer(new StringRedisSerializer());
                redisTemplate.setHashKeySerializer(new StringRedisSerializer());


                redisTemplate.setConnectionFactory(connectionFactory);
                redisTemplate.setBeanClassLoader(getClass().getClassLoader());
                redisTemplate.afterPropertiesSet();
                return redisTemplate;
            }


            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                if (bean instanceof RedisOperationsSessionRepository) {
                    AutomannnJedisConnectionFactory connectionFactory =
                            applicationContext.getBean(AutomannnJedisConnectionFactory.class);
                    bean = sessionRepository(applicationContext, connectionFactory);
                    return bean;
                }
                return null;
            }
        };
        return beanPostProcessor;


    }
    
    @Bean
    public AutomannnJedisConnectionFactory automannnJedisConnectionFactory(SessionConfig sessionConfig) {
        AutomannnJedisConnectionFactory connectionFactory =
                new AutomannnJedisConnectionFactory(sessionConfig.getRedisConnectionFactory().getClusterConfiguration(), sessionConfig.getRedisConnectionFactory().getClientConfiguration());
        return connectionFactory;
    }
}

# 集成配置

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({Configuration.class})
public @interface EnableRedisSessionReConnect {
}

# 连接的实现细节

public class JedisConnectionFactory implements InitializingBean, DisposableBean, RedisConnectionFactory {
    
 	protected Jedis fetchJedisConnector() {

			if (getUsePool() && pool != null) {
				return pool.getResource();
			}

			Jedis jedis = createJedis();
			jedis.connect();
			potentiallySetClientName(jedis);
			return jedis;
	}   
}
  • 默认情况下,如果配置了pool参数,则会使用连接池