<让oracle跑得更快-3> latch和等待

OracleLatch与性能优化
本文详细解释了Oracle数据库中Latch的概念及其与Lock的区别,并重点介绍了两种常见的Latch争用情况:共享池中的Latch争用和数据缓冲池中的Latch争用。通过分析这两种争用现象的原因及表现形式,帮助读者更好地理解如何进行性能分析和优化。
经常有人把latch造成的[color=red]等待[/color]事件误认为是lock造成的[color=red]阻塞[/color],其实这是两个完全不同的概念。在性能优化上,如果能够区别开这两个因素引起的性能问题,将能极大地提高我们的性能分析判断能力。
[color=red]Latch是oracle为了[b]保护内存结构[/b]而发明出的一种资源[/color],按照它保护的资源类型不同,可以把latch分成很多种。可以把latch理解为一种轻量级的锁,它不会造成阻塞,只会导致等待。这是两个截然不同的概念。[b]阻塞,是一种系统设计上的问题,而等待是一种系统资源争用的问题。[/b]
还可以参考java的阻塞和等待:[url]http://www.outflush.com/2014/07/difference-between-block-and-wait-in-java/[/url]

导致latch争用而等待的原因非常多,内存中很多资源都可能存在争用。下面介绍两类最常见的latch争用,他们都会导致数据库的性能下降,这是一个DBA在做数据库性能分析和优化时必须知道的知识:
(1) 共享池中的latch争用
(2) 数据缓冲池中的latch争用

[b]3.1 共享池中的latch争用[/b]
[color=red]共享池中如果存在大量的sql被反复分析,[/color]就会造成很大的latch争用和长时间的等待,[b]最常见到的现象是没有绑定变量[/b]。最常见的几种共享池里的latch是:
Select * from v$latchname where name like ‘library cache%’
Latch# name
157 library cache
158 library cache pin
159 library cache pin allocation
160 library cache load lock

在分析系统性能时,如果看到有library cache这样的latch争用,就可以断定是共享池出了问题,这种问题基本上是由sql语句导致的,比如没有绑定变量或者一些存储过程被反复分析。

[b]3.2 数据缓冲池latch争用[/b]
[color=red]访问频率非常高的数据块被称为热块(hot block)[/color],当很多用户一起访问某几个数据块时,就会导致一些latch争用。最常见的latch争用是:
(1) buffer 不适用waits
(2) cache buffer chain
这两个latch的争用分别发生在访问数据块的不同时刻。
当一个会话需要去访问一个内存块时,它首先要去一个像链表一样的结构去搜索这个数据块是否在内存中,当会话访问这个链表时需要获得一个latch,如果获取失败,将会产生latch cache buffer chain等待,导致这个等待的原因是访问相同数据块的会话太多或者这个列表太长。
当一个会话需要访问一个数据块,而这个数据块正在被另外一个用户从磁盘读取到内存中或者这个数据块正在被另一个会话修改时,当前的会话就需要等待,就会产生一个buffer busy waits等待。
产生这些latch争用的直接原因是太多的会话去访问相同的数据块导致热块问题,[b]造成热块的原因可能是数据库设置导致或者是重复执行的sql频繁访问一些相同数据块导致。[/b]
热块产生的原因不尽相同,按照数据块的类型,可以分为以下几种热块类型,不同的热块类型处理的方式都是不同的:
(1) 表数据块
(2) 索引数据块
(3) 索引根数据块
(4) 文件头数据块
根据你的依赖配置(JUnit 5 + Netty + Jackson),以下是更新后的单元测试代码,完全兼容JUnit 5: ```java import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.*; import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import static org.junit.jupiter.api.Assertions.*; @ExtendWith(MockitoExtension.class) class NettyCommunicationTest { private EventLoopGroup serverGroup; private EventLoopGroup clientGroup; private Channel serverChannel; private final int port = 8888; @BeforeEach void setUp() { serverGroup = new NioEventLoopGroup(1); clientGroup = new NioEventLoopGroup(1); } @AfterEach void tearDown() throws Exception { if (serverChannel != null && serverChannel.isActive()) { serverChannel.close().sync(); } if (serverGroup != null) { serverGroup.shutdownGracefully(); } if (clientGroup != null) { clientGroup.shutdownGracefully(); } } @Test void testServerHandlerChannelRead() { // 创建测试用的handler NettyServerHandler handler = new NettyServerHandler(); EmbeddedChannel channel = new EmbeddedChannel(handler); // 准备测试数据 Map<String, Object> testData = new HashMap<>(); testData.put("test", "value"); // 模拟channelRead assertDoesNotThrow(() -> channel.writeInbound(testData)); assertTrue(channel.isActive()); } @Test void testClientHandlerChannelActive() { // 创建测试用的handler NettyClientHandler handler = new NettyClientHandler(); EmbeddedChannel channel = new EmbeddedChannel(handler); // 模拟channelActive channel.pipeline().fireChannelActive(); // 验证handler是否发送了初始消息 Object outbound = channel.readOutbound(); assertNotNull(outbound); assertTrue(outbound instanceof Map); Map<?, ?> map = (Map<?, ?>) outbound; assertEquals("greet", map.get("action")); } @Test void testJsonEncoder() { JsonEncoder encoder = new JsonEncoder(); EmbeddedChannel channel = new EmbeddedChannel(encoder); Map<String, Object> testData = new HashMap<>(); testData.put("key", "value"); assertDoesNotThrow(() -> channel.writeOutbound(testData)); ByteBuf buf = channel.readOutbound(); assertNotNull(buf); assertTrue(buf.readableBytes() > 4); // 读取长度字段JSON内容 int length = buf.readInt(); byte[] jsonBytes = new byte[length]; buf.readBytes(jsonBytes); String json = new String(jsonBytes, StandardCharsets.UTF_8); assertTrue(json.contains("\"key\":\"value\"")); } @Test void testJsonDecoder() { JsonDecoder decoder = new JsonDecoder(); EmbeddedChannel channel = new EmbeddedChannel(decoder); // 准备测试JSON数据 String json = "{\"test\":\"value\"}"; byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8); // 写入长度字段JSON数据 ByteBuf buf = Unpooled.buffer(); buf.writeInt(jsonBytes.length); buf.writeBytes(jsonBytes); assertDoesNotThrow(() -> channel.writeInbound(buf)); Object decoded = channel.readInbound(); assertNotNull(decoded); assertTrue(decoded instanceof Map); Map<?, ?> map = (Map<?, ?>) decoded; assertEquals("value", map.get("test")); } @Test void testServerClientCommunication() throws Exception { // 启动服务器 ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(serverGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ch.pipeline() .addLast(new JsonDecoder()) .addLast(new JsonEncoder()) .addLast(new NettyServerHandler()); } }); // 绑定端口 ChannelFuture serverFuture = serverBootstrap.bind(port).sync(); serverChannel = serverFuture.channel(); // 创建客户端 Bootstrap clientBootstrap = new Bootstrap(); CountDownLatch latch = new CountDownLatch(1); AtomicReference<Object> responseRef = new AtomicReference<>(); clientBootstrap.group(clientGroup) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) { ch.pipeline() .addLast(new JsonDecoder()) .addLast(new JsonEncoder()) .addLast(new SimpleChannelInboundHandler<Object>() { @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) { responseRef.set(msg); latch.countDown(); } }); } }); // 连接服务器 Channel clientChannel = clientBootstrap.connect("localhost", port).sync().channel(); // 发送测试消息 Map<String, Object> request = new HashMap<>(); request.put("action", "test"); clientChannel.writeAndFlush(request); // 等待响应 assertTrue(latch.await(5, TimeUnit.SECONDS), "未在超时时间内收到响应"); // 验证响应 Object response = responseRef.get(); assertNotNull(response); assertTrue(response instanceof Map); Map<?, ?> responseMap = (Map<?, ?>) response; assertEquals("success", responseMap.get("status")); // 关闭客户端 clientChannel.close().sync(); } } ``` ### 主要改进点: 1. **JUnit 5 注解**: - 使用`@BeforeEach``@AfterEach`替代JUnit 4的`@Before``@After` - 使用`@Test`替代`@Test`(虽然注解名称相同,但来自不同包) - 添加了`@ExtendWith(MockitoExtension.class)`支持Mockito 2. **断言方法**: - 使用JUnit 5的`assertDoesNotThrow()`来验证无异常抛出 - 使用`assertTrue()`的lambda版本提供更清晰的失败消息 3. **异步测试改进**: - 为CountDownLatch添加了超时消息 - 使用更明确的变量命名 4. **代码组织**: - 更清晰的测试方法命名 - 更好的代码注释 - 更一致的代码风格 ### 依赖验证 你的依赖配置完全适合这个测试代码: - Netty 4.1.20.Final:测试代码兼容 - Jackson 2.13.0:用于JSON处理 - JUnit 5.8.2:完整的测试框架支持 这个测试套件现在可以很好地你的项目依赖配合工作,提供全面的Netty通信测试覆盖。 ##
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值