新特性解读 | MySQL 8.0 新密码策略
转载于:https://mp.weixin.qq.com/s/GK56N5SaafCIKuP6N3lMUg
引言
这里来介绍下 MySQL 8.0 版本自带的新密码验证策略。
正文
我们非常熟悉这样的模式:用户想更改自己密码,需要提供原来密码或者追加手机验证码才可以, 这种模式在 MySQL 数据库里一直不存在。在 MySQL 8.0 之前的版本,普通用户可以直接更改自己密码,不需要旧密码验证,也不需要知会管理员,比如用户 ytt_admin 需要更改密码,在 MySQL 5.7 下直接敲 alter user 命令即可:
root@ytt-ubuntu:~# mysql -uytt_admin -proot1234 -P5734 -h ytt-ubuntu -e "alter user ytt_admin identified by 'root'"
mysql: [Warning] Using a password on the command line interface can be insecure.
这样的密码更改行为其实不是很安全,假设有下面的场景出现:
用户 ytt_admin 登录到 MySQL 服务后,做了些日常操作,完成后忘记退出;此时刚好有一个别有用心的用户 ytt_fake 进入 ytt_admin 的登录环境,直接敲命令 alter user 即可更改用户 ytt_admin 的密码,并且退出当前登录环境,用户 ytt_admin 本尊再次登录 MySQL ,就会提示密码错误,不允许登录,此时用户 ytt_admin 大脑肯定是懵的。
为了防止这类不安全事件的发生,MySQL 8.0 发布了一系列密码验证策略。这里介绍第一项:当前密码验证策略设置!
当前密码验证策略有两种方法来给到具体用户。
第一种,从管理员侧来设置单个用户的当前密码验证策略。
创建用户或者更改用户设置时使用子句:password require current(表示强制此用户满足当前密码验证策略) 。
mysql:(none)>create user ytt_admin identified by 'root123' password require current;
Query OK, 0 rows affected (0.11 sec)
之后以用户 ytt_admin 登录 MySQL 并且更改密码,提示需要提供旧密码才行。
root@ytt-ubuntu:/home/ytt# mysql -h ytt-ubuntu -uytt_admin -proot123
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 33
Server version: 8.0.27 MySQL Community Server - GPL
mysql:(none)>alter user ytt_admin identified by 'root';
ERROR 3892 (HY000): Current password needs to be specified in the REPLACE clause in order to change it.
接下来,alter user 跟上子句 replace 来让用户 ytt_admin 输入旧密码,成功更改新密码。
mysql:(none)>alter user ytt_admin identified by 'root' replace 'root123';
Query OK, 0 rows affected (0.00 sec)
如果有的场景下需要保持 MySQL 旧版本的密码更改行为,管理员侧可以用子句:password require current optional 关闭新特性。
-- (optional 关键词可用default 替代,参考全局密码验证参数设置)
mysql:(none)>alter user ytt_admin password require current optional;
Query OK, 0 rows affected (0.04 sec)
来再次验证下用户 ytt_admin 更改密码的行为:保持 MySQL 8.0 之前的的规则,不对该用户密码做校验。
mysql:(none)>alter user ytt_admin identified by 'root';
Query OK, 0 rows affected (0.01 sec)
第二种,设置全局参数,来强制所有用户使用当前密码验证策略。
MySQL 8.0 新版本内置的参数 password_require_current 定义一个全局密码策略,默认关闭。开启这个选项时,要求用户更改密码时必须提供旧密码。
开启全局参数:
mysql:(none)>set persist password_require_current=on;
Query OK, 0 rows affected (0.00 sec)
创建另外一个新用户 ytt_usage :
mysql:(none)>create user ytt_usage identified by 'root123';
Query OK, 0 rows affected (0.00 sec)
以用户 ytt_usage 登录 MySQL 更改自己密码:直接拒绝更改,需要提供旧密码。
root@ytt-ubuntu:~# mysql -uytt_usage -proot123 -h ytt-ubuntu
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 37
Server version: 8.0.27 MySQL Community Server - GPL
...
mysql:(none)>alter user ytt_usage identified by 'root';
ERROR 3892 (HY000): Current password needs to be specified in the REPLACE clause in order to change it.
mysql:(none)>
replace 子句提供旧密码再次成功更改新密码:
mysql:(none)>alter user ytt_usage identified by 'root' replace 'root123';
Query OK, 0 rows affected (0.02 sec)
这里有一个需要注意的点:虽然全局参数开启,但是 alter user 命令优先级更高,可以直接覆盖全局参数设置。下面是全局参数开启的环境下,用 alter user 命令来关闭用户 ytt_usage 的当前密码验证策略。
mysql:(none)>alter user ytt_usage password require current optional;
Query OK, 0 rows affected (0.11 sec)
接下来用户 ytt_usage 又恢复为 MySQL 旧版本的安全行为:
mysql:(none)>alter user ytt_usage identified by 'rootnew';
Query OK, 0 rows affected (0.11 sec)
还有另外一个子句:password require current default ,具体行为由全局参数password_require_current 的设置决定,全局参数关闭,这个子句恢复 MySQL 旧版本安全行为;全局参数开启,这个子句使用 MySQL 新版本安全行为。
mysql:(none)>alter user ytt_usage password require current default;
Query OK, 0 rows affected (0.09 sec)
总结:
本文介绍的当前密码验证策略,使得 MySQL 朝着更加安全的方向努力
设有这样的需求: 管理员创建了两个用户,职责分别是开发和运维,并且要求这两个用户必须满足如下需求,
-
开发用户要求定期更改密码,并且密码不能与近期更改过的密码重叠,也即不能复用历史密码,这里限定历史密码个数为3;
-
运维用户同样要求定期更改密码,并且密码不能与某段时间内更改过的密码重叠,也即同样不能复用历史密码,这里限定时间段为一个礼拜。
以上两种改密码需求,在数据库侧暂时无法实现,只能拿个“小本子记住历史密码保留个数、历史密码保留天数”,在用户每次更改密码前,先检测小本子上有没有和新密码重叠的历史密码。MySQL 8.0 对以上这两种改密码需求,直接从数据库端实现,用户可以扔掉“小本子”了。我来分两部分讲解在 MySQL 8.0 版本里对以上改密码需求的具体实现。
第一,在配置文件里写上全局参数
参数 password_history 表示最近使用的密码保留次数;参数 password_reuse_interval 表示最近使用的密码保留天数。
先来实现开发用户的需求:保留历史密码个数为3。
管理员用户登录,设置全局参数:
mysql:(none)>set persist password_history=3;
Query OK, 0 rows affected (0.00 sec)
退出重连,创建用户 ytt_dev :
root@ytt-ubuntu:/home/ytt# mysql -S /opt/mysql/mysqld.sock
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 33
Server version: 8.0.27 MySQL Community Server - GPL
...
mysql:(none)>create user ytt_dev identified by 'root123';
Query OK, 0 rows affected (0.15 sec)
退出连接,用户 ytt_dev 重新连接数据库,并且更改两次密码:
root@ytt-ubuntu:/home/ytt# mysql -uytt_dev -hytt-ubuntu -proot123
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 34
Server version: 8.0.27 MySQL Community Server - GPL
...
mysql:(none)>
mysql:(none)>alter user ytt_dev identified by 'root456';
Query OK, 0 rows affected (0.03 sec)
mysql:(none)>alter user ytt_dev identified by 'root789';
Query OK, 0 rows affected (0.17 sec)
加上原始密码,也就是3次密码,再来更改一次密码,此时不允许更改密码,错误提示和密码历史策略冲突:
mysql:(none)>alter user ytt_dev identified by 'root123';
ERROR 3638 (HY000): Cannot use these credentials for 'ytt_dev@%' because they contradict the password history policy
接下来,选择一个与历史密码不冲突的新密码进行修改,此时密码修改成功:
mysql:(none)>alter user ytt_dev identified by 'rootnew';
Query OK, 0 rows affected (0.04 sec)
再来实现运维用户的需求:保留密码天数为7天。
同样,管理员用户登录 MySQL ,并且设置全局参数:
mysql:(none)>set persist password_reuse_interval = 7;
Query OK, 0 rows affected (0.00 sec)
mysql:(none)>set persist password_history=default;
Query OK, 0 rows affected (0.00 sec)
退出重新连接,创建运维用户 ytt_dba :
mysql:(none)>create user ytt_dba identified by 'root123';
Query OK, 0 rows affected (0.01 sec)
mysql:(none)>\q
Bye
以用户 ytt_dba 登录数据库,并且改了五次密码:
root@ytt-ubuntu:/home/ytt# mysql -uytt_dba -hytt-ubuntu -proot123
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 39
Server version: 8.0.27 MySQL Community Server - GPL
...
mysql:(none)>alter user ytt_dba identified by 'root456';
Query OK, 0 rows affected (0.15 sec)
mysql:(none)>alter user ytt_dba identified by 'root789';
Query OK, 0 rows affected (0.08 sec)
mysql:(none)>alter user ytt_dba identified by 'root000';
Query OK, 0 rows affected (0.02 sec)
mysql:(none)>alter user ytt_dba identified by 'root888';
Query OK, 0 rows affected (0.02 sec)
mysql:(none)>alter user ytt_dba identified by 'root999';
Query OK, 0 rows affected (0.12 sec)
接下来验证历史密码验证策略,由于我们设置了密码历史保留天数,任何在设定时间内的历史密码,均不能作为新密码使用:MySQL 拒绝用户更改密码,错误提示与密码历史策略冲突:
mysql:(none)>alter user ytt_dba identified by 'root123';
ERROR 3638 (HY000): Cannot use these credentials for 'ytt_dba@%' because they contradict the password history policy
mysql:(none)>alter user ytt_dba identified by 'root456';
ERROR 3638 (HY000): Cannot use these credentials for 'ytt_dba@%' because they contradict the password history policy
mysql:(none)>
选择一个非最近更改过的新密码,改密成功:
mysql:(none)>alter user ytt_dba identified by 'rootnew';
Query OK, 0 rows affected (0.10 sec)
如果有一个用户同时需要具备开发用户和运维用户的密码限制条件,可以把两个全局参数一起修改即可:历史密码保留天数为7天、同时历史密码保留个数为3次。
mysql:(none)>set persist password_reuse_interval = 7;
Query OK, 0 rows affected (0.00 sec)
mysql:(none)>set persist password_history=3;
Query OK, 0 rows affected (0.00 sec)
第二, 管理员在创建用户或者更改用户属性时可以对单个用户定义密码验证策略
把全局参数重置为默认,也即关闭密码验证策略:
mysql:(none)>set persist password_reuse_interval = default;
Query OK, 0 rows affected (0.00 sec)
mysql:(none)>set persist password_history=default;
Query OK, 0 rows affected (0.00 sec)
管理员退出连接重新进入,创建两个用户 ytt_dev1 和 ytt_dba1 :
mysql:(none)>create user ytt_dev1 identified by 'root123';
Query OK, 0 rows affected (0.04 sec)
mysql:(none)>create user ytt_dba1 identified by 'root123';
Query OK, 0 rows affected (0.02 sec)
更改两个用户的密码历史保留策略:
mysql:(none)>alter user ytt_dev1 password history 3;
Query OK, 0 rows affected (0.01 sec)
mysql:(none)>alter user ytt_dba1 password reuse interval 7 day;
Query OK, 0 rows affected (0.02 sec)
检索 mysql.user 表,看看是否更改成功:
mysql:(none)>select user,password_reuse_history,password_reuse_time from mysql.user where password_reuse_history is not null or password_reuse_time is not null;
+----------+------------------------+---------------------+
| user | password_reuse_history | password_reuse_time |
+----------+------------------------+---------------------+
| ytt_dba1 | NULL | 7 |
| ytt_dev1 | 3 | NULL |
+----------+------------------------+---------------------+
2 rows in set (0.00 sec)
具体验证方法类似全局参数设置部分,此处省略。
总结:
MySQL 8.0 推出的历史密码验证策略是对用户密码安全机制的另外一个全新的改进,可以省去此类需求非数据库侧的繁琐实现。
今天我们来继续介绍 MySQL 8.0 的新密码策略, 分别为双密码策略和内置随机密码生成。
第一,双密码策略:
首先来解释下什么是双密码策略?双密码策略就是在日常运维中,需要定期更改指定用户密码,同时又需要旧密码暂时保留一定时长的一种策略。其作用是延迟应用与数据库之间的用户新旧密码对接时间,进而平滑应用的操作感知。可以在如下场景中使用:
在 MySQL 数据库里我们部署最多也是最成熟的架构:一主多从。比如说此架构做了读写分离,主负责处理前端的写流量,读负责处理前端的读流量,为了安全起见,需要定期对应用连接数据库的用户更改密码。有了双密码机制,对用户密码的更改在应用端可以有一定的缓冲延迟,避免业务中断风险以及开发人员的抱怨。应用端依然可以使用旧密码来完成对数据库的检索,等待合适时机再使用管理员发来的新密码检索数据库。双密码机制包含主密码与备密码,当备密码不再使用时,告知管理员丢弃备密码,此时用户的主密码即是唯一密码。具体如何使用呢?用法如下:
管理员先创建一个新用户 ytt ,密码是 root_old ,完了更改他的密码为 root_new 。此时 root_new 即为主密码,而 root_old 即为备密码。
mysql:(none)>create user ytt identified by 'root_old';
Query OK, 0 rows affected, 2 warnings (0.24 sec)
mysql:(none)>alter user ytt identified by 'root_new' retain current password;
Query OK, 0 rows affected (0.17 sec)
接下来用户 ytt 分别使用备密码与主密码连接 MySQL 并且执行一条简单的 SQL 语句:
备密码连接数据库:
root@ytt-ubuntu:/home/ytt# mysql -h ytt-ubuntu -P 3306 -uytt -proot_old -e "select 'hello world'"
mysql: [Warning] Using a password on the command line interface can be insecure.
+-------------+
| hello world |
+-------------+
| hello world |
+-------------+
主密码连接数据库:
root@ytt-ubuntu:/home/ytt# mysql -h ytt-ubuntu -P 3306 -uytt -proot_new -e "select 'hello world'"
mysql: [Warning] Using a password on the command line interface can be insecure.
+-------------+
| hello world |
+-------------+
| hello world |
+-------------+
可以发现在管理员没有丢弃旧密码前,两个密码都能正常使用。
相关业务更改完成后,即可告知管理员丢弃备密码:
root@ytt-ubuntu:/home/ytt# mysql -S /opt/mysql/mysqld.sock
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 27
Server version: 8.0.27 MySQL Community Server - GPL
...
mysql:(none)>alter user ytt discard old password;
Query OK, 0 rows affected (0.02 sec)
mysql:(none)>\q
Bye
双密码策略有以下需要注意的事项:
-
如果用户本身已经有双密码策略,再次更改新密码时没有带 retain current password 子句,那之前的主密码被替换成新改的密码,但是备密码不会被替换。比如更改新密码为 root_new_new ,此时备密码依然是 root_old ,并非之前的主密码 root_new 。下面例子中输入密码 root_old 依然可以连接数据库,而输入密码 root_new 则被数据库拒绝连接:
mysql:(none)>alter user ytt identified by 'root_new_new';
Query OK, 0 rows affected (0.16 sec)
root@ytt-ubuntu:/home/ytt# mysql -h ytt-ubuntu -u ytt -proot_old -e "select 'hello world'"
mysql: [Warning] Using a password on the command line interface can be insecure.
+-------------+
| hello world |
+-------------+
| hello world |
+-------------+
root@ytt-ubuntu:/home/ytt# mysql -h ytt-ubuntu -u ytt -proot_new -e "select 'hello world'"
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 1045 (28000): Access denied for user 'ytt'@'ytt-ubuntu' (using password: YES)
还有一点需要注意的细节,如果不带 retain current password 子句,并且更改新密码为空串,那么主备密码则会统一更改为空串。下面例子中数据库拒绝之前的备密码连接:
mysql:(none)>alter user ytt identified by '';
Query OK, 0 rows affected (0.80 sec)
root@ytt-ubuntu:/home/ytt# mysql -h ytt-ubuntu -u ytt -proot_old -e "select 'hello world'"
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 1045 (28000): Access denied for user 'ytt'@'ytt-ubuntu' (using password: YES)
root@ytt-ubuntu:/home/ytt# mysql -h ytt-ubuntu -u ytt -e "select 'hello world'"
+-------------+
| hello world |
+-------------+
| hello world |
+-------------+
- 新密码为空,不允许使用备用密码。
mysql:(none)>alter user ytt identified by '' retain current password;
ERROR 3895 (HY000): Current password can not be retained for user 'ytt'@'%' because new password is empty.
- 使用双密码策略时,不能更改用户的认证插件。
mysql:(none)>alter user ytt identified with sha256_password by 'root_new' retain current password;
ERROR 3894 (HY000): Current password can not be retained for user 'ytt'@'%' because authentication plugin is being changed.
第二,随机密码生成:
以往旧版本有生成随机密码的需求,在 MySQL 端无法直接设定,除非封装用户密码设定逻辑,并且在代码里实现随机密码生成。比如用存储过程,脚本等等。
MySQL 8.0 直接可以设置用户随机密码
mysql:(none)>create user ytt_new identified by random password;
+---------+------+----------------------+-------------+
| user | host | generated password | auth_factor |
+---------+------+----------------------+-------------+
| ytt_new | % | >h<m3[bnigz%*f/SnLfp | 1 |
+---------+------+----------------------+-------------+
1 row in set (0.02 sec)
也可以用 set password 子句来设置随机密码
mysql:(none)>set password for ytt_new to random;
+---------+------+----------------------+-------------+
| user | host | generated password | auth_factor |
+---------+------+----------------------+-------------+
| ytt_new | % | 5wzZ+0[27cd_CW/]<ua, | 1 |
+---------+------+----------------------+-------------+
1 row in set (0.04 sec)
另外,随机密码的长度由参数 generated_random_password_length 调整,默认为 20 个。
总结
双密码策略能让应用和DBA沟通起来更加协调;随机密码设置能让数据库系统更加安全。
我们时常会遇到的场景:用银行卡在 ATM 机取款、在 APP 上转账、网购付款等等环节,因密码连续输错一定的次数,银行卡即被锁定而无法使用,除非拿着有效证件去银行柜台人工解锁才可正常使用。
随着 MySQL 数据库被越来越多的金融场景使用,类似连续输错银行卡密码而导致的锁卡功能呼之欲出。MySQL 从 8.0.19 开始,就推出了类似策略:Failed-Login Tracking and Temporary Account Locking 。翻译过来就是 失败登录追踪和临时密码锁定,后面我们简称为:FLTTAL 。
和之前几个密码策略不同,FLTTAL 没有全局参数匹配,只能在创建用户或者是更改用户属性时被匹配。有两个选项:
1. FAILED_LOGIN_ATTEMPTS N
:代表密码失败重试次数。2. PASSWORD_LOCK_TIMEN
| UNBOUNDED:代表密码连续“FAILED_LOGIN_ATTEMPTS”次验证失败后被锁定的天数
FLTTAL 有以下几个需要注意的点:
1. failed_login_attempts 和 password_lock_time 必须同时不为 0 ,FLTTAL 才能生效。
2. 创建新用户不指定 failed_login_attempts 和 password_lock_time ,则默认关闭 FLTTAL 。
3. 已使用 FLTTAL 的用户,管理员对其 alter user 后不改变原有密码验证策略。
4. 一旦账户被锁定,即使输入正确密码也无法登录。
5. 还有最重要的一点:由于 FLTTAL 对密码验证正确与否的连续性,任意一次成功登录,FLTTAL 计数器重置。例如 failed_login_attempts 设置为 3 ,前两次密码连续输错,第三次输入正确的密码,FLTTAL 计数器重置。
那接下来我们来看下如何具体使用这个密码验证策略:
-
对于普通用户的使用方法:
管理员创建用户 test1@'localhost' ,并且设置 FLTTAL 策略:失败重试次数为 3 ,密码锁定时间为 3 天。
mysql:(none)>create user test1@'localhost' identified by 'test' failed_login_attempts 3 password_lock_time 3;
Query OK, 0 rows affected (0.14 sec)
密码连续输错 3 次,test1@'localhost' 账号被锁定:
root@ytt-ubuntu:/home/ytt# mysql -utest1 -p -S /opt/mysql/mysqld.sock
Enter password:
ERROR 1045 (28000): Access denied for user 'test1'@'localhost' (using password: NO)
root@ytt-ubuntu:/home/ytt# mysql -utest1 -p -S /opt/mysql/mysqld.sock
Enter password:
ERROR 1045 (28000): Access denied for user 'test1'@'localhost' (using password: NO)
root@ytt-ubuntu:/home/ytt# mysql -utest1 -p -S /opt/mysql/mysqld.sock
Enter password:
ERROR 3955 (HY000): Access denied for user 'test1'@'localhost'. Account is blocked for 3 day(s) (3 day(s) remaining) due to 3 consecutive failed logins.
管理员解锁账户方能正常使用:(或者忘记密码,让管理员解锁账号并且重置新密码。)
mysql:(none)>alter user test1@'localhost' account unlock;
Query OK, 0 rows affected (0.00 sec)
用正确密码再次登录:登录成功。
root@ytt-ubuntu:/home/ytt# mysql -utest1 -p -S /opt/mysql/mysqld.sock -e "select 'hello world\!'"
Enter password:
+--------------+
| hello world! |
+--------------+
| hello world! |
+--------------+
-
对于代理用户的使用方法:
对于代理用户来讲,FLTTAL 只影响代理用户本身,并不影响隐藏的真实用户。
代理用户介绍详见我之前的文章:MySQL 8.0 代理用户使用
之前创建的代理用户:
mysql:(none)>show grants for ytt_fake;
+-------------------------------------------------+
| Grants for ytt_fake@% |
+-------------------------------------------------+
| GRANT USAGE ON *.* TO `ytt_fake`@`%` |
| GRANT PROXY ON `ytt_real`@`%` TO `ytt_fake`@`%` |
+-------------------------------------------------+
2 rows in set (0.00 sec)
把真实用户插件改为 mysql_native_password 让其可以正常登录:
mysql:(none)>alter user ytt_real identified with mysql_native_password;
Query OK, 0 rows affected (0.10 sec)
给代理用户 ytt_fake 设定 FLTTAL 策略:失败重试次数为 2 ,密码锁定时间为 7 天。
mysql:(none)>alter user ytt_fake failed_login_attempts 2 password_lock_time 7;
Query OK, 0 rows affected (0.14 sec)
代理用户连续输错两次密码,账号被锁住:
root@ytt-ubuntu:/home/ytt# mysql -u ytt_fake -p -hytt-ubuntu
Enter password:
ERROR 1045 (28000): Access denied for user 'ytt_fake'@'ytt-ubuntu' (using password: YES)
root@ytt-ubuntu:/home/ytt# mysql -u ytt_fake -p -hytt-ubuntu
Enter password:
ERROR 3955 (HY000): Access denied for user 'ytt_fake'@'ytt-ubuntu'. Account is blocked for 7 day(s) (7 day(s) remaining) due to 2 consecutive failed logins.
使用真实用户登录,不受代理用户影响:真实用户可以正常登录。
root@ytt-ubuntu:/home/ytt# mysql -u ytt_real -p -hytt-ubuntu -e "select 'hello world\!'";
Enter password:
+--------------+
| hello world! |
+--------------+
| hello world! |
+--------------+
用户账号被锁定并且禁止登录后,除了管理员通过手动解锁重置计数器外,还可以有以下几种方法重置计数器:
- MySQLD 服务重启。
- 执行 FLUSH PRIVILEGES,对用户权限数据刷盘。
- 一次成功的账户登录。
- 锁定时间过期。例如锁定时间为 7 天,7 天内管理员没做任何处理,FLTTAL 计数器重置。
- 管理员重新更改 failed_login_attempts 或者 password_lock_time 选项,FLTTAL 计数器重置。
总结:
这里讲解了 MySQL 8.0 的失败登录追踪和临时密码锁定策略, 结合之前介绍过的其他密码验证策略一起使用,可以弥补 MySQL 数据库在这块领域的不足。