reids面试题

image.png

1 redis是单线程吗?

image.png

Redis是单线程
主要是指Redis的网络10和键值对读写是由一个线程来完成的,Redis在处理客户端的请求时包括获取(socket 读)、解析、执行、内容返回(socket 写) 等都由一个顺序串行的主线程处理,
但Redis的其他功能,比如持久化RDB、AOF、异步删除、集群数据同步等等,其实是由额外的线程执行的。Redis命令工作线程是单线程的,但是,整个Redis来说,是多线程的;

2 IO多路复用听说过吗?

什么叫IO?
input/output,针对文件的输入和输出。
Linux下的文件类型:
b(block块设备)
c(character字符设备)
d(directory目录)
-(普通文件)
l(line链接文件)
s(socket套接字文件)
p(pipe管道文件)
image.png

image.png

BIO:BIO(blocking I/O):同步阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销
BIO方式使用于连接数目比较小且固定的架构,这种服务方式对服务器资源要求比价高,并且局限于应用中,
image.png

package com.redis.redis01;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @version 1.0
 * @Author zhaozhiqiang
 * @Date 2023/11/10 9:08
 * @Param
 * @Description //TODO Socket(BIO,同步阻塞):BIO全称是Blocking IO,同步阻塞式IO,是JDK1.4之前的传统IO模型。
 */
public class SocketServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);

        while (true) {
            Socket socket = serverSocket.accept(); // 阻塞等待客户端连接
            new Thread(() -> {
                try {
                    BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    String message = reader.readLine(); // 阻塞等待客户端数据
                    System.out.println("Received: " + message);
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start(); // 为每个客户端连接创建一个新线程处理,可能导致线程资源耗尽。
        }
    }

}

NIO:NIO全称 java non-blocking IO。从JDK 1.4开始,java提供了一些列改进的输入/输出(I/O)的新特性,被称为NIO,是同步非阻塞的
NIO三大核心部分:Channel(通道),Buffer(缓冲区),Selector(选择器)
image.png

package com.redis.redis01;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

/**
 * @version 1.0
 * @Author zhaozhiqiang
 * @Date 2023/11/10 9:15
 * @Param
 * @Description //TODO NIO(同步非阻塞): Non-blocking,不单纯是 New,是解决高并发、I/O高性能的有效方式。
 */
public class NIOServer {
    public static void main(String[] args) throws IOException {
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        //使SocketChannel工作在非阻塞模式
        serverChannel.configureBlocking(false);
        serverChannel.socket().bind(new InetSocketAddress(8080));

        Selector selector = Selector.open();
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            selector.select(); // 阻塞等待就绪Channel
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();

            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();

                if (key.isAcceptable()) {
                    ServerSocketChannel server = (ServerSocketChannel) key.channel();
                    SocketChannel client = server.accept();
                    client.configureBlocking(false);
                    client.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    SocketChannel client = (SocketChannel) key.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    int bytesRead = client.read(buffer);
                    if (bytesRead > 0) {
                        buffer.flip();
                        System.out.println("Received: " + new String(buffer.array(), 0, bytesRead));
                        client.close();
                    }
                }
            }
        }
    }

}

3 redis为啥那么快?

3.1 基于内存操作
所有Redis的数据都存在内存中,因此所有的运算都是内存级别的,所以他的性能高
3.2 数据结构简单
Redis的数据结构是专门设计的,这些简单的数据结构的查找和操作时间大部分复杂度都是O(1),性能高
3.3 多路复用和非阻塞IO
Redis使用I/O多路复用功能来监听多个socket连接客户端,这样可以使用一个线程来处理多个请求,减少线程切换带来额开销,同时也避免了I/O阻塞操作
3.4 避免上下文切换
因为是单线程模型,因此就避免了不必要的上下文切换和多线程竞争,这就省去了多线程切换带来的时间和性能上的消耗,而且单线程不会导致死锁问题的发生

4 redis读写能力咋样?

官网介绍
大数据量高性能(Redis一秒可以写8万次,读11万次,NoSQL的缓存记录级,是一种细粒度的缓存,性能会比较高)

WINDOWNS局域网本地测试

image.png

已用内存80%情况测试

package com.redis.redis01;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import javax.annotation.Resource;
import java.util.UUID;

@SpringBootTest
public class TestWR {
    //注入模板 序列化
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    volatile boolean stop = false;

    /**
     * redis写测试能力;简单字符平均大概在7000左右
     * @throws InterruptedException
     */
    @Test
    void testWrite() throws InterruptedException {
        Runnable runnable = new Runnable() {
            public void run() {
                int i=0;
                while (!stop) {
                    //1 key和value都是UUID.randomUUID()+System.currentTimeMillis():每10秒683283个  每秒6833
                    //2 key和value都是UUID.randomUUID():每10秒67559个  每秒6755
                    //3 key和value都是从数字0开始递增:每10秒72562个  每秒72562
                    redisTemplate.opsForValue().set("" + i , "" + i);
                    i++;
                }
            }
        };
        new Thread(runnable).start();
        Thread.sleep(10000);
        new Thread(() -> {
            stop = true;//调用shtdown方法stop为true
        }).start();
    }

    /**
     * redis读测试能力;简单字符平均大概在8000左右,比写快点
     * @throws InterruptedException
     */
    @Test
    void testRead() throws InterruptedException {
        Runnable runnable = new Runnable() {
            public void run() {
                int i=0;
                while (!stop) {
                   
                    redisTemplate.opsForValue().get("" + i );
                    i++;
                }
                System.out.println("读取到:"+i);//读取到:81374 大概每秒8134
            }
        };
        new Thread(runnable).start();
        Thread.sleep(10000);
        new Thread(() -> {
            stop = true;//调用shtdown方法stop为true
        }).start();
    }
}

结论:读能力大概81374/s,写能力大概72562/s,当前是redis3.2本地测试,是局域网,网络i/o影响不大,主要是内存当前没测试之前我的内存已经80%了;

内存剩余50%情况下测试

写7475/s,读8828/s

linux局域网测试

单机版本16G

内存已占用60

image.png

写1025/s,读1080/s

内存已经占用16

image.png

写1230/s,读1214

集群版本(一台16台机器装3主3从,和上面单机共用)

写1222/s,1312/s

redis7配置多线程后测试

image.png

写1270/s,读1029

Jedis驱动测试

  /**
     * redis每秒读操作:
     *      1 windows本地测试       13085/s
     *      2 连接linux测试          1438/s
     *
     */
    @Test
    public void redisTester2() throws InterruptedException {
        // 1 connection 连接,通过指定ip和端口号
        Jedis jedis = new Jedis("172.16.204.51", 6379);
        // 2 指定访问服务器密码
        jedis.auth("123456");
        Runnable runnable = new Runnable() {
            public void run() {
                int i = 0;
                try {
                    while (!stop) {
                        i++;
                        jedis.set("test" + i, i + "");
                    }
                    // 打印1秒内对Redis的操作次数
                    System.out.println("redis每秒操作:" + i + "次");
                } finally {// 关闭连接
                    jedis.close();
                }
            }
        };
        new Thread(runnable).start();
        Thread.sleep(1000);
        new Thread(() -> {
            stop = true;//调用shtdown方法stop为true
        }).start();

    }

1 jedis比lettuce快2倍左右,达到13085/s,(相同环境测试)
2 受网络带宽影响很大,连接linux测试慢了10倍
3 目前spingboot2.0之后集成lettuce连接redis,实际情况也就是5000左右,不会向jedis上万的

结论

1 上述16G配置在一般中小型公司算是比较高的配置了,当前测试都是局域网,带宽没限制比外网好太多,所以主要影响的就是内存了,redis官方理论写8万,读11万,应该是内存和网络io没限制的情况下最理想的情况下,当然内存2个T的肯定比16g测试出来的高太多,没条件测试
2 同一个机器下集群确实比单机redis读写能力更好,现在微服务大多数服务器配置2G,4G,8G,16G,32G的因该比较少,这种情况下redis读写能力在5000/s左右,集群的话可能达到1万左右,已经够用了,当前这是大概的说话,实际还要看情况
3 Jedis连接比lettuce确实快了很多将尽2倍数,RedisTemplate底层是lettuce线程安全的

5 redis7默认是否开启多线程?

image.png

在单机模式下,可以开启多线程,但是在其他模式,最好不开启,Redis实例的 CPU开销不大但吞吐量却没有提升,可以考虑使用Redis7的多线程机制,加速网络处理,进而提升实例的吞吐量

6 redis是否支持多线程?

redis4之后才慢慢支持多线程,直到redis6/7稳定

7 redis是单线程的,如何利用多个cpu/内核?

CPU并不是您使用Redis的瓶颈,因为通常Redis要么受内存限制,要么受网络限制。例如,使用在平均Linux系统上运行的流水线Redis每秒可以发送一百万个请求,因此,如果您的应用程序主要使用0(N)或O(lg(N) )命令,则几乎不会使用过多的CPU。
但是,为了最大程度地利用CPU,您可以在同一中启动多个Redis实例,并将它们视为不同的服务器。在某个时候,单个盒子可能还不够,因此,如果您要使用多个CPU,则可以开始考虑更早地进行分片的某种方法。

8 Redis单线程为什么加了多线程特性?

8.1 因为单线程有单线程的问题,比如我要删除一个比较大的key,
del bigkey 会一直阻塞,等待删除完成,才能继续操作,会导致Redis主线程卡顿
所以引入了 惰性删除 可以有效避免Redis主线程卡顿
Redis 4.0 中就新增了多线程的模块,当然此版本中的多线程主要是为了解决删除数据效率比较低的问题的。
而lazy free的本质就是把某些cost(主要时间复制度,占用主线程cpu时间片)较高删除操作,从redis主线程剥离让BIO子线程来处理,极大地减少主线阻塞时间。从而减少删除导致性能和稳定性问题。
虽然引入了多个线程来实现数据的异步惰性删除等功能,但其处理读写请求的仍然只有一个线程,所以仍然算是狭义上的单线程

9 影响redis主要的性能瓶颈是内存/网络带宽/cpu?

image.png

内存和带宽

10 redis大key如何处理?

准备数据阶段

1 造数据
for((i=1;i<=100*10000;i++)); do echo “set k i v i v ivi” >> /tmp/redisTest.txt ;done;
2 pipe管道命令执行
cat /tmp/redisTest.txt | /usr/local/install/redis-6.2.5/src/redis-cli -h localhost -p 6379 -a 123456 --pipe

:::info
linux redis6 100万数据, windows redis3.2 231万数据
:::
image.png

10.1 你如何生产上限制keys*、flushdb、flushall等危险命令以及防止误删误用?

image.png

image.png

  • keys * / flushall / flushdb 严禁 在线上使用
  • keys * / flushall / flushdb 会造成阻塞,会导致Redis其他的读写都被延后甚至是超时报错,可能会引起缓存雪崩甚至数据库宕机

通过配置redis.conf禁用危险命令

rename-command keys ""
rename-command flushdb ""
rename-command flushall ""

重启禁用之后找不到命令
image.png

替代方案 scan类似与mysql limit,避免卡顿

image.png

 @Test
    void tesScan() throws InterruptedException {
        long start = System.currentTimeMillis();
        //需要匹配的key
        String patternKey = "1*";
        ScanOptions options = ScanOptions.scanOptions()
                //这里指定每次扫描key的数量(很多博客瞎说要指定Integer.MAX_VALUE,这样的话跟        keys有什么区别?)
                .count(10000)
                .match(patternKey).build();
        RedisSerializer<String> redisSerializer = (RedisSerializer<String>) redisTemplate.getKeySerializer();
        Cursor cursor = (Cursor) redisTemplate.executeWithStickyConnection(redisConnection -> new ConvertingCursor<>(redisConnection.scan(options), redisSerializer::deserialize));
        List<String> result = new ArrayList<>();
        while(cursor.hasNext()){
            result.add(cursor.next().toString());
        }
        //切记这里一定要关闭,否则会耗尽连接数。报Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisException: Could not get a
        cursor.close();
       log.info("scan扫描共耗时:{} ms key数量:{}",System.currentTimeMillis()-start,result.size());

    }

10.2 MEMORY USAGE命令你用过吗?

image.png

10.3 BigKey问题,多大算big?你如何发现?如何删除?如何处理?

多大算big

大的内容不是key本身,二十key对应的value
image.png
image.png

危害

  1. 内存不均,集群迁移困难
  2. 超时删除,大key导致阻塞
  3. 网络流量阻塞
如何产生、发现、删除

产生

  1. 社交类粉丝列表逐步递增
  2. 汇总统计某个报表,经年累月的积累

发现

redis-cli --bigkeys

redis-cli -h 127.0.0.1 -p 6379 -a 111111 --bigkeys
//每隔 100 条 scan 指令就会休眠 0.1s,ops 就不会剧烈抬升,但是扫描的时间会变长
redis-cli -h 127.0.0.1 -p 6379 –-bigkeys -i 0.1
image.png

  • 好处
    • 给出每种数据结构Top 1 bigkey,同时给出每种数据类型的键值个数+平均大小
  • 不足
    • 想查询大于10kb的所有key,–bigkeys参数就无能为力了,需要用到memory usage来计算每个键值的字节数

memory usage
image.png
image.png
删除bigkey

  • String
    • 一般用del,过于庞大 unlink
  • hash
    • 使用hscan每次获取少量field-value,再使用hdel删除每个field
    • 语法 hscan key cursor[match pattern] [count count]

image.png

  • ** list**
    • 使用 ltrim 渐进式逐步删除,直到全部删除
    • 命令 redis localhost:6379> ltrim key_name start stop

image.png
image.png

  • set
    • 使用sscan 每次获取部分元素,再使用 srem 命令删除每个元素
  • image.png

image.png

  • zset
    • 使用zscan每次获取部分元素,再使用ZREMRANGEBYRANK 命令删除每个元素
    • 命令

image.png

10.4 BigKey你做过调优吗?惰性释放lazyfree了解吗?

  • 阻塞和非阻塞删除命令

image.png

10.5 MoreKey问题,生产上redis数据库有1000w记录,你如何遍历?keys*可以吗?

1 scan 命令代替 keys ,避免卡顿

11 双写一致性

11.1 双写一致性,你先动缓存redis还是数据库mysql,why?

4中双写一致性更新策略

先更新数据库,再更新缓存

问题

  • 回写redis失败,读到redis脏数据
  • 多线程下/高并发下,回写redis延迟,redis更新的数据不是最终结果,导致督导redis脏数据,mysql和redis数据不一致

image.png

先更新缓存,再更新数据库

问题

  • 一般把mysql写入作为准则,保证最后的解释,不推荐这样做
  • 多线程高并发下,redis和mysql数据不一致

image.png

先删除缓存,再更新数据库

a线程删除redis,正在更新mysql还没commit,b线程读取mysql回写redis,a线程更新完成mysql发现redis 还是之前旧值

解决方案延时双删

image.png

延迟双删问题
这个删除该休眠多久呢?
  • 统计线程读取和写入缓存的操作时间,自行评估项目中读业务数据耗时逻辑,进行估算,然后写数据的休眠时间再读取业务逻辑耗时的基础上增加百毫秒即可
  • 新启动一个后台监控程序,watchDog监控程序会加时
这种同步淘汰策略,吞吐量降低咋么办?

image.png

先更新数据库,再删除缓存

image.png
如果删除redis中的key不成功,如何解决
消息队列
image.png

总结

先更新数据库,再删除缓存
如果业务层要求必须读取一致性的数据,那么我们就需要在更新数据库时,先在Redis缓存客户端暂停并发读请求,等数据库更新完、缓存值删除后,再读取数据,从而保证数据一致性,这是理论可以达到的效果,但实际,不推荐,因为真实生产环境中,分布式下很难做到实时一致性,一般都是最终一致性。

11.2 延迟双删你做过吗?会有哪些问题?

参照3.1 3.2

11.3 微服务查询redis无mysql有,为保证数据双写一致性回写redis需要注意仕么?

11.4 双加锁策略你了解吗?如何经量避免缓存击穿?

双锁策略定义

  • 多个线程同时去查询数据库的这条数据,就在第一个查询数据的请求上使用一个互斥锁来锁住他。
  • 其他线程获取不到锁就一直等待,等第一个线程查询到了数据,然后做了缓存
  • 后面的线程进来发现已经有了缓存,就直接走缓存

避免缓存击穿

1 设置热点数据永不过期
2 加互斥锁,如下代码

/**
     * 压力测试结果
     *  1 id存在
     *   type=0,qps=1000,id存在    吞吐量    979.4/s   平均值  2
     *   type=0,qps=10000,id存在   吞吐量   1550.2/s   平均值  14
     *   type=1,qps=10000,id存在   吞吐量   4207.2/s   平均值  13
     *  2 id不存在(null,失效,攻击能情况)
     *    type=0,qps=1000,id不存在    吞吐量    1541.3/s   平均值  42
     *    type=0,qps=10000,id不存在   吞吐量   1420.2/s   平均值  99
     *    type=1,qps=10000,id不存在   吞吐量   1582.7/s   平均值  89
     *
     * 结论:
     *    1 重量级锁synchronized的吞吐量小于轻量级可重入锁ReentrantLock的吞吐量
     *    2 重量级锁synchronized的平均响应时间大于轻量级锁ReentrantLock
     *    3 双入保护机制能有效的保护系统的健壮性
     *
     * @param id
     * @param type
     * @param qps
     * @return
     */
    @RequestMapping(value = "findUserById")
    public RedisBs findUserById(@RequestParam("id") Integer id, @RequestParam("type") int type, @RequestParam("qps")int qps) {
        com.redis.redis01.bean.RedisBs userById = service.findUserById(id,type,qps);
        return userById;

    }
package com.redis.redis01.service;

import com.redis.redis01.bean.RedisBs;
import com.redis.redis01.mapper.RedisBsMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.beans.Transient;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
@Service
public class RedisBsService {

    //定义key前缀/命名空间
    public static final String CACHE_KEY_USER = "user:";
    @Autowired
    private RedisBsMapper mapper;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    private static ReentrantLock lock = new ReentrantLock();

    /**
     * 业务逻辑没有写错,对于中小长(qps<=1000)可以使用,但是大厂不行:大长需要采用双检加锁策略
     *
     * @param id
     * @return
     */
    @Transactional
    public RedisBs findUserById(Integer id,int type,int qps) {
        //qps<=1000
        if(qps<=1000){
            return qpsSmall1000(id);
        }
        //qps>1000
        return qpsBig1000(id, type);
    }

    /**
     * 加强补充,避免突然key失效了,或者不存在的key穿透redis打爆mysql,做一下预防,尽量不出现缓存击穿的情况,进行排队等候
     * @param id
     * @param type 0使用synchronized重锁,1ReentrantLock轻量锁
     * @return
     */
    private RedisBs qpsBig1000(Integer id, int type) {
        RedisBs redisBs = null;
        String key = CACHE_KEY_USER + id;
        //1先从redis里面查询,如果有直接返回,没有再去查mysql
        redisBs = (RedisBs) redisTemplate.opsForValue().get(key);
        if (null == redisBs) {
            switch (type) {
                case 0:
                    //加锁,假设请求量很大,缓存过期,大厂用,对于高qps的优化,进行加锁保证一个请求操作,让外面的redis等待一下,避免击穿mysql
                    synchronized (RedisBsService.class) {
                        //第二次查询缓存目的防止加锁之前刚好被其他线程缓存了
                        redisBs = (RedisBs) redisTemplate.opsForValue().get(key);
                        if (null != redisBs) {
                            //查询到数据直接返回
                            return redisBs;
                        } else {
                            //数据缓存
                            //查询mysql,回写到redis中
                            redisBs = mapper.findUserById(id);
                            if (null == redisBs) {
                                // 3 redis+mysql都没有数据,防止多次穿透(redis为防弹衣,mysql为人,穿透直接伤人,就是直接访问mysql),优化:记录这个null值的key,列入黑名单或者记录或者异常
                                return new RedisBs(-1, "当前值已经列入黑名单");
                            }
                            //4 mysql有,回写保证数据一致性
                            //setifabsent
                            redisTemplate.opsForValue().setIfAbsent(key, redisBs,7l, TimeUnit.DAYS);
                        }
                    }
                    break;
                case 1:
                    //加锁,大厂用,对于高qps的优化,进行加锁保证一个请求操作,让外面的redis等待一下,避免击穿mysql
                    lock.lock();
                    try {
                        //第二次查询缓存目的防止加锁之前刚好被其他线程缓存了
                        redisBs = (RedisBs) redisTemplate.opsForValue().get(key);
                        if (null != redisBs) {
                            //查询到数据直接返回
                            return redisBs;
                        } else {
                            //数据缓存
                            //查询mysql,回写到redis中
                            redisBs = mapper.findUserById(id);
                            if (null == redisBs) {
                                // 3 redis+mysql都没有数据,防止多次穿透(redis为防弹衣,mysql为人,穿透直接伤人,就是直接访问mysql),优化:记录这个null值的key,列入黑名单或者记录或者异常
                                return new RedisBs(-1, "当前值已经列入黑名单");
                            }
                            //4 mysql有,回写保证数据一致性
                            redisTemplate.opsForValue().set(key, redisBs);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        //解锁
                        lock.unlock();
                    }
            }
        }
        return redisBs;
    }
    private RedisBs qpsSmall1000(Integer id) {
        RedisBs redisBs = null;
        String key = CACHE_KEY_USER + id;
        //1先从redis里面查询,如果有直接返回,没有再去查mysql
        redisBs = (RedisBs) redisTemplate.opsForValue().get(key);
        if (null == redisBs) {
            //2查询mysql,回写到redis中
            redisBs = mapper.findUserById(id);
            if (null == redisBs) {
                // 3 redis+mysql都没有数据,防止多次穿透(redis为防弹衣,mysql为人,穿透直接伤人,就是直接访问mysql),优化:记录这个null值的key,列入黑名单或者记录或者异常
                return new RedisBs(-1, "当前值已经列入黑名单");
            }
            //4 mysql有,回写保证数据一致性
            redisTemplate.opsForValue().set(key, redisBs);
        }
        return redisBs;
    }

}

11.5 redis和mysql双写100%会处纰漏,做不到强一直性,你如何保证最终一致性?

以mysql写入数据为准,对缓存操作做最大努力即可,也就是说,写入mysql成功,缓存更新失败,那么只要达到过期时间,后面请求自然会从数据库中读取新值然后回填缓存,达到最终一致性

12 mysql有记录改动了,立刻同步反应到redis?该如何做?

canal 监听mysql回写redis
https://github.com/alibaba/canal

image.pngcanal [kə’næl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费
早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同步的业务需求,实现方式主要是基于业务 trigger 获取增量变更。从 2010 年开始,业务逐步尝试数据库日志解析获取增量变更进行同步,由此衍生出了大量的数据库增量订阅和消费业务。
基于日志增量订阅和消费的业务包括

  • 数据库镜像
  • 数据库实时备份
  • 索引构建和实时维护(拆分异构索引、倒排索引等)
  • 业务 cache 刷新
  • 带业务逻辑的增量数据处理

当前的 canal 支持源端 MySQL 版本包括 5.1.x , 5.5.x , 5.6.x , 5.7.x , 8.0.x

13 对于亿级数据的收集、清洗、统计、展现,需要用仕么技术实现?介绍一下pv、uv、pau?仕么是基数统计?

UV: Unique Visitor ,独立访客数,是指在一个统计周期内,访问网站的人数之和。一般理解客户ip,需要去重
PV : Page View,浏览量,是指在一个统计周期内,浏览页面的数之和。不需要去重
DAU: Daily Active User 日活跃用户数量;去重
DNU:Daily New User,日新增用户数
MAU:Monthly New User,月活跃用户;去重
需要使用redis hyperloglog基数统计数据结构来实现
基数统计:数据集中不重复的元素的个数

HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的内存空间总是固定的、并且是很小的。因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身;
HyperLogLog 优势在于只需要花费 12 KB 内存,就可以计算接近 2^64 个元素的基数,和元素越多就越耗费内存的 Set 和 Hash 类型相比,HyperLogLog 就非常节省空间。所以,非常适合统计百万级以计网站的独立访客数场景。
image.png

14 布隆过滤器了解过吗?

由一个初值都为零的bit数组和多个哈希函数构成,用来快速判断集合中是否存在某个元素

目的
减少内存占用
方式
不保存数据信息,只是在内存中做一个是否存在的标记flag
本质
判断具体数据是否村在于一个大的集合中
布隆过滤器是一种类似 set 的数据结构,只是统计结果在巨量数据下有点小瑕疵,不够完美

  • 它实际上是一个很长的二进制数组(00000000)+一系列随机hash算法映射函数,主要用于判断一个元素是否在集合中。
  • 通常我们会遇到很多要判断一个元素是否在某个集合中的业务场景,一般想到的是将集合中所有元素保存起来,然后通过比较确定。
  • 链表、树、哈希表等等数据结构都是这种思路。但是随着集合中元素的增加,我们需要的存储空间也会呈现线性增长,最终达到瓶颈。同时检索速度也越来越慢,上述三种结构的检索时间复杂度分别为O(n),O(logn),O(1)。这个时候,布隆过滤器(Bloom Filter)就应运而生

能干嘛

  • 高效地插入和查询,占用空间少,返回地结果是不确定性 + 不完美性
    • 一个元素如果判断结果:存在时,元素不一定存在,不存在时,元素一定不存在
  • 布隆过滤器可以添加元素,但是不能删除元素
    • 涉及到hashcode判断依据,删掉元素会导致误判率增加
    • 为什么不能删掉?
      - 因为他是有多个 hash 函数,对一个值进行多次 hash 运算,将获得的每个值,在对应位置存 1 ,容易导致这个 1 也代表别的值,一旦删除,另一个值也无法通过

image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/157498.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

结合 Django 和 Vue.js 打造现代 Web 应用

概要 在 Web 开发的世界里&#xff0c;Django 和 Vue.js 分别是后端和前端两个非常流行的框架。Django 以其强大的后端能力、快速开发以及安全性而著称&#xff0c;而 Vue.js 因其简洁、灵活和易于上手在前端开发领域广受欢迎。 本篇文章将详细介绍如何将 Django 与 Vue.js 结…

Jetson简介、编程开发与环境搭建

Jetson简介、编程开发与环境搭建 简介常用指令Jetpack环境搭建 简介 Jetson是由NVIDIA推出的一系列嵌入式系统&#xff0c;旨在用于机器学习和人工智能应用的开发。Jetson平台通常使用NVIDIA的GPU加速技术&#xff0c;以提供高性能的计算能力。NVIDIA推出了多个Jetson系列的产…

成集云 | 企业微信集成用友T+ | 解决方案

源系统成集云目标系统 方案介绍 用友T是一款由用友畅捷通推出的新型互联网企业管理系统&#xff0c;它主要满足成长型小微企业对其灵活业务流程的管控需求&#xff0c;并重点解决往来业务管理、订单跟踪、资金、库存等管理难题。 企业微信是一款通讯与办公工具&#xff0c;具…

NJU操作系统公开课笔记(1)

目录 一.计算机系统概述 二.计算机硬件系统 三.计算机软件系统 四.计算机操作技术的发展 五.计算机OS 1.资源管理的角度 2. 程序控制的角度 3.OS控制计算机的角度 4.人机交互的角度 5.程序接口的角度 6.系统结构的角度 单道批处理系统 多道批处理系统 分时系统 …

【Git学习二】时光回溯:git reset和git checkout命令详解

&#x1f601; 作者简介&#xff1a;一名大四的学生&#xff0c;致力学习前端开发技术 ⭐️个人主页&#xff1a;夜宵饽饽的主页 ❔ 系列专栏&#xff1a;JavaScript小贴士Git等软件工具技术的使用 &#x1f450;学习格言&#xff1a;成功不是终点&#xff0c;失败也并非末日&a…

链路追踪,助您洞悉数据联动分析的奥秘

前言 在当今复杂的分布式系统中&#xff0c;了解请求在不同服务之间的传递路径和性能情况对于系统的性能优化至关重要。链路追踪通过记录和分析请求在系统中的传递路径和性能数据&#xff0c;为实现数据联动分析提供了重要的支持。我们曾谈论观测云提供火焰图能够实现链路追踪…

开发知识点-前端-webpack

webpack技术笔记 一、 介绍二、 下载使用 一、 介绍 Webpack是一个现代 JavaScript 应用程序的静态模块打包器 打包&#xff1a;可以把js、css等资源按模块的方式进行处理然后再统一打包输出 静态&#xff1a;最终产出的静态资源都可以直接部署到静态资源服务器上进行使用 模…

mysql之rsync远程同步

&#xff08;一&#xff09;rsync 1、rsync&#xff1a;是一个开源的快速备份工具&#xff0c;可以在不同主机之间同步整个目录 2、在远程同步中&#xff0c;一个是源端&#xff0c;一个是发起端 &#xff08;1&#xff09;源端负责文件的原始位置&#xff0c;发起端和源端的…

最新随机小姐姐无水印开源版HTML源码

推荐一款高质量的HTML引流源码&#xff0c;适合浏览小姐姐短视频 这款源码具有出色的用户界面设计&#xff0c;给人视觉上的美感 视频播放流畅稳定&#xff0c;给用户带来良好的观影体验 小姐姐们的外貌也非常美丽吸引人 想要浏览精彩的小姐姐短视频&#xff0c;不妨试试这款…

Equifax案例分析与合规性场景实践

在当今数字化时代&#xff0c;数据安全已经成为各个组织和企业亟待解决的问题。尤其是在数据泄露事件不断增多的背景下&#xff0c;保护敏感数据免受非法访问和泄露变得尤为紧迫。为了应对这一挑战&#xff0c;许多组织和企业开始利用密钥管理服务(KMS)来加强其数据安全性&…

易点易动RFID管理系统:年终固定资产盘点的革命

随着现代企业规模的扩大和固定资产数量的增加&#xff0c;年终固定资产盘点成为了一项繁琐而耗时的任务。传统的手工盘点方法不仅效率低下&#xff0c;还容易出现错误和遗漏。为了解决这一难题&#xff0c;易点易动RFID管理系统应运而生。本文将重点介绍易点易动RFID管理系统在…

基于SSM的个人通讯录(有报告)。Javaee项目。

演示视频&#xff1a; 基于SSM的个人通讯录&#xff08;有报告&#xff09;。Javaee项目。 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 项目介绍&#xff1a; 采用M&#xff08;mod…

分享一个字体抽取软件

一、先下载软件。 地址 蓝奏云 密码 bjnr Fontmin-v0.2.0-win64 下载下来直接就是exe文件&#xff0c;直接双击安装即可。 二、打开默认如图显示 三、接下来把你的字体文件拖拽到里面 四、在把你用到这个字体的文字填入到框内 五、最后点击生成。 生成的字体文件只有2k了…

Eclipse切换中文环境

PACK包链接 地址&#xff0c;进入后可以看到不同版本的包。 要选择跟自己Eclipse版本一致的包&#xff0c;比如我的Eclipse启动界面如下&#xff0c;我就要找Helios的包&#xff08; Juno、Indigo、Helios、Kepler这些具体怎么划分的我也不清楚&#xff09;。 在线安装 打…

Oracle OCM考试(史上最详细的介绍,需要19c OCP的证书)

Oracle 19c OCM考试和之前版本的OCM考试差不多&#xff0c;对于考生来说最大的难点是题量大&#xff0c;每场3小时&#xff0c;一共4场&#xff0c;敲键盘敲得手抽筋。姚远老师&#xff08;v:dataace&#xff09;的很多Oracle OCP学员都对19c OCM考试很有兴趣&#xff0c;这里给…

电子眼与无人机在城市安防中的协同应用研究

随着城市化进程的快速推进&#xff0c;城市安全问题成为了人们关注的焦点。传统的安防手段已经无法满足现代城市复杂多变的安全需求。因此&#xff0c;结合电子眼与无人机技术&#xff0c;实现二者之间的协同应用&#xff0c;成为提升城市安防能力的重要途径。 一、电子眼与无人…

独立站和跨境电商平台的一个优缺点对比,哪个更适合你?

不管哪个行业都是一样的&#xff0c;有优点也有缺点&#xff0c;就像人们常说的人无完人一样。然后&#xff0c;我们来说说最近比较受关注的跨境电商独立站和跨境电商平台的一个优缺点对比吧&#xff01; 了解跨境电商独立站 建站&#xff0c;测试模板网站 跨境电商独立站 优点…

ChatGPT被曝测试新功能:学习所有历史聊天,还能重置记忆、“阅后即焚”

丰色 发自 凹非寺 量子位|公众号QbitAI ChatGPT可能要上新一项重大功能了。 那就是记住你之前的聊天内容并不断从中学习&#xff08;并非单纯保存历史记录&#xff09;&#xff0c;从而了解你的偏好和信息&#xff0c;用于在日后聊天派上用场。 比如你可以给它下达一句“始终…

相关系数和协方差的关系与区别

相关系数和协方差都是用来衡量两个变量之间关系的统计量&#xff0c;它们在描述变量之间的线性关系上提供了一些信息。下面是它们的关系与区别&#xff1a; 关系&#xff1a; 相关系数的计算涉及到协方差。相关系数等于协方差除以两个变量的标准差的乘积。具体而言&#xff0c…

利用邮件营销提升电商营业额的策略与方法

随着电子商务的迅速发展&#xff0c;各种推广和营销手段层出不穷&#xff0c;但在这个信息泛滥的时代&#xff0c;如何有效地吸引用户关注并提升电商的营业额成为了每一个电商人关注的焦点。而邮件营销正是一个被广泛应用且具有潜力的推广方式。通过合理规划和执行邮件营销策略…