一、问题描述
目前来说,多数 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


