Berkeley DB 4.8.30环境新增加密数据库时偶现Error 22问题排查
问题描述
在已包含多个未加密数据库的Berkeley DB 4.8.30环境中添加首个加密数据库,升级至包含该加密数据库创建逻辑的软件版本时,偶现db->open返回Error 22(EINVAL,无效参数),并打印BDB相关日志。相关配置及数据库打开代码如下:
环境初始化代码
static STATUS sPR_create_env(const char *passwd, BOOL open_env) { STATUS ret=OK, err=OK; if( db_env_passwd == NULL ) { asprintf(&db_env_passwd, "%s", passwd); } /* Create an environment object and initialize it */ ret = db_env_create(&dbenv, 0 ); dbenv->set_msgcall(dbenv, sPR_db_print_to_syslog); dbenv->set_errcall(dbenv, sPR_db_err_print); /* * We want to specify the shared memory buffer pool cachesize */ ret = dbenv->set_cachesize(dbenv, 0, (4 * 1024 * 1024), 0); /* Set databases prefix to data files. */ (void)dbenv->set_data_dir(dbenv, data_dir); /* Set encrypt to environment for the supporting secure DB */ ret = dbenv->set_encrypt(dbenv, passwd, DB_ENCRYPT_AES); ret = dbenv->set_tx_max(dbenv, MAX_NUM_OF_TRANSACTIONS); ret = dbenv->set_lk_partitions(dbenv, 1); /* optimized for single CPU systems */ /* Set Log file auto-removal on */ ret = dbenv->log_set_config(dbenv, DB_LOG_AUTO_REMOVE, 1); /* Set the maximum # of locks/lockers/lock-objects (should be at least 5*lock-partitions) */ ret = dbenv->set_lk_max_locks(dbenv, 4000); ret = dbenv->set_lk_max_objects(dbenv, 4000); ret = dbenv->set_lk_max_lockers(dbenv, 4000); /* Reject lock request leading to deadlocks which have the least # of locks taken */ ret = dbenv->set_lk_detect(dbenv, DB_LOCK_MINWRITE); ret = dbenv->set_lg_dir(dbenv, DB_LOG_FILE_PATH); ret = dbenv->set_lg_max(dbenv, DB_LOG_FILE_SIZE); if( open_env ) { /* open environment DB */ ret = dbenv->open(dbenv, DB_home, DB_THREAD | DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_RECOVER, 0644); } exit: if( err == ERROR ) { dbenv->close(dbenv, 0); dbenv=NULL; } return err; }
加密数据库打开代码
/* set encryption */ ret = dbp->set_flags(dbp, DB_ENCRYPT); /* Open a database with DB_BTREE access method.*/ ret = dbp->open(dbp, NULL, file_name, NULL, DB_BTREE, flags, 0644);
未加密数据库打开代码
/* Open a database with DB_BTREE access method.*/ ret = dbp->open(dbp, NULL, file_name, NULL, DB_BTREE, flags, 0644);
咨询问题:
- Error 22的成因是否与加密/未加密数据库共存有关?
- 为何问题偶发?
- 环境配置或标志是否存在问题?
问题分析与解答
1. Error 22与加密/未加密数据库共存的关联
Error 22对应EINVAL(无效参数),直接成因就是加密/未加密数据库的配置冲突。Berkeley DB 4.8中,若给整个环境设置了set_encrypt(环境级加密),会默认强制所有数据库使用加密规则;但原有未加密数据库的元数据并未标记加密,此时打开这些未加密库时未明确禁用加密,就会触发参数校验失败,返回EINVAL。
2. 问题偶发的原因
偶发性源于环境初始化时机、缓存加载状态及事务恢复的不确定性:
- 若加密数据库的创建/打开操作先于未加密数据库,环境缓存尚未加载未加密库的元数据,暂时不会触发配置冲突校验;
- 当系统重启、环境缓存失效,或未加密库的元数据被主动加载到缓存时,才会触发加密配置与未加密库的冲突检查;
- 代码中使用了
DB_RECOVER标志,环境启动时会执行恢复操作,若恢复过程中读取到未加密库的元数据与当前环境加密配置冲突,就会触发错误,但恢复操作的执行逻辑和数据状态相关,并非每次启动都会触发,因此表现为偶发。
3. 环境配置与标志的问题
代码中存在多处关键配置错误:
- 环境级加密设置错误:不应给整个环境设置
set_encrypt,这会强制所有数据库使用加密,与原有未加密库冲突; - 未加密数据库未明确禁用加密:打开未加密库时,未调用
dbp->set_encrypt(dbp, NULL, 0)明确禁用加密,导致环境默认加密规则与未加密库元数据不匹配; - 忽略API返回值:
dbenv->set_data_dir的返回值被(void)忽略,若该调用失败导致数据目录错误,会间接引发数据库打开失败;同时set_encrypt等关键调用的返回值未做严格错误检查,可能掩盖加密初始化失败的问题; DB_RECOVER滥用:每次启动都添加DB_RECOVER会触发不必要的环境恢复操作,增加了触发配置冲突校验的概率,正常启动无需强制使用该标志。
修复建议
- 移除环境级加密设置:将
dbenv->set_encrypt调用删除,改为仅在打开加密数据库时单独设置:dbp->set_encrypt(dbp, passwd, DB_ENCRYPT_AES); - 明确禁用未加密库的加密:打开未加密数据库时,添加
dbp->set_encrypt(dbp, NULL, 0)调用,确保与加密库的配置隔离; - 校验所有API返回值:去掉
(void)忽略,对set_data_dir、set_encrypt等所有BDB API的返回值做错误判断,及时处理异常; - 调整
DB_RECOVER的使用:仅在环境异常(如崩溃后)启动时使用该标志,正常启动可移除,或替换为DB_RECOVER_FATAL以减少不必要的恢复操作。
内容的提问来源于stack exchange,提问作者guy1010
相关产品推荐
相关产品推荐

