一、问题描述
目前来说,多数 MySQL 都是以 utf8 存储,注意 MySQL 的 utf8 和我们所说的传统 utf8 有点区别。MySQL 的 utf8 实际上不是真正的 UTF-8,MySQL 的 utf8 编码规定了最多只能有 3 个字节,而真正的 UTF-8 是每个字符最多四个字节。
MySQL 一直没有修复这个 bug,他们在 2010 年发布了一个叫作 utf8mb4 的字符集,绕过了这个问题。
简单概括如下:
- MySQL 的 utf8mb4 是真正的 UTF-8。
- MySQL 的 utf8 是一种“专属的编码”,它能够编码的 Unicode 字符并不多。
所以,所有在使用 utf8 的 MySQL 和 MariaDB 用户都应该改用 utf8mb4,永远都不要再使用 utf8。utf8mb4 向下兼容 utf8。
经典问题 emoji 表情,emoji 表情的 unicode 已经超过了 3 个字节,因此 MySQL 的 utf8 无法识别与存储 emoji。从 MySQL 5.5 版本后开始支持 utf8mb4,这种编码支持 1 ~ 4 个字节,这种编码可以表示 emoji。另外 utf8mb4 向下兼容 utf8。
为了验证一下以上问题,我使用 Django 简单构建了一个应用,用来存储 emoji。下面是简化了很多 Django 的很多操作,所以需要有一定基础才可以运行起来。
假设我们有一个非常简单的 Django 应用来管理评论,假设你已经创建了项目,并且创建了应用,应用并且已经加入到了settings.py
。
二、应用创建
我们的模型将是一个Comment
类,有两个字段:一个文本字段和一个作者字段。在 Django 的世界里,这个存储模型的文件是models.py
:
1 2 3 4 5 |
from django.db import models class Comment(models.Model): text = models.TextField(max_length=2048, blank=True) author = models.CharField(max_length=128, blank=True) |
然后创建一个视图,将显示评论列表,在views.py
:
1 2 3 4 5 6 7 8 |
from django.views.generic import ListView from .models import Comment class CommentsView(ListView): model = Comment context_object_name = 'comments' template_name = 'comments/comment_list.html' paginate_by = 20 |
这里我们直接继承了 Django 内置的 ListView 类,用于方便展示列表数据,可以很快实现分页展示。
关联的模板位于templates/comments/comment_list.html
:
1 2 3 4 5 6 7 8 9 10 11 |
<h1>Comments</h1> <ul> {% for comment in comments %} <li> <p>{{ comment.author }}</p> <p>{{ comment.text }}</p> </li> {% empty %} <li>No comments yet.</li> {% endfor %} </ul> |
在admin.py
创建/编辑我们的看法:
1 2 3 4 |
from django.contrib import admin from .models import Comment admin.site.register(Comment) |
然后,我们的数据库配置在settings.py
:
1 2 3 4 5 6 7 8 9 10 |
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'django', 'USER': 'root', 'PASSWORD': '123456', 'HOST': '10.10.0.109', 'PORT': '3306', } } |
最后,配置urls.py
:
1 2 3 4 5 6 7 8 |
from django.contrib import admin from django.conf.urls import url from comment import views urlpatterns = [ url(r'^$', views.CommentsView.as_view(), name='Comment-list'), url('admin/', admin.site.urls), ] |
创建一个超级用户:
1 |
$ python manage.py createsuperuser --username=admin --email=admin@admin.com |
如果您打开管理页面,并创建一个包含表情符号的新评论:
Django 将抛出一个异常:Incorrect string value: ’\xF0\x9F\x90\xAF’ for column ’text’ at row 1
三、问题解决
1. 把 MySQL 从 utf8 切换到 utf8mb4
1 |
mysql> SHOW VARIABLES WHERE Variable_name LIKE 'character\_set\_%' OR Variable_name LIKE 'collation%'; |
你应该看到类似如下信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
+--------------------------+-------------------+ | Variable_name | Value | +--------------------------+-------------------+ | character_set_client | utf8 | | character_set_connection | utf8 | | character_set_database | latin1 | | character_set_filesystem | binary | | character_set_results | utf8 | | character_set_server | latin1 | | character_set_system | utf8 | | collation_connection | utf8_general_ci | | collation_database | latin1_swedish_ci | | collation_server | latin1_swedish_ci | +--------------------------+-------------------+ |
因此,首先,我们要更改数据库,表的字符集和排序规则属性,以使用 utf8mb4,而不是 utf8。
1 2 3 4 5 |
# For each database: ALTER DATABASE database_name CHARACTER SET = utf8mb4 COLLATE utf8mb4_general_ci; # For each table: ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; |
2. 修改服务器应用程序代码以使用正确的字符集
超简单,只需添加'OPTIONS': {'charset': 'utf8mb4'}
到您的 DATABASES 配置:
1 2 3 4 5 6 7 8 9 10 11 |
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'django', 'USER': 'root', 'PASSWORD': '123456', 'HOST': '10.10.0.109', 'PORT': '3306', 'OPTIONS': {'charset': 'utf8mb4'}, } } |
这应该足够了。如果你重新启动服务器,你应该可以在任何评论中插入 emoji。
在前端访问效果如下:
3. 检查客户端和字符集
最后,这第两步是可选的,但强烈建议。在 /etc/my.cnf 配置文件中,你可以设置以下参数:
1 2 3 4 5 |
[client] default-character-set = utf8mb4 [mysqld] character-set-server = utf8mb4 |
在数据库端的测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
mysql> \s -------------- mysql Ver 8.0.18 for macos10.14 on x86_64 (MySQL Community Server - GPL) .... Server characterset: utf8mb4 Db characterset: utf8mb4 Client characterset: utf8mb4 Conn. characterset: utf8mb4 mysql> show create table t2\G *************************** 1. row *************************** Table: t2 Create Table: CREATE TABLE `t2` ( `id` varchar(30) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 1 row in set (0.01 sec) mysql> insert into t2(id) values('🙂'); Query OK, 1 row affected (0.02 sec) mysql> select * from t2; +------+ | id | +------+ | 🙂 | +------+ 1 row in set (0.01 sec) mysql> set names utf8; Query OK, 0 rows affected, 1 warning (0.01 sec) mysql> \s -------------- mysql Ver 8.0.18 for macos10.14 on x86_64 (MySQL Community Server - GPL) ... Server characterset: utf8mb4 Db characterset: utf8mb4 Client characterset: utf8 Conn. characterset: utf8 mysql> select * from t2; +------+ | id | +------+ | ? | +------+ 1 row in set (0.00 sec) mysql> set names utf8mb4; Query OK, 0 rows affected (0.00 sec) mysql> select * from t2; +------+ | id | +------+ | 🙂 | +------+ 1 row in set (0.00 sec) |
<参考>
http://blog.manbolo.com/2014/03/31/using-emojis-in-django-model-fields