《尚硅谷Redis7教程》笔记(大厂篇)

2023-10-27

1:Redis单线程 vs 多线程

How can Redis use multiple CPUs or cores?(Redis如何使用多个CPU或内核)

https://redis.io/docs/getting-started/faq/#how-can-redis-use-multiple-cpus-or-cores

It’s not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound.

As of version 4.0, Redis has started implementing threaded actions.

面试题1:redis到底是单线程还是多线程

这个问题的重点:版本操作

官方文档里面的答案:As of version 4.0, Redis has started implementing threaded actions.

Redis 处理客户端请求命令是单线程的,持久化、异步删除等功能是由后台线程程去完成的。

网络是Redis的主要瓶颈,Redis 6以及以后版本,通过多个IO线程处理网络请求。

面试题2:IO多路复用

一个或者一组线程处理多个TCP连接,无需创建或者维护过多的线程。

一个服务端进程可以同时处理多个套接字描述符。

面试题3:redis为什么快

单线程为什么快的原因:

  • 基于内存操作:
  • 数据结构简单:
  • 多路复用和非阻塞IO:
  • 避免上下文切换:

Redis为什么选择单线程

单线程的问题:

删除大key的时候如果在主线程删除会造成卡顿,需要使用后台线程删除。

为什么逐渐加入多线程的特性?

在4.0之前,大key的删除会导致性能问题。

在4.0以后,例如UNLINK命令 The actual removal will happen later asynchronously.

UNLINK key [key ...] 4.0

Redis 性能影响因素?

  • CPU:命令的复杂度。
  • 内存:内存的大小。
  • 网络:

Unix五种IO模型

文件描述符:FD(FileDescriptor)

一个线程监控多个FD,

103 开启多线程IO特性支持

配置文件THREADED I/O模块配置下面两个内容。

# io-threads 4
# io-threads-do-reads no
io-threads-do-reads yes

2:BigKey

104 BigKey面试题

海量数据查询某一固定前缀的key
如何生产上限制危险命令
MEMORY USAGE 命令你用过吗
多大算big,如何发现?如何删除?如何处理?
BigKey调优?lazyfree?
数据库有1000w记录,如何遍历。

105 100w记录案例和生产故障

MoreKey案例

首先准备:Redis写入数据,这里我先写入 1w。

DBSIZE命令:Return the number of keys in the currently-selected database,返回当前选择的库key数量。

禁止危险的命令

keys * 命令统计耗时,生产环境禁止使用

通过配置禁止这些命令 1071行

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

效果

127.0.0.1:6379> keys *
(error) ERR unknown command 'keys', with args beginning with: '*'
127.0.0.1:6379> flushall
(error) ERR unknown command 'flushall', with args beginning with:
127.0.0.1:6379> flushdb
(error) ERR unknown command 'flushdb', with args beginning with:
127.0.0.1:6379>

SCAN、SSCAN、HSCAN、ZSCAN

不用keys*,避免卡顿应该用什么呢?

SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]

106 BigKey发现删除优化策略

多大算大:阿里云Redis开发规范

非 String 类型不要使用 del 删除,

大 key 的认定:

哪些危害:

  • 内存不均,集群迁移困难,超时删除,网络流量。

如何产生:

社交类:XXX命令粉丝列表,
汇总统计:

如何发现

–bigkeys

[root@localhost redis-one]# redis-cli -p 6379 -a 123456 --bigkeys
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.

# Scanning the entire keyspace to find biggest keys as well as
# average sizes per key type.  You can use -i 0.1 to sleep 0.1 sec
# per 100 SCAN commands (not usually needed).


-------- summary -------

Sampled 0 keys in the keyspace!
Total key length in bytes is 0 (avg len 0.00)


0 hashs with 0 fields (00.00% of keys, avg size 0.00)
0 lists with 0 items (00.00% of keys, avg size 0.00)
0 strings with 0 bytes (00.00% of keys, avg size 0.00)
0 streams with 0 entries (00.00% of keys, avg size 0.00)
0 sets with 0 members (00.00% of keys, avg size 0.00)
0 zsets with 0 members (00.00% of keys, avg size 0.00)
[root@localhost redis-one]#

MEMORY USAGE :统计某个key占用空间

127.0.0.1:6379> set k1 v1111111111111111111111111111111111111111111111
OK
127.0.0.1:6379> MEMORY USAGE k1
(integer) 104
127.0.0.1:6379>

如何删除

string:del

hash:hcan + hdel

list:LTRIM

set:SCAN + SREM

zset:zscan + zrange

BigKey 生产调优

redis.conf LAZY FREEING 1206行,默认配置和建议修改配置如下

lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no 	默认no,改为yes
replica-lazy-flush no    		默认no,改为yes 	
lazyfree-lazy-user-del no 		默认no,改为yes
lazyfree-lazy-user-flush no

在执行 DEL 命令时,释放内存也会放到后台线程中执行

3:缓存双写一致性之更新策略讨论

107 面试题概览

108 细则要求

版本1:如下

    @GetMapping("read")
    public String read(String key) {
        String val = stringRedisTemplate.opsForValue().get(key);
        if (ObjectUtils.isEmpty(val)) {
            val = fromDB();
            if (ObjectUtils.isEmpty(val)) {
                return null;
            }
            stringRedisTemplate.opsForValue().set(key, val);
        }
        return val;
    }

存在的问题:多线程环境下会导致过多的请求落到DB,解决思路:加锁。

加锁的粒度:synchronized 或者 分布式锁,具体业务具体分析。 版本v2:如下

    @GetMapping("read/v2")
    public String readV2(String key) {
        String val = stringRedisTemplate.opsForValue().get(key);
        if (ObjectUtils.isEmpty(val)) {
            //降低DB压力
            synchronized (ConsistencyController.class) {
                //多查一遍redis
                val = stringRedisTemplate.opsForValue().get(key);
                if (ObjectUtils.isEmpty(val)) {
                    val = fromDB();
                    if (ObjectUtils.isEmpty(val)) {
                        return null;
                    }
                    stringRedisTemplate.opsForValue().set(key, val);
                }
            }
        }
        return val;
    }

为什么要多查一遍Redis:最大限度降低DB压力,如果某一个JVM进程已经写入Redis其余无需执行读库的流程。

109 四种更新策略

这里重要的思想是:DB拥有数据正确性的唯一解释权,所以一定要先操作DB,否则大概率出现并发问题。

唯一可用策略:先更新数据库,后删除缓存

为了防止删缓存失败,引入MQ等中间件保障删缓存的一致性。

如果真的要求一致性应该考虑是否真的引入缓存。

如果真的需要引入缓存,只能通过分布式锁来解决。

4:Redis与MySQL数据双写一致性工程落地案例

110 工程落地和canal简介

监听MySQL改动中间件Canal;

https://github.com/alibaba/canal/wiki/

虽然视频这里介绍了 canal 中间件,但是我不会首先考虑,因为引入中间件的代价是如何维护中间件的高可用。

111 案例代码

后续如果遇到需要 canal 的场景回来补充。

5:案例落地实战bitmap/hyperloglog/geo

112 常见面试题

113 数据统计

UV:(Unique Visitor):独立访客,一般理解为IP,需要按IP去重。
PV:(page view):页面浏览量,不用去重。
DAU:(Daily Active User),日活跃用户数量。
MAU:(Monthly Active User),月活。

114 去重复思路和误差率

统计数量根据精确类型可以分为:精确和非精确。

其中精确可以使用HashSet和bitmaps等方案,非精确可以使用HyperLogLog等方案。

首先根据业务特点选择是否精确统计。

精确型数据结构的缺点:当数据量变大就会占用非常大的空间。

115 HyperLogLog案例

116 GEO 面试题

117 GEO统计附近元素

118 bitmap

bitmap简介和后续说明

6:布隆过滤器 BloomFiler

大数据量下的集合过滤—Bloom Filter

119 Bloom Filter(布隆过滤器)

案例:快速判断在海量数据中找到数据是否存在。

120 Bloom Filter(布隆过滤器)是什么

布隆过滤器是一种概率性数据结构。

选型:不要求数据绝对准确。

优点:占用空间少。

缺点:可能错报,不会漏报,有可能有,没肯定没。

原理:

解决缓存穿透问题:在 Redis 查询之前加一层布隆过滤器,如果布隆过滤器中存在,进入查询流程,如果布隆过滤器不存在,则说明数据不存在。

使用细节:1:尽量一次性给足容量。2:当实际元素超过初始化时,应该新建一个布隆过滤器,执行重新写入。

121 布隆过滤器自研案例

122 手写布隆过滤器

布谷鸟过滤器(Cuckoo Filter)

7: 缓存常见问题(预热、雪崩、击穿、穿透)

123 简介

124 预热、雪崩、穿透

预热

没有预热:第一个读请求会从DB读取数据回写到Redis,
预热:第一个读请求之前

雪崩

雪崩定义:同一时间key的大面积失效称为雪崩,Redis服务挂了严格意义上不属于雪崩的范畴。

预防+解决:

  • 针对key,分散过期时间或者永久有效。
  • 针对高可用,采用主从哨兵或者集群的方式,并且开启持久化机制。
  • 多级缓存思想,使用本地缓存预防雪崩。
  • 限流&降级
  • 人民币玩家,云数据库Redis。

穿透

定义:redis像一个摆设,每次请求都会跳过Redis,落到DB,最终导致DB压力升高严重可能宕机。
方案:

  • 空对象缓存(当请求一个Redis中不存在的key时,如果DB中也没有同样回写Redis,不过缓存的是null值)
  • 过滤器(布隆过滤器、谷歌guava等)

空对象缓存:
优点:简单,
缺点:如果遇到黑客攻击,会缓存非常多的无效key,最终导致内存占用增加,无效内存等问题。

过滤器:

下面使用 Guava 布隆过滤器演示案例

125 Guava 布隆过滤器

使用案例后续会补充

空间越大,误判率越低,空间越小,误判率越大。

126 缓存击穿

定义:热点key失效,大量请求落到DB。

危害:DB压力升高,严重可导致宕机。

解决方案:

  • 方案一:热点key不设置失效时间。
  • 方案二:从降低DB压力入手,采用不同粒度的锁。

127 聚划算案例

案例:后续代码补充

128 小总结

8:手写Redis分布式锁

129 简介

130 是什么

锁的种类,JVM锁或者分布式锁。

可靠的分布式锁的条件:独占性、高可用、防死锁、不乱抢、重入性。

独占性:只有一个线程可以持有锁。

高可用:

防死锁:超时控制机制,

不乱抢:不能操作别人的锁。

重入性:同一个节点获取锁之后,无需重复加锁。

131 迭代01

132 迭代02( lua脚本)

133 迭代03

使用 hash 结构替代 string,实现可重入性。

134 迭代04(可重入性)

135 迭代05 ()

136 迭代06(设计模式)

137 迭代07(自动续期)

考量:自动续期

138 小总结

9:RedLock算法和底层源码分析

139 RedLock底层Redisson源码深度分析

https://redis.io/docs/manual/patterns/distributed-locks/

后续补充

140 中

后续补充

141 下

后续补充

10:Redis的缓存过期淘汰策略

https://redis.io/docs/reference/eviction/

142 简介

143 内存查看和打满OOM

最大内存 maxmemory

maxmemory 100mb

Setting maxmemory to zero results into no memory limits.
This is the default behavior for 64 bit systems, while 32 bit systems use an implicit memory limit of 3GB.

查看内存使用情况 info memory

info memory

144 八种策略和使用建议

Eviction policies(驱逐策略)

  • noeviction: New values aren’t saved when memory limit is reached. When a database uses replication, this applies to the primary database
  • allkeys-lru: Keeps most recently used keys; removes least recently used (LRU) keys
  • allkeys-lfu: Keeps frequently used keys; removes least frequently used (LFU) keys
  • volatile-lru: Removes least recently used keys with the expire field set to true.
  • volatile-lfu: Removes least frequently used keys with the expire field set to true.
  • allkeys-random: Randomly removes keys to make space for the new data added.
  • volatile-random: Randomly removes keys with expire field set to true.
  • volatile-ttl: Removes keys with expire field set to true and the shortest remaining time-to-live (TTL) value.(存活时间最短的)

根据算法分类:ttl、random、lfu、lru、noeviction。根据是否有过期时间分为:allkeys和volatile。

f = frequency,频率,访问次数。
r = recent,最近,时间。

建议:避免bigkey,开启lazy-free。

11:Redis经典五大类型源码及底层实现

145 简介

SDS动态字符串
双向链表
压缩列表,ziplist
哈希表,hashtable
跳表,skiplist
整数集合,intset
快速列表,quicklist
紧凑列表,listpack

146 源码包简介

redis / src 目录

《Redis设计与实现》
《Redis5设计与源码分析》

bitmap实质String
hyperLoglog实质String
geo实质Zset
Stream实质Stream
bitfield看具体的key

147 dictEntry到RedisObject

待补充

148 redisObject各个字段含义

待补充

149 String类型数据结构

https://redis.io/commands/object-encoding/

OBJECT ENCODING

OBJECT ENCODING key

Redis objects can be encoded in different ways:

  • raw, normal string encoding.(普通字符串编码)
  • int, strings representing integers in a 64-bit signed interval, encoded in this way to save space.(节约空间)
  • embstr, an embedded string, which is an object where the internal simple dynamic string, sds, is an unmodifiable string allocated in the same chuck as the object itself. embstr can be strings with lengths up to the hardcoded limit of OBJ_ENCODING_EMBSTR_SIZE_LIMIT or 44 bytes.

Lists can be encoded as ziplist or linkedlist. The ziplist is the special representation that is used to save space for small lists.

Sets can be encoded as intset or hashtable. The intset is a special encoding used for small sets composed solely of integers.

Hashes can be encoded as ziplist or hashtable. The ziplist is a special encoding used for small hashes.

Sorted Sets can be encoded as ziplist or skiplist format. As for the List type small sorted sets can be specially encoded using ziplist, while the skiplist encoding is the one that works with sorted sets of any size.

150 SDS

12:Redis为什么快? epoll和IO多路复用深度解析

涉及视频的集数:165 - 177

165 简介

多路复用之前的网络模型:同步阻塞网络IO模型
多路复用要解决的问题:(复用)一个进程处理多个TCP连接。
存在的困难:假设一个进程对应10000条连接,如何才能知道哪一个连接可以进行读写呢?
我们希望当某个连接有事件发生时可以快速的将其找出来。

166 redis为什么这么快

Redis利用 epoll 实现IO多路复用,select、poll、epoll 都是Linux的多路复用机制,可以理解为越来越先进。

167 吃米线

168 同步、异步、阻塞、非阻塞

同步(A):等待
异步(B):回调、通知
同步和异步:获取调用结果的方式上。
阻塞(C):调用方什么都不做。
非阻塞(D):调用方继续忙自己的。
阻塞和非阻塞:等候调用结果的行为。
四种组合:
同步阻塞(AC):客户端等待结果。
同步非阻塞(AD):

169 BIO

c语言 recvfrom,

单线程模式:

server示例代码

public class RedisServer {
    public static void main(String[] args) throws Exception {
        try (ServerSocket serverSocket = new ServerSocket(6379)) {
            while (true) {
                System.out.println("等待建立连接");
                Socket accept = serverSocket.accept();
                int port = accept.getPort();
                System.out.println(port + "建立连接");
                InputStream is = accept.getInputStream();
                byte[] bytes = new byte[36];
                while (is.read(bytes) != -1) {
                    String value = new String(bytes, StandardCharsets.UTF_8);
                    System.out.println("读取到数据" + value);
                }
                is.close();
                accept.close();
            }
        }
    }
}

client示例代码

public class RedisClient1 {
    public static void main(String[] args) throws Exception {
        System.out.println("client1 start");
        try (Socket socket = new Socket("127.0.0.1", 6379)) {
            OutputStream os = socket.getOutputStream();
            int size = 0;
            while (true) {
                TimeUnit.MILLISECONDS.sleep(500);
                size++;
                if (size == 10) {
                    break;
                }
                byte[] bytes = UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8);
                os.write(bytes);
            }
            os.close();
        }
        System.out.println("client1 end");
    }
}

一次只能处理一个客户端。

多线程模式:

public class RedisServerMoreThread {

    public static void main(String[] args) throws Exception {
        try (ServerSocket serverSocket = new ServerSocket(6379)) {

            while (true) {
                System.out.println("等待一个新的连接");
                Socket accept = serverSocket.accept();
                new Thread(() -> {
                    try {
                        InputStream is = accept.getInputStream();
                        byte[] bytes = new byte[36];
                        while (is.read(bytes) != -1) {
                            String value = new String(bytes, StandardCharsets.UTF_8);
                            System.out.println("读取到数据" + value);
                        }
                        is.close();
                        accept.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                }).start();
            }
        }
    }
}

缺点:线程太多,

优化:1:使用线程池,2:NIO

tomcat 7 之前使用 BIO 多线程解决多连接。

170 nio

特点:使用轮训替代阻塞,线程关系一对多

public class RedisServerNio {

    public static void main(String[] args) throws Exception {
        List<SocketChannel> socketChannelList = new ArrayList<>();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        SocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 6379);
        serverSocketChannel.bind(socketAddress);
        serverSocketChannel.configureBlocking(false);

        System.out.println("server start...");

        while (true) {
            if (!socketChannelList.isEmpty()) {
                for (SocketChannel socketChannel : socketChannelList) {
                    int read = socketChannel.read(buffer);
                    if (read > 0) {
                        buffer.flip();
                        byte[] bytes = new byte[read];
                        buffer.get(bytes);
                        String value = new String(bytes, StandardCharsets.UTF_8);
                        System.out.println(value);
                        buffer.clear();
                    }
                }
            }

            SocketChannel accept = serverSocketChannel.accept();
            if (accept != null) {
                System.out.println("成功连接");
                serverSocketChannel.configureBlocking(false);
                socketChannelList.add(accept);
                System.out.println("socketChannelList大小为" + socketChannelList.size());
            }
        }
    }
}

存在的问题:

  • 1:无效轮训 ,当socket变多的时候,如果只有少量socket存在数据,仍然会每次循环无效的socket。
  • 2:资源浪费,涉及用户态和内核态频繁转换。

改进思路:将上述过程放入Linux内核,

171 io多路复用理论简介

I/O multiplexing,

Windows iocp类似于Linux epoll函数的作用。

nginx同样使用epoll接收请求

FileDescriptor(FD)文件描述符

172 从学术到人话版本

你是一个监考老师,有30个学生需要收卷,你有以下几种选择:

从1开始收,如果没写完,等待。(BIO)

呼叫29个老师,一个人应对一个学生,互不影响。(BIO多线程)

你站在讲台上,依次观察每一个学生,如果写完了收卷,重复这个过程。(Nio)

你站在讲台上等待,谁举手示意,你收谁的卷子。(多路复用)

173 能干嘛

174 select

man select

缺点:

  • 一个进程最大处理1024个客户端。
  • rest不能重用,每次有数据都需要置位。
  • FD数组拷贝内核态,仍然有开销
  • 仍然需要O(n)级别的遍历

175 poll

man poll

解决了:

  • 1024 限制
  • 实现了数组复用
    缺点:剩下两条

176 epoll

man epoll
epoll_create 
epoll_ctl
epoll_wait

一个socket生命周期只有一次拷贝过程,使用event事件通知机制,每次socket中有数据主动通知内核,不需要遍历socket。

177 兜底函数方案和系统选择

Redis

178 抢红包案例

业务描述:wx红包
需求分析:

  • 1:金额拆分,每个人抢到的金额尽量均匀,尽量不受到顺序的影响。
  • 2:每个人只能抢一次,并且记录抢红包信息。
  • 3:计算抢红包耗时。
  • 4:红包未抢完、超时金额回退。

二倍均值法:剩余M,剩余人数N,计算公式如下,(0,(M/N)*2)

@Api(tags = "微信红包")
@RestController
@RequestMapping("red-bag")
public class RedBagController {

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    public static final String LIST_KEY_PREFIX = "redbag:";
    public static final String HASH_KEY_PREFIX = "redbag:record:";

    @GetMapping("send")
    public void sendRedBag(int money, int peopleNum) {
        ArrayList<String> list = sf(money, peopleNum);
        String key = LIST_KEY_PREFIX + UUID.randomUUID().toString();
        stringRedisTemplate.opsForList().leftPushAll(key, list);
        stringRedisTemplate.expire(key, Duration.ofDays(1));
    }

    /**
     * 抢红包
     *
     * @param uid      实际 uid 来自于上下文或者其他地方
     * @param redBagId
     * @return
     */
    @GetMapping("rob")
    public String rob(String uid, String redBagId) {
        //红包记录
        String recordKey = HASH_KEY_PREFIX + redBagId;
        //红包list
        String redBagKey = LIST_KEY_PREFIX + redBagId;
        HashOperations<String, Object, Object> hashOps = stringRedisTemplate.opsForHash();
        //判断资格,
        Object userRobInfo = hashOps.get(recordKey, uid);
        //可以抢
        if (userRobInfo == null) {
            String money = stringRedisTemplate.opsForList().rightPop(redBagKey);
            //记录红包信息
            if (money != null) {
                hashOps.put(recordKey, uid, money);
                System.out.println("抢到了" + money + "红包");
                //    后续将数据数据统计........
                return money;
            }
            return "errorCode:-1,抢完了";
        }
        //不可以抢多次
        return "errorCode:-2,message:" + uid + "你已经抢过红包了,不能重新抢";
    }


    /**
     * 二倍均值算法
     */
    private ArrayList<String> sf(int money, int peopleNum) {
        ArrayList<String> list = new ArrayList<>(peopleNum);
        int useMoney = 0;
        for (int i = 0; i < peopleNum; i++) {
            //最后一个给所有的钱
            if (i == peopleNum - 1) {
                list.add(i, String.valueOf(money - useMoney));
                continue;
            }
            //剩余钱数
            int free = money - useMoney;
            //剩余人数
            int lastPeopleNum = peopleNum - i;
            //max金额
            int max = (free / lastPeopleNum) * 2;
            //【0,max)
            //实际【1,max),1是保底
            //【0.01,max)
            int intVal = 1 + new Random().nextInt(max - 1);
            useMoney += intVal;
            String val = String.valueOf(intVal);
            list.add(i, val);
        }
        return list;
    }

    public static void main(String[] args) {
        ArrayList<String> sf = new RedBagController().sf(100, 10);
        System.out.println(sf);
    }
}

说明:抢红包中的uid实际应该来自上下文而不是参数传入,案例只考虑到整数的情况,也就是最小金额1元。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《尚硅谷Redis7教程》笔记(大厂篇) 的相关文章

随机推荐

  • console.log机制

    介绍 前端在调试的时候很多都使用了console log 但大家都知道他的机制吗 遇到问题 在想要获取一个对象的某一个属性的属性值的时候 因为发送了ajax请求 然后在mounted阶段打印对象的属性时为空 而打印对象显示要获取的属性有值
  • Tomcat优化(一)-线程池优化

    tomcat处理请求的过程 在accept队列中接收连接 当客户端向服务器发送请求时 如果客户端与OS完成三次握手建立了连接 则OS将该连接放入accept队列 在连接中获取请求的数据 生成request 调用servlet容器处理请求 返
  • 能详细介绍审核任务分配有那些算法

    审核任务分配是一个有关人工智能和机器学习的研究领域 它的目的是分配审核任务 例如审核文本内容 给合适的审核员 以保证审核任务的高效性和准确性 下面是一些常见的审核任务分配算法 贪心算法 这是一种简单的分配算法 它按照审核员的审核能力和审核任
  • 记一次Gitlab升级, 14.6.0-ee到14.10.0-ee

    升级原因 Gitlab无法删除项目 点删除按钮后项目依然存在 打开后提示 No repository 不确定是否这个版本问题 想通过版本升级重装一遍看能否解决问题 升级过程 时间 2022 04 27 当前最新版本GitLab Enterp
  • 那些你可能不知道的浏览器奇技淫巧

    平常工作少不了用浏览器 以下分享一些浏览器的使用技巧 更好的有助于你的工作 ps 以下技巧均在 Chrome 浏览器下测试的 网页长截图 按 F12 弹出控制台 按 ctrl shift p 弹出输入框 输入full 选择 capture
  • 二货小易有一个W*H的网格盒子,网格的行编号为0~H-1,网格的列编号为0~W-1。每个格子至多可以放 一块蛋糕,任意两块蛋糕的欧几里得距离不能等于2。 对于两个格子坐标(x1,y1),(x2,y2)

    题目 二货小易有一个W H的网格盒子 网格的行编号为0 H 1 网格的列编号为0 W 1 每个格子至多可以放一块蛋糕 任意两块蛋糕的欧几里得距离不能等于2 对于两个格子坐标 x1 y1 x2 y2 的欧几里得距离为 x1 x2 x1 x2
  • JS-Array

    JS Array 数组的基本使用
  • 《软件测试的艺术》第九章 敏捷开发模式下的测试

    软件测试的艺术 第九章 敏捷开发模式下的测试 9 0 前言 9 1 敏捷开发的特征 9 2 敏捷测试 9 3 极限编程 XP 与测试 9 3 1 极限编程基础 9 3 1 1 XP计划 9 3 1 2 XP测试 9 3 2 极限测试 概念
  • 理解innodb的锁(record,gap,Next-Key lock)

    Record lock 单条索引记录上加锁 record lock锁住的永远是索引 而非记录本身 即使该表上没有任何索引 那么innodb会在后台创建一个隐藏的聚集主键索引 那么锁住的就是这个隐藏的聚集主键索引 所以说当一条sql没有走任何
  • 算法系列-leetcode-20.有效的括号

    20 有效的括号 简单 给定一个只包括 的字符串 s 判断字符串是否有效 有效字符串需满足 左括号必须用相同类型的右括号闭合 左括号必须以正确的顺序闭合 示例 1 输入 s 输出 true 示例 2 输入 s 输出 true 栈 遇到左括号
  • 科普:手机电池的保护电路

    转自 http www elecfans com yuanqijian dianchi dianchijichu 20120905287417 html 此篇文章主要介绍手机电池的保护电路 我们大家在使用电池的时候总会发生各种误操作 而手机
  • 【图像隐藏】基于DWT与SVD算法实现数字水印嵌入提取附matlab代码

    作者简介 热爱科研的Matlab仿真开发者 修心和技术同步精进 matlab项目合作可私信 个人主页 Matlab科研工作室 个人信条 格物致知 更多Matlab仿真内容点击 智能优化算法 神经网络预测 雷达通信 无线传感器 电力系统 信号
  • Spring State Machine

    Spring 状态机初探 一 spring statemachine 1 枚举定义 public enum OrderStatus WAIT PAYMENT 等待支付 WAIT RECEIVE 等待收货 FINISH 完成 public e
  • Javascript中获取数组最大值和最小值以及最大值和最小值的下标 没学对象前的最简单最基本的方法

    用for循环和if 为什么不用对象 因为这是还没学对象的时候留的作业 这个写法 数组里有重复值也可以识别 var arr 20 5 16 80 60 5 80 80 80 var max arr 0 var min arr 0 for va
  • python报错

    一 TypeError list indices must be integers or slices not str 暂无影响 未解决 二 Keyerror 解决方案 https blog csdn net jacke121 articl
  • C—数据的储存(下)

    文章目录 前言 一 练习一下 1 例一 2 例二 3 例三 4 例四 二 浮点型在内存中的储存 1 浮点数 2 浮点数存储 1 二进制浮点数 2 浮点数的存储规定 3 浮点数的取出规定 3 例题 总结 前言 个人主页 小沈熬夜秃头中 小编介
  • OCR技术原理

    OCR技术原理 文档识别技术功能特点图像输入 读取不同图像格式文件的算法 图像预处理 主要包括图像二值化 噪声去除 倾斜较正等算法 版面分析 将文档图片分段落 分行的算法就叫版面分析算法 字符切割 字符切割算法主要处理因字符粘连 断笔造成字
  • CCS6 配置工程头文件路径方法

    软件版本 Code Composer Studio Version 6 1 3 00034 配置头文件 给工程配置 右键工程名字没然后选择Properties 选择CCS Build gt Cxxxx Compiler gt Include
  • 【硬件设计】基于K78系列芯片的电源板电路

    文章目录 1 电源板介绍 1 1 电源板作用 1 2 电源板基本原理和类型 1 3 电源板应用场景 2 K78系列的线性稳压芯片介绍 2 1 K78系列线性稳压芯片的特点和基本参数 2 2 K78系列线性稳压芯片的工作原理 2 3 K78系
  • 《尚硅谷Redis7教程》笔记(大厂篇)

    1 Redis单线程 vs 多线程 How can Redis use multiple CPUs or cores Redis如何使用多个CPU或内核 https redis io docs getting started faq how