怪异的latch free – session allocation事件

本文记录了一次在Oracle数据库中遇到的严重latch free等待问题的排查与解决过程。问题出现在一个业务系统运行效率骤降的情况下,通过分析发现是由特定索引的高并发查询引发的。最终通过调整索引的degree参数解决了问题。
On October 8, 2007, in Oracle管理 , by 三少(David.Guo)

..

..

本来一直想写这个的,不过最近感觉身体不好,今天才写出来.

10月5日晚上,在客户现场配合业务运行.业务开始后,发现作同样业务的系统,有一个系统十分不正常,速度不如另外一个系统的1/4.客户无法忍受,其实我也无法忍受了,如果照那个速度,我估计要联系工作24小时才可以.

没有办法,对系统进行检查.发现在不正常的系统中,产生了严重的latch free等待.当看到这个的时候,我第一反应是sql语句有问题或者产生了热点,因为应用干的活是在一个表中查询,通过索引查询,但是同时有几十个session作同样的事情.仔细检查了sql语句,语句非常简单.

select user_id,nvl(substr(os_status,:”SYS_B_0″,:”SYS_B_1″),:”SYS_B_2″),nvl(substr(os_status,:”SYS_B_3″,:”SYS_B_4″),:”SYS_B_5″) from table_name where acc_id = :f1  

而执行计划走的索引,也是在acc_id上的索引.

经过检查索引和表的状态,发现表基本没有碎片,索引也是被最近被重建过的(后来才发现,问题就出在重建这个索引上,这是后话).

分析系统的latch free的小类,发现系统中,排名最高的几种latch free事件如下:

LATCH# NAME GETS MISSES SLEEPS

row cache objects 7309905930 1076355567 24799351

shared pool 1.2642E+10 168368806 28928269

process allocation 102171945 9233300 29316107

library cache 2.9539E+10 1234502998 87366744

session allocation 3091150764 514908537 244535123

从这个看,sleeps最多的事件是session allocation,并且,该事件的misses/gets超过了10%。

跟踪产生latch free的session,发现根本无法获得trace文件。Session会立刻断开。

分析listener.log,发现网络连接并无问题。经过分析和判断,初步认定该问题应该是由session的不断创建和退出引起,查询v$px_session发现,果然有session不断的在创建和退出。

分析后,我们认为,产生该问题,可能是由于并发查询引起。仔细检查使用的表,发现表的degree为1,并无问题,分析使用到的那个索引,该索引的degree为15. 这就是问题所在了,也是为什么查询中,无法trace session,因为session会立刻断开,到此也明白问题所在了.

因为系统不可能允许我停机修改数据库参数,调整并发查询的参数,因此,只能修改该索引的degree了,将该索引的degree修改为1以后,业务立刻正常.

其实我们也经常听人说,并发开高了不好,会有问题,到底有什么问题,估计也没有多少人真的碰到过.另外,在创建索引的时候,我们都喜欢并发创建,可是创建完成以后,喜欢忘记关闭并行.结果列,给后来人造成麻烦,不是吗?

作DBA,关键是要小心,小心再小心,其实这个问题出现,就是因为在系统作一个大的割接的时候,为了加快重建索引的速度,使用了非常大的并发,结果忘记关闭并行,就在业务繁忙的时候,导致问题出现.

DBA,小心一切操作,充分评估你的操作给后续系统带来的隐患.

 

最后非常感谢处理这个问题的时候,BITI_RAINY给予的帮助:)

 

 

from:http://www.oracledba.com.cn/blog/?p=327

bool worker_file_log_set_handle(WORKER_CTX *worker_ctx, struct tpsocket_fd *sock, struct list_head *buf) { // 1. 获取设备MAC地址 char *mac = common_find_key_from_buf(buf, "Device-Mac"); if (!mac || strlen(mac) < 12) { DBG_ERR("Invalid MAC address\n"); return false; } DBG_ERR("mac: %s\n", mac); // 2. MAC格式化处理(冒号转下划线) char processed_mac[18] = {0}; strncpy(processed_mac, mac, sizeof(processed_mac)-1); for (char *p = processed_mac; *p; p++) { if (*p == ':') *p = '_'; } DBG_ERR("processed_mac: %s\n", processed_mac); // 3. 创建目标目录 char dirpath[128]; snprintf(dirpath, sizeof(dirpath), "%s/%s", FILELOG_PATH , processed_mac); // 检创建父目录(/mnt/sd_card) if (access(FILELOG_PATH, F_OK) != 0) { if (mkdir(FILELOG_PATH,0755) != 0) { DBG_ERR("SD card mount failed: %s\n", strerror(errno)); return false; } } // 创建设备目录(/mnt/sd_card/<processed_mac>) if (access(dirpath, F_OK) != 0) { if (mkdir(dirpath, 0755) != 0) { DBG_ERR("Device dir '%s' creation failed: %s\n", dirpath, strerror(errno)); return false; } } // 4. 构建文件路径 char filepath[256]; snprintf(filepath, sizeof(filepath), "%s/log.txt", dirpath); DBG_ERR("filepath: %s\n", filepath); // 5. 获取内容长度( char *content_length_str = common_find_key_from_buf(buf, "Content-Length"); if (!content_length_str || *content_length_str == '\0') { DBG_ERR("Missing Content-Length header\n"); return false; } size_t content_len = atol(content_length_str); if (content_len <= 0) { DBG_ERR("Invalid content length: %s\n", content_length_str); return false; } DBG_ERR("Content-Length: %zu\n", content_len); // 滚动写入 FILE *fp = fopen(filepath, "ab+"); if (!fp) { /* 错误处理 */ } fseek(fp, 0, SEEK_END); long file_size = ftell(fp); // 处理超大数据特殊情况 if (content_len > MAX_FILELOG_DIR_SIZE) { DBG_ERR("Unable to free sufficient space\n"); const char *resp = "HTTP/1.1 507 Insufficient Storage\r\n\r\n"; struct tpsocket_buf *resp_buf = tpbuf_snprintf(strlen(resp)+1, resp); if (resp_buf) tpsocket_write(sock, resp_buf); return false; } // 循环删除行直到空间足够 while (file_size > 0 && file_size + content_len > MAX_FILELOG_DIR_SIZE) { rewind(fp); long line_start = 0; long line_end = 0; // 增强行定位:逐字符扫描换行符 int ch; while ((ch = fgetc(fp)) != EOF) { line_end = ftell(fp); if (ch == '\n') { // 找到完整行边界 line_start = line_end; // 检查剩余空间 if (file_size - line_start + content_len <= MAX_FILELOG_DIR_SIZE) break; } } // 截断并移位(避免大内存分配) if (line_start > 0) { FILE *temp = tmpfile(); // 创建临时文件 fseek(fp, line_start, SEEK_SET); // 流式复制保留内容 char buffer[1024]; size_t bytes; while ((bytes = fread(buffer, 1, sizeof(buffer), fp)) > 0) { fwrite(buffer, 1, bytes, temp); } // 原子替换原文件 ftruncate(fileno(fp), 0); rewind(fp); rewind(temp); while ((bytes = fread(buffer, 1, sizeof(buffer), temp)) > 0) { fwrite(buffer, 1, bytes, fp); } fclose(temp); file_size = ftell(fp); } else { ftruncate(fileno(fp), 0); file_size = 0; break; } } // fclose(fp); //session FileLogSession *session = calloc(1, sizeof(FileLogSession)); if (!session) return false; // session->fp = fopen(filepath, "ab"); session->fp = fp; if (!session->fp) { free(session); return false; } session->remaining = content_len; session->total_size = content_len; char *if_encrypt = common_find_key_from_buf(buf, "X-If-Encrypt"); DBG_ERR("if_encrypt:%s\n",if_encrypt); if (if_encrypt) { session->decrypt_flag = atoi(if_encrypt); free(if_encrypt); char *nonce = NULL; if ((nonce = common_find_key_from_buf(buf, "X-Nonce"))) { DBG_ERR("nonce:%s\n",nonce); work_tapo_log_refresh_nonce(nonce); free(nonce); } } else { session->decrypt_flag = 0; } snprintf(session->filepath,sizeof(session->filepath),"%s",filepath); sock->handler.cb = tpsocket_event_filelog_to_hub; sock->handler.priv = session; return true; }bool tpsocket_event_filelog_to_hub(struct tpsocket_handler *handler, struct list_head *buf, int event) { struct tpsocket_fd *sock = container_of(handler, struct tpsocket_fd, handler); FileLogSession *session = handler->priv; int decrypt_flag = session->decrypt_flag; struct tpsocket_buf *nbuf = NULL; if (event != TPSOCKET_EVENT_STREAM) { DBG_ERR("sock=%p, event=%s\n", sock, tpsocket_event_name(event)); } switch(event) { case TPSOCKET_EVENT_LISTEN: DBG_ERR("TPSOCKET_EVENT_LISTEN: %s:%s Listen\n", sock->addr, sock->port); break; case TPSOCKET_EVENT_SHUTDOWN: DBG_ERR("TPSOCKET_EVENT_LISTEN: %s:%s Shutdown\n", sock->addr, sock->port); break; case TPSOCKET_EVENT_ACCEPT: DBG_ERR("TPSOCKET_EVENT_LISTEN: %s:%s Accepted\n", sock->addr, sock->port); break; case TPSOCKET_EVENT_CONNECTED: DBG_ERR("TPSOCKET_EVENT_LISTEN: %s:%s connected\n", sock->addr, sock->port); break; case TPSOCKET_EVENT_REQ_HEAD: DBG_ERR("TPSOCKET_EVENT_LISTEN: %s Requested path: %s\n", sock->path, sock->parser.http.url); break; case TPSOCKET_EVENT_RSP_HEAD: break; case TPSOCKET_EVENT_SUB_HEAD: break; case TPSOCKET_EVENT_UPGRADE: break; case TPSOCKET_EVENT_MESSAGE: if (!session || !session->fp) { DBG_ERR("Invalid session during write\n"); return false; } nbuf = tpsocket_merge_buf(buf); if (!nbuf) { DBG_ERR("Failed to merge buffer\n"); break; } bool error = false; unsigned char *input_data = tpbuf_data(nbuf); int input_len = tpbuf_data_len(nbuf); DBG_ERR("input_len:%d", input_len); unsigned char *decoded_data = NULL; int decoded_len = 0; if (decrypt_flag) { // 分配足够大的缓冲区以存储解码后的数据 decoded_data = calloc(input_len, sizeof(char)); if (!decoded_data) { DBG_ERR("Memory allocation failed for decoded buffer\n"); tpbuf_free(nbuf); break; } // 调用 Base64 解码函数 decoded_len = tpsocket_base64_decode(input_data, input_len, decoded_data); if (decoded_len < 0) { DBG_ERR("Base64 decoding failed\n"); free(decoded_data); tpbuf_free(nbuf); break; } DBG_ERR("decoded_data:%s\n",decoded_data); DBG_ERR("decoded_len:%d\n",decoded_len); unsigned char *plain_data = calloc(decoded_len, sizeof(char)); if (!plain_data) { DBG_ERR("Memory allocation failed for plain buffer\n"); free(decoded_data); tpbuf_free(nbuf); break; } int plain_len = decoded_len; if(session->remaining > decoded_len){ session->remaining = decoded_len; } DBG_ERR("decode session->remaining:%d\n",session->remaining); work_tapo_log_decrypt(decoded_data, decoded_len, plain_data, &plain_len); // int unpadded_len = pkcs7_unpad(plain_data, plain_len, 16); // if (unpadded_len < 0) { // DBG_ERR("Padding error\n"); // error = true; // break; // } // plain_data[unpadded_len] = '\0'; // 如果是字符串 DBG_ERR("plain_data:%s\n",plain_data); DBG_ERR("plain_len:%d\n",plain_len); // 写入解密后的内容 if (fwrite(plain_data, 1, plain_len, session->fp) < plain_len) { DBG_ERR("File write error after decryption: %s\n", strerror(errno)); free(plain_data); free(decoded_data); tpbuf_free(nbuf); break; } session->remaining -= plain_len; free(plain_data); free(decoded_data); } else { if (fwrite(input_data, 1, input_len, session->fp) < input_len) { DBG_ERR("File write error: %s\n", strerror(errno)); } else { session->remaining -= input_len; } } DBG_ERR("session->remaining: %d\n", session->remaining); // 传输完成处理 if (session->remaining <= 0 || error) { fclose(session->fp); session->fp = NULL; char resp[256]; if (error) { snprintf(resp, sizeof(resp), "HTTP/1.1 500 Internal Error\r\n" "Connection: close\r\n" "\r\n"); } else { snprintf(resp, sizeof(resp), "HTTP/1.1 200 OK\r\n" "Content-Type: text/plain\r\n" "Content-Length: 0\r\n" "Connection: close\r\n" "\r\n"); } struct tpsocket_buf *resp_buf = tpbuf_snprintf(strlen(resp) + 1, resp); if (resp_buf) tpsocket_write(sock, resp_buf); if (error) unlink(session->filepath); free(session); handler->priv = NULL; } break; case TPSOCKET_EVENT_STREAM: return true; case TPSOCKET_EVENT_RESET: break; case TPSOCKET_EVENT_TIMEOUT: break; case TPSOCKET_EVENT_CLOSED: if (session) { // 异常关闭时清理资源 if (session->fp) fclose(session->fp); unlink(session->filepath); free(session); handler->priv = NULL; } break; case TPSOCKET_EVENT_ERROR: default: break; } tpsocket_free_buf(buf, tpsocket_event_name(event), 0); return true; } 现在客户端从connection:close改为connection:keep-alive,并持续传递数据,帮我对服务端代码进行相应的修改以适配。
11-01
bool tpsocket_event_filelog_to_hub(struct tpsocket_handler *handler, struct list_head *buf, int event) { struct tpsocket_fd *sock = container_of(handler, struct tpsocket_fd, handler); FileLogSession *session = handler->priv; if(session == NULL){ return true; } int decrypt_flag = session->decrypt_flag; struct tpsocket_buf *nbuf = NULL; if (event != TPSOCKET_EVENT_STREAM) { DBG_ERR("sock=%p, event=%s\n", sock, tpsocket_event_name(event)); } switch(event) { case TPSOCKET_EVENT_LISTEN: DBG_ERR("TPSOCKET_EVENT_LISTEN: %s:%s Listen\n", sock->addr, sock->port); break; case TPSOCKET_EVENT_SHUTDOWN: DBG_ERR("TPSOCKET_EVENT_LISTEN: %s:%s Shutdown\n", sock->addr, sock->port); break; case TPSOCKET_EVENT_ACCEPT: DBG_ERR("TPSOCKET_EVENT_LISTEN: %s:%s Accepted\n", sock->addr, sock->port); break; case TPSOCKET_EVENT_CONNECTED: DBG_ERR("TPSOCKET_EVENT_LISTEN: %s:%s connected\n", sock->addr, sock->port); break; case TPSOCKET_EVENT_REQ_HEAD: DBG_ERR("TPSOCKET_EVENT_LISTEN: %s Requested path: %s\n", sock->path, sock->parser.http.url); break; case TPSOCKET_EVENT_RSP_HEAD: break; case TPSOCKET_EVENT_SUB_HEAD: break; case TPSOCKET_EVENT_UPGRADE: break; case TPSOCKET_EVENT_MESSAGE: if (session == NULL) { return true; } nbuf = tpsocket_merge_buf(buf); if (!nbuf) { DBG_ERR("Failed to merge buffer\n"); break; } bool error = false; unsigned char *input_data = tpbuf_data(nbuf); int input_len = tpbuf_data_len(nbuf); DBG_ERR("input_len:%d", input_len); unsigned char *decoded_data = NULL; int decoded_len = 0; if (decrypt_flag) { DBG_ERR("+++++++++++++++++++++++++++++"); // 分配足够大的缓冲区以存储解码后的数据 decoded_data = calloc(input_len+16, sizeof(unsigned char)); if (!decoded_data) { DBG_ERR("Memory allocation failed for decoded buffer\n"); free(decoded_data); tpbuf_free(nbuf); break; } // 调用 Base64 解码函数 decoded_len = tpsocket_base64_decode(input_data, input_len, decoded_data); if (decoded_len < 0) { DBG_ERR("Base64 decoding failed\n"); free(decoded_data); tpbuf_free(nbuf); break; } unsigned char *plain_data = calloc(decoded_len, sizeof(char)); if (!plain_data) { DBG_ERR("Memory allocation failed for plain buffer\n"); free(decoded_data); tpbuf_free(nbuf); break; } int plain_len = decoded_len; if(session->remaining > decoded_len){ session->remaining = decoded_len; DBG_ERR("decode session->remaining:%d\n",session->remaining); } work_tapo_log_decrypt(decoded_data, decoded_len, plain_data, &plain_len); DBG_ERR("plain_len:%d\n",plain_len); // 写入解密后的内容 if (fwrite(plain_data, 1, plain_len, session->fp) < plain_len) { DBG_ERR("File write error after decryption: %s\n", strerror(errno)); free(plain_data); free(decoded_data); tpbuf_free(nbuf); break; }else { const char newline = '\n'; if (fwrite(&newline, 1, 1, session->fp) != 1) { DBG_ERR("Failed to write newline character: %s\n", strerror(errno)); } session->remaining -= plain_len; } free(plain_data); free(decoded_data); } else { if (fwrite(input_data, 1, input_len, session->fp) < input_len) { DBG_ERR("File write error: %s\n", strerror(errno)); } else { const char newline = '\n'; if (fwrite(&newline, 1, 1, session->fp) != 1) { DBG_ERR("Failed to write newline character: %s\n", strerror(errno)); } session->remaining -= input_len; } } DBG_ERR("session->remaining: %d\n", session->remaining); // 传输完成处理 if (session->remaining <= 0 || error) { fclose(session->fp); session->fp = NULL; char resp[256]; if (error) { snprintf(resp, sizeof(resp), "HTTP/1.1 500 Internal Error\r\n" "Connection: close\r\n" "\r\n"); } else { snprintf(resp, sizeof(resp), "HTTP/1.1 200 OK\r\n" "Content-Type: text/plain\r\n" "Content-Length: 0\r\n" "Connection: close\r\n" "\r\n"); } struct tpsocket_buf *resp_buf = tpbuf_snprintf(strlen(resp) + 1, resp); if (resp_buf) tpsocket_write(sock, resp_buf); if (error) unlink(session->filepath); free(session); handler->priv = NULL; } break; case TPSOCKET_EVENT_STREAM: return true; case TPSOCKET_EVENT_RESET: break; case TPSOCKET_EVENT_TIMEOUT: break; case TPSOCKET_EVENT_CLOSED: if (session != NULL) { // 异常关闭时清理资源 if (session->fp) fclose(session->fp); unlink(session->filepath); free(session); handler->priv = NULL; } break; case TPSOCKET_EVENT_ERROR: default: break; } tpsocket_free_buf(buf, tpsocket_event_name(event), 0); return true; }检查下哪会发生段错误?
最新发布
11-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值