当前位置: 首页 > news >正文

Redis基础知识

Redis基础知识

一、Redis简介

1.1 什么是Redis?

Redis是一个开源的、基于内存的数据结构存储系统,可以用作:

  • 数据库
  • 缓存
  • 消息中间件
  • 分布式锁

1.2 Redis特点

  • 高性能:基于内存操作
  • 支持多种数据结构
  • 支持数据持久化
  • 支持主从复制
  • 支持事务
  • 支持Lua脚本

1.3 应用场景

  • 缓存
  • 计数器
  • 排行榜
  • 消息队列
  • 分布式锁
  • 会话管理

二、Redis安装与配置

2.1 安装Redis

# Windows安装
# 1. 下载Redis for Windows
# 2. 运行安装程序
# 3. 配置环境变量

# Linux安装
sudo apt-get update
sudo apt-get install redis-server

# Mac安装
brew install redis

2.2 启动Redis

# Windows启动
redis-server

# Linux启动
sudo systemctl start redis-server

# Mac启动
brew services start redis

2.3 连接Redis

# 命令行连接
redis-cli

# 指定主机和端口连接
redis-cli -h localhost -p 6379

# 使用密码连接
redis-cli -a password

三、Redis数据类型

3.1 字符串(String)

# 设置值
SET key value

# 获取值
GET key

# 设置过期时间
SETEX key seconds value

# 批量设置
MSET key1 value1 key2 value2

# 批量获取
MGET key1 key2

# 递增
INCR key

# 递减
DECR key

3.2 哈希(Hash)

# 设置字段值
HSET key field value

# 获取字段值
HGET key field

# 获取所有字段
HGETALL key

# 删除字段
HDEL key field

# 判断字段是否存在
HEXISTS key field

3.3 列表(List)

# 左侧插入
LPUSH key value

# 右侧插入
RPUSH key value

# 左侧弹出
LPOP key

# 右侧弹出
RPOP key

# 获取列表范围
LRANGE key start stop

# 获取列表长度
LLEN key

3.4 集合(Set)

# 添加元素
SADD key member

# 删除元素
SREM key member

# 获取所有元素
SMEMBERS key

# 判断元素是否存在
SISMEMBER key member

# 获取集合大小
SCARD key

3.5 有序集合(Sorted Set)

# 添加元素
ZADD key score member

# 删除元素
ZREM key member

# 获取分数范围内的元素
ZRANGEBYSCORE key min max

# 获取排名
ZRANK key member

# 获取分数
ZSCORE key member

四、Redis高级特性

4.1 事务

# 开始事务
MULTI

# 执行命令
SET key1 value1
SET key2 value2

# 提交事务
EXEC

# 取消事务
DISCARD

4.2 发布订阅

# 订阅频道
SUBSCRIBE channel

# 发布消息
PUBLISH channel message

# 取消订阅
UNSUBSCRIBE channel

4.3 持久化

# RDB持久化配置
save 900 1
save 300 10
save 60 10000

# AOF持久化配置
appendonly yes
appendfsync everysec

五、Redis集群

5.1 主从复制

# 配置从节点
SLAVEOF master_ip master_port

# 查看复制状态
INFO replication

5.2 哨兵模式

# 启动哨兵
redis-sentinel sentinel.conf

# 哨兵配置
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000

5.3 集群模式

# 创建集群
redis-cli --cluster create node1:6379 node2:6379 node3:6379

# 添加节点
redis-cli --cluster add-node new_node:6379 existing_node:6379

六、Redis性能优化

6.1 内存优化

  • 使用适当的数据类型
  • 设置过期时间
  • 使用压缩
  • 定期清理

6.2 配置优化

# redis.conf
maxmemory 2gb
maxmemory-policy allkeys-lru
appendonly yes
appendfsync everysec

6.3 监控

# 查看内存使用
INFO memory

# 查看客户端连接
INFO clients

# 查看命令统计
INFO commandstats

七、Redis安全

7.1 访问控制

# 设置密码
CONFIG SET requirepass password

# 修改密码
AUTH old_password new_password

7.2 网络安全

# redis.conf
bind 127.0.0.1
protected-mode yes

八、Redis最佳实践

8.1 开发规范

  1. 键名设计

    • 使用有意义的名称
    • 使用冒号分隔
    • 控制键名长度
  2. 数据设计

    • 合理使用数据类型
    • 避免大key
    • 设置过期时间
  3. 性能优化

    • 使用pipeline
    • 使用连接池
    • 避免频繁操作

8.2 运维规范

  1. 监控

    • 监控内存使用
    • 监控连接数
    • 监控命令执行
  2. 备份

    • 定期备份数据
    • 测试恢复流程
    • 保存备份记录
  3. 安全

    • 启用密码认证
    • 限制网络访问
    • 定期更新密码

九、Redis与Java集成

9.1 Jedis客户端

// 添加Jedis依赖
// Maven
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>4.3.1</version>
</dependency>

// Gradle
implementation 'redis.clients:jedis:4.3.1'

9.2 基本操作示例

// 创建Jedis客户端
Jedis jedis = new Jedis("localhost", 6379);

// 设置密码(如果有)
jedis.auth("password");

// 字符串操作
jedis.set("key", "value");
String value = jedis.get("key");

// 哈希操作
jedis.hset("user:1", "name", "张三");
jedis.hset("user:1", "age", "25");
Map<String, String> user = jedis.hgetAll("user:1");

// 列表操作
jedis.lpush("messages", "消息1");
jedis.lpush("messages", "消息2");
List<String> messages = jedis.lrange("messages", 0, -1);

// 集合操作
jedis.sadd("tags", "Java", "Redis", "Spring");
Set<String> tags = jedis.smembers("tags");

// 有序集合操作
jedis.zadd("scores", 89.5, "张三");
jedis.zadd("scores", 92.0, "李四");
Set<String> topScores = jedis.zrevrange("scores", 0, 1);

// 关闭连接
jedis.close();

9.3 使用连接池

// 创建连接池配置
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(8);
poolConfig.setMaxIdle(8);
poolConfig.setMinIdle(0);
poolConfig.setTestOnBorrow(true);
poolConfig.setTestOnReturn(true);
poolConfig.setTestWhileIdle(true);
poolConfig.setMinEvictableIdleTimeMillis(60000);
poolConfig.setTimeBetweenEvictionRunsMillis(30000);
poolConfig.setNumTestsPerEvictionRun(3);
poolConfig.setBlockWhenExhausted(true);

// 创建连接池
JedisPool jedisPool = new JedisPool(poolConfig, "localhost", 6379, 2000, "password");

// 从连接池获取连接
try (Jedis jedis = jedisPool.getResource()) {
    // 使用Jedis进行操作
    jedis.set("key", "value");
    String value = jedis.get("key");
}

// 关闭连接池
jedisPool.close();

9.4 使用Pipeline批量操作

Jedis jedis = new Jedis("localhost", 6379);
Pipeline pipeline = jedis.pipelined();

// 批量设置
for (int i = 0; i < 1000; i++) {
    pipeline.set("key:" + i, "value:" + i);
}

// 执行Pipeline
pipeline.sync();

// 关闭连接
jedis.close();

十、Redis与Spring Boot集成

10.1 添加Spring Data Redis依赖

<!-- Maven -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- Gradle -->
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

10.2 配置Redis连接

# application.yml
spring:
  redis:
    host: localhost
    port: 6379
    password: password
    database: 0
    timeout: 10000
    lettuce:
      pool:
        max-active: 8
        max-wait: -1
        max-idle: 8
        min-idle: 0

10.3 创建Redis配置类

@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        
        // 使用Jackson2JsonRedisSerializer序列化值
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, 
                ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        serializer.setObjectMapper(mapper);
        
        // 设置key和value的序列化规则
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        
        return template;
    }
}

10.4 使用RedisTemplate

@Service
public class UserService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // 存储用户信息
    public void saveUser(String id, User user) {
        redisTemplate.opsForValue().set("user:" + id, user);
    }
    
    // 获取用户信息
    public User getUser(String id) {
        return (User) redisTemplate.opsForValue().get("user:" + id);
    }
    
    // 删除用户信息
    public void deleteUser(String id) {
        redisTemplate.delete("user:" + id);
    }
    
    // 设置过期时间
    public void setExpire(String key, long timeout, TimeUnit unit) {
        redisTemplate.expire(key, timeout, unit);
    }
}

10.5 使用RedisRepository

@RedisHash("users")
public class User {
    @Id
    private String id;
    private String name;
    private int age;
    
    // 构造函数、getter和setter方法
}

@Repository
public interface UserRepository extends CrudRepository<User, String> {
    List<User> findByAge(int age);
    List<User> findByNameStartingWith(String prefix);
}

十一、Redis与Docker部署

11.1 使用Docker运行Redis

# 拉取Redis镜像
docker pull redis:latest

# 运行Redis容器
docker run -d --name redis -p 6379:6379 redis:latest

# 运行Redis容器(带数据持久化)
docker run -d --name redis -p 6379:6379 -v /data/redis:/data redis:latest

# 运行Redis容器(带密码)
docker run -d --name redis -p 6379:6379 redis:latest redis-server --requirepass password

11.2 使用Docker Compose部署Redis

# docker-compose.yml
version: '3'
services:
  redis:
    image: redis:latest
    container_name: redis
    ports:
      - "6379:6379"
    volumes:
      - ./data:/data
    command: redis-server --requirepass password
    networks:
      - redis-network

  redis-commander:
    image: rediscommander/redis-commander:latest
    container_name: redis-commander
    ports:
      - "8081:8081"
    environment:
      - REDIS_HOSTS=local:redis:6379:0:password
    depends_on:
      - redis
    networks:
      - redis-network

networks:
  redis-network:
    driver: bridge

11.3 使用Docker部署Redis集群

# docker-compose-cluster.yml
version: '3'
services:
  redis1:
    image: redis:latest
    container_name: redis1
    command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes
    ports:
      - "6371:6379"
    volumes:
      - ./data1:/data
    networks:
      - redis-cluster-network

  redis2:
    image: redis:latest
    container_name: redis2
    command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes
    ports:
      - "6372:6379"
    volumes:
      - ./data2:/data
    networks:
      - redis-cluster-network

  redis3:
    image: redis:latest
    container_name: redis3
    command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes
    ports:
      - "6373:6379"
    volumes:
      - ./data3:/data
    networks:
      - redis-cluster-network

  redis4:
    image: redis:latest
    container_name: redis4
    command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes
    ports:
      - "6374:6379"
    volumes:
      - ./data4:/data
    networks:
      - redis-cluster-network

  redis5:
    image: redis:latest
    container_name: redis5
    command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes
    ports:
      - "6375:6379"
    volumes:
      - ./data5:/data
    networks:
      - redis-cluster-network

  redis6:
    image: redis:latest
    container_name: redis6
    command: redis-server --port 6379 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes
    ports:
      - "6376:6379"
    volumes:
      - ./data6:/data
    networks:
      - redis-cluster-network

networks:
  redis-cluster-network:
    driver: bridge
# 创建Redis集群
docker exec -it redis1 redis-cli --cluster create \
  redis1:6379 redis2:6379 redis3:6379 redis4:6379 redis5:6379 redis6:6379 \
  --cluster-replicas 1

十二、Redis常见问题解答

12.1 内存问题

问题: Redis内存占用过高
解决方案:

  1. 设置最大内存限制:CONFIG SET maxmemory 2gb
  2. 配置内存淘汰策略:CONFIG SET maxmemory-policy allkeys-lru
  3. 使用适当的数据类型,避免大key
  4. 设置过期时间,让不常用的数据自动过期
  5. 使用压缩算法减少内存占用

12.2 性能问题

问题: Redis响应慢
解决方案:

  1. 检查是否有慢查询:SLOWLOG GET 10
  2. 优化数据结构,避免使用大key
  3. 使用Pipeline批量操作
  4. 增加Redis实例,使用集群分散负载
  5. 检查网络延迟和带宽

12.3 连接问题

问题: 客户端连接Redis失败
解决方案:

  1. 检查Redis服务是否正在运行
  2. 检查防火墙设置,确保端口6379开放
  3. 检查Redis配置文件中的bind设置
  4. 如果使用密码,确保密码正确
  5. 检查客户端连接池配置

12.4 数据一致性问题

问题: 主从复制数据不一致
解决方案:

  1. 检查主从复制状态:INFO replication
  2. 检查网络连接是否稳定
  3. 如果从节点落后太多,考虑重新同步
  4. 使用哨兵模式自动处理故障转移
  5. 考虑使用Redis集群提高可用性

12.5 持久化问题

问题: Redis重启后数据丢失
解决方案:

  1. 启用RDB持久化:CONFIG SET save "900 1 300 10 60 10000"
  2. 启用AOF持久化:CONFIG SET appendonly yes
  3. 配置AOF同步策略:CONFIG SET appendfsync everysec
  4. 定期备份数据文件
  5. 测试恢复流程确保数据可以正确恢复

十三、Redis高级应用场景

13.1 分布式锁

// 使用Redis实现分布式锁
public class RedisLock {
    private RedisTemplate<String, String> redisTemplate;
    private String lockKey;
    
    public RedisLock(RedisTemplate<String, String> redisTemplate, String lockKey) {
        this.redisTemplate = redisTemplate;
        this.lockKey = lockKey;
    }
    
    public boolean tryLock(String requestId, long expireTime) {
        return Boolean.TRUE.equals(redisTemplate.opsForValue()
            .setIfAbsent(lockKey, requestId, expireTime, TimeUnit.MILLISECONDS));
    }
    
    public boolean releaseLock(String requestId) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                        "return redis.call('del', KEYS[1]) else return 0 end";
        return Boolean.TRUE.equals(redisTemplate.execute(
            new DefaultRedisScript<>(script, Boolean.class), 
            Collections.singletonList(lockKey), 
            requestId));
    }
}

// 使用示例
String requestId = UUID.randomUUID().toString();
RedisLock lock = new RedisLock(redisTemplate, "lock:resource");
try {
    if (lock.tryLock(requestId, 30000)) {
        // 获取锁成功,执行业务逻辑
        // ...
    } else {
        // 获取锁失败
    }
} finally {
    // 释放锁
    lock.releaseLock(requestId);
}

13.2 限流器

// 使用Redis实现限流器
public class RedisRateLimiter {
    private RedisTemplate<String, String> redisTemplate;
    private String key;
    private int limit;
    private int period;
    
    public RedisRateLimiter(RedisTemplate<String, String> redisTemplate, 
                           String key, int limit, int period) {
        this.redisTemplate = redisTemplate;
        this.key = key;
        this.limit = limit;
        this.period = period;
    }
    
    public boolean tryAcquire() {
        String script = "local current = redis.call('incr', KEYS[1]) " +
                        "if current == 1 then " +
                        "redis.call('expire', KEYS[1], ARGV[1]) " +
                        "end " +
                        "return current <= tonumber(ARGV[2])";
        return Boolean.TRUE.equals(redisTemplate.execute(
            new DefaultRedisScript<>(script, Boolean.class), 
            Collections.singletonList(key), 
            String.valueOf(period), String.valueOf(limit)));
    }
}

// 使用示例
RedisRateLimiter limiter = new RedisRateLimiter(redisTemplate, "rate:api", 100, 60);
if (limiter.tryAcquire()) {
    // 允许请求通过
    // ...
} else {
    // 请求被限流
}

13.3 排行榜

// 使用Redis实现排行榜
public class RedisLeaderboard {
    private RedisTemplate<String, String> redisTemplate;
    private String key;
    
    public RedisLeaderboard(RedisTemplate<String, String> redisTemplate, String key) {
        this.redisTemplate = redisTemplate;
        this.key = key;
    }
    
    public void addScore(String member, double score) {
        redisTemplate.opsForZSet().add(key, member, score);
    }
    
    public List<String> getTopMembers(int count) {
        return new ArrayList<>(redisTemplate.opsForZSet().reverseRange(key, 0, count - 1));
    }
    
    public List<String> getTopMembersWithScores(int count) {
        Set<ZSetOperations.TypedTuple<String>> tuples = 
            redisTemplate.opsForZSet().reverseRangeWithScores(key, 0, count - 1);
        List<String> result = new ArrayList<>();
        for (ZSetOperations.TypedTuple<String> tuple : tuples) {
            result.add(tuple.getValue() + ":" + tuple.getScore());
        }
        return result;
    }
    
    public Long getRank(String member) {
        return redisTemplate.opsForZSet().reverseRank(key, member);
    }
}

// 使用示例
RedisLeaderboard leaderboard = new RedisLeaderboard(redisTemplate, "leaderboard:game");
leaderboard.addScore("player1", 100);
leaderboard.addScore("player2", 200);
leaderboard.addScore("player3", 150);
List<String> topPlayers = leaderboard.getTopMembers(3);
// 结果: [player2, player3, player1]

13.4 消息队列

// 使用Redis实现消息队列
public class RedisMessageQueue {
    private RedisTemplate<String, String> redisTemplate;
    private String queueKey;
    
    public RedisMessageQueue(RedisTemplate<String, String> redisTemplate, String queueKey) {
        this.redisTemplate = redisTemplate;
        this.queueKey = queueKey;
    }
    
    public void push(String message) {
        redisTemplate.opsForList().rightPush(queueKey, message);
    }
    
    public String pop() {
        return redisTemplate.opsForList().leftPop(queueKey);
    }
    
    public String popWithTimeout(long timeout, TimeUnit unit) {
        return redisTemplate.opsForList().leftPop(queueKey, timeout, unit);
    }
    
    public long size() {
        return redisTemplate.opsForList().size(queueKey);
    }
}

// 生产者
RedisMessageQueue queue = new RedisMessageQueue(redisTemplate, "queue:messages");
queue.push("消息1");
queue.push("消息2");

// 消费者
while (true) {
    String message = queue.popWithTimeout(1, TimeUnit.SECONDS);
    if (message != null) {
        // 处理消息
        // ...
    }
}

13.5 缓存策略

// 使用Redis实现缓存策略
public class RedisCache {
    private RedisTemplate<String, Object> redisTemplate;
    private long defaultExpire;
    
    public RedisCache(RedisTemplate<String, Object> redisTemplate, long defaultExpire) {
        this.redisTemplate = redisTemplate;
        this.defaultExpire = defaultExpire;
    }
    
    public <T> T get(String key, Class<T> clazz) {
        return (T) redisTemplate.opsForValue().get(key);
    }
    
    public void set(String key, Object value) {
        set(key, value, defaultExpire);
    }
    
    public void set(String key, Object value, long expire) {
        redisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS);
    }
    
    public void delete(String key) {
        redisTemplate.delete(key);
    }
    
    public <T> T getOrSet(String key, Supplier<T> supplier, Class<T> clazz) {
        return getOrSet(key, supplier, defaultExpire, clazz);
    }
    
    public <T> T getOrSet(String key, Supplier<T> supplier, long expire, Class<T> clazz) {
        T value = get(key, clazz);
        if (value == null) {
            value = supplier.get();
            set(key, value, expire);
        }
        return value;
    }
}

// 使用示例
RedisCache cache = new RedisCache(redisTemplate, 3600);
User user = cache.getOrSet("user:1", () -> userService.findById(1), User.class);

十四、Redis最佳实践总结

14.1 键名设计最佳实践

  1. 命名规范

    • 使用冒号分隔不同部分:业务:类型:ID
    • 使用小写字母
    • 避免使用特殊字符
    • 控制键名长度
  2. 常见模式

    • 用户信息:user:1
    • 用户会话:session:abc123
    • 计数器:counter:pageviews
    • 排行榜:leaderboard:game
    • 缓存:cache:product:1

14.2 数据类型选择最佳实践

  1. 字符串(String)

    • 适用于简单数据:计数器、缓存、会话ID
    • 可以存储序列化的对象(JSON、二进制)
  2. 哈希(Hash)

    • 适用于对象属性:用户信息、产品详情
    • 可以部分更新,节省内存
  3. 列表(List)

    • 适用于有序集合:消息队列、最新动态
    • 可以实现分页
  4. 集合(Set)

    • 适用于无序唯一集合:标签、关注者
    • 支持集合运算:交集、并集、差集
  5. 有序集合(Sorted Set)

    • 适用于排序集合:排行榜、权重队列
    • 支持范围查询和排序

14.3 性能优化最佳实践

  1. 内存优化

    • 使用适当的数据类型
    • 避免大key(超过10KB)
    • 设置过期时间
    • 使用压缩算法
  2. 查询优化

    • 使用Pipeline批量操作
    • 使用连接池
    • 避免频繁操作
    • 使用适当的命令(如HGETALL替代多次HGET)
  3. 配置优化

    • 调整内存限制和淘汰策略
    • 配置持久化策略
    • 调整连接池大小
    • 启用慢查询日志

14.4 高可用最佳实践

  1. 主从复制

    • 至少配置一个从节点
    • 监控复制状态
    • 定期备份数据
  2. 哨兵模式

    • 至少配置三个哨兵节点
    • 配置合理的故障检测参数
    • 测试故障转移流程
  3. 集群模式

    • 至少配置三个主节点和三个从节点
    • 合理分配槽位
    • 监控集群状态

14.5 安全最佳实践

  1. 访问控制

    • 设置强密码
    • 限制命令执行权限
    • 定期更新密码
  2. 网络安全

    • 限制IP访问
    • 使用SSL/TLS加密
    • 禁用危险命令
  3. 数据安全

    • 定期备份数据
    • 加密敏感数据
    • 实施数据访问控制

相关文章:

  • 12-产品经理-维护模块
  • verilog学习--1、语言要素
  • 深挖 TypeScript 基础数据类型:应用与陷阱
  • 使用 `pandas` 库来读取 Excel 文件,并实现六种算法的遍历计算
  • 算法——整数规格化
  • 需求分析-用例图绘制、流程图绘制
  • 在Hive中,将数据从一个表查询并插入到另一个表
  • pulsar中的延迟队列使用详解
  • Golang系列 - 内存对齐
  • Linux中用gdb查看coredump文件
  • eprime相嵌模式实验设计
  • 【Linux内核】如何更加优雅阅读Linux内核源码(vscode)
  • Seata TCC模式是怎么实现的?
  • 国内外AI大模型汇总合集-文本类
  • NLP 梳理01 — 文本预处理和分词
  • 软件测试的本质:方法、流程与未来趋势
  • Cocos Creator 进行 Web 发布后,目录结构解析
  • AIP-215 API特定proto
  • 【MySQL基础】MySQL内连接(INNER JOIN)详解:高效关联查询的基础
  • 数字人:从科幻走向现实的未来(1/10)
  • 长沙公司网站开发/百度公司地址
  • 网站左侧导航代码/网站建设多少钱
  • 建设工程招标网站/成功营销案例100例
  • jsp做网站视频教程/什么是电商?电商怎么做
  • 网站背景怎么换/网络软文营销
  • 网站开发技术考试试卷/网址生成短链接