游戏服务器使用mongodb 新建数据库作为数据库,还有必要使用Redis缓存吗

下次自动登录
Geeker的编程技术博客
现在的位置:
redis作为mysql的缓存服务器(读写分离)
一、redis简介
Redis是一个key-value存储系统。和Memcached类似,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。在部分场合可以对关系数据库起到很好的补充作用。它提供了Java,C/C++(hiredis),C#,PHP,JavaScript,Perl,Object-C,Python,Ruby等客户端,使用很方便。
二、架构图
大致结构就是读写分离,将mysql中的数据通过触发器同步到redis中
三、安装LNMP环境(这里为了省事,就是用yum来安装)
1、修改yum源
[root@redis&~]#&vim&/etc/yum.repos.d/epel.repo&&&&#添加这个文件
name=Extra&Packages&for&Enterprise&Linux&6&-&$basearch
baseurl=http://download.fedoraproject.org/pub/epel/6/$basearch
failovermethod=priority
gpgcheck=0
name=nginx&repo
baseurl=http://nginx.org/packages/centos/6/$basearch/
gpgcheck=0
2、yum安装
&[root@redis&~]#&yum&-y&install&nginx&php&php-fpm&php-cli&php-common&php-gd&php-mbstring&php-mysql&php-pdo&php-devel&php-xmlrpc&php-xml&php-bcmath&php-dba&php-enchant&mysql&mysql-server
3、简单配置一下nginx
[root@redis&~]#&vim&/etc/nginx/nginx.conf
&&&&&&&&listen&&&&&&&80;
&&&&&&&&#定义使用访问
&&&&&&&&server_name&&;
&&&&&&&&#设定本虚拟主机的访问日志
&&&&&&&&access_log&&/logs/.access.log&&
&&&&&&&&#默认请求
&&&&&&&&location&/&{
&&&&&&&&&&root&&&/www/;&&&&&&#定义服务器的默认网站根目录位置
&&&&&&&&&&index&index.php&index.html&index.&&&#定义首页索引文件的名称
&&&&&&&&location&~&\.php$&{
&&&&&&&&root&/www/;
&&&&&&&&fastcgi_pass&127.0.0.1:9000;
&&&&&&&&fastcgi_index&index.
&&&&&&&&fastcgi_param&SCRIPT_FILENAME&/www/$fastcgi_script_
&&&&&&&&include&fastcgi_
4、启动服务
[root@redis&~]#&sed&-i&'s/apache/nginx/g'&/etc/php-fpm.d/www.conf
[root@redis&~]#&/etc/init.d/php-fpm&start
正在启动&php-fpm:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&[确定]
[root@redis&~]#&/etc/init.d/mysqld&start
正在启动&mysqld:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&[确定]
[root@redis&~]#&mkdir&/{logs,www}
[root@redis&~]#&chown&-R&nginx:nginx&/{logs,www}
[root@redis&~]#&/etc/init.d/nginx&start
正在启动&nginx:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&[确定]
[root@redis&www]#&service&iptables&stop
iptables:&Flushing&firewall&rules:&&&&&&&&&&&&&&&&&&&&&&&&&[&&OK&&]
iptables:&Setting&chains&to&policy&ACCEPT:&filter&&&&&&&&&&[&&OK&&]
iptables:&Unloading&modules:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&[&&OK&&]
[root@redis&redis]#&netstat&-tnlp&&&&&&#查看监听
Active&Internet&connections&(only&servers)
Proto&Recv-Q&Send-Q&Local&Address&&&&&&&&&&&&&&&Foreign&Address&&&&&&&&&&&&&State&&&&&&&PID/Program&name&&&
tcp&&&&&&&&0&&&&&&0&0.0.0.0:80&&&&&&&&&&&&&&&&&&0.0.0.0:*&&&&&&&&&&&&&&&&&&&LISTEN&&&&&&2101/nginx&&&&&&&&&&&&&&&&&&&
tcp&&&&&&&&0&&&&&&0&127.0.0.1:9000&&&&&&&&&&&&&&0.0.0.0:*&&&&&&&&&&&&&&&&&&&LISTEN&&&&&&7544/php-fpm&&&&&&&&
tcp&&&&&&&&0&&&&&&0&0.0.0.0:3306&&&&&&&&&&&&&&&&0.0.0.0:*&&&&&&&&&&&&&&&&&&&LISTEN&&&&&&1871/mysqld
5、给mysql授权
[root@redis&~]#&mysql
mysql&&grant&all&privileges&on&*.*&to&root@localhost&identified&by&'123456';
mysql&&flush&
[root@redis&~]#&vim&/www/index.php&
&&&&&phpinfo();
然后访问页面看到php的相关信息,基础环境就算搭建完成了。
四、安装redis
1、安装redis
[root@redis&~]#&wget&-c&-t&0&http://download.redis.io/releases/redis-2.8.19.tar.gz
[root@redis&~]#&mkdir&/usr/local/redis
[root@redis&~]#&tar&xvf&redis-2.8.19.tar.gz&
#安装很简单、直接make就可以了
[root@redis&~]#&cd&redis-2.8.19
[root@redis&redis-2.8.19]#&make&
#编译完成后,将src中的可执行文件拷贝到刚刚创建的目录中
[root@redis&src]#&cp&redis-benchmark&redis-check-aof&redis-check-dump&redis-cli&redis-sentinel&redis-server&/usr/local/redis/
[root@redis&redis-2.8.19]#&cp&redis.conf&sentinel.conf&/usr/local/redis/
Redis-benchmark & & &压力测试工具
Redis-check-aof & & &检查redis持久化命令文件的完整性
Redis-check-dump & & 检查redis持久化数据文件的完整性
Redis-cli & & & & & &redis在linux上的客户端
Redis-sentinel & & & redis-sentinel是集群管理工具,主要负责主从切换。
Redis-server & & & & Redis服务器的daemon启动程序
2、安装php的redis扩展
[root@redis&~]#&wget&-c&-t&0&/owlient/phpredis/archive/master.zip
[root@redis&~]#&unzip&master.zip
[root@redis&~]#&cd&phpredis-master/
[root@redis&phpredis-master]#&phpize&
[root@redis&phpredis-master]#&./configure&--with-php-config=/usr/bin/php-config
[root@redis&phpredis-master]#&make&&&&make&install&
#修改php的配置文件,如果没有“extension=redis.so”,就加上这一行
[root@redis&~]#&vim&/etc/php.ini&
extension=redis.so
[root@redis&~]#&/etc/init.d/php-fpm&restart
停止&php-fpm:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&[确定]
正在启动&php-fpm:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&[确定]
3、是否安装成功
还是访问phpinfo的那个界面
看到这个就是安装完成了。
五、读写分离
这里只是简单的做了一下读,没有写操作的相关代码,过一会测试,直接到数据库里执行update来模拟写操作。
1、在mysql中插入一些测试数据
[root@redis&~]#&mysql&-u&root&-p123456
mysql&&create&database&
mysql&&CREATE&TABLE&`test`&(`id`&int(7)&NOT&NULL&AUTO_INCREMENT,&`name`&char(8)&DEFAULT&NULL,&PRIMARY&KEY&(`id`))&ENGINE=InnoDB&AUTO_INCREMENT=10&DEFAULT&CHARSET=utf8;
mysql&&INSERT&INTO&`test`&VALUES&(1,'sven'),(2,'jim'),(3,'zhu'),(4,'wang'),(5,'ftd'),(6,'test'),(7,'test01'),(8,'test02'),(9,'test03');
mysql&&select&*&from&mytest.
+----+--------+
|&id&|&name&&&|
+----+--------+
|&&1&|&sven&&&|
|&&2&|&jim&&&&|
|&&3&|&zhu&&&&|
|&&4&|&wang&&&|
|&&5&|&ftd&&&&|
|&&6&|&test&&&|
|&&7&|&test01&|
|&&8&|&test02&|
|&&9&|&test03&|
+----+--------+
2、编写php的测试代码
&&&&&&&&$redis&=&new&Redis();
&&&&&&&&$redis-&connect('127.0.0.1',6379)&or&die&("could&net&connect&redis&server");
&&&&&&&&$query&=&"select&*&from&test&limit&8";
&&&&&&&&//为了简单一点,这里就读取了8条数据
&&&&&&&&for&($key&=&1;&$key&&&9;&$key++)
&&&&&&&&&&&&&&&&if&(!$redis-&get($key))
&&&&&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&&&&&&&&&$connect&=&mysql_connect('127.0.0.1','root','123456');
&&&&&&&&&&&&&&&&&&&&&&&&mysql_select_db(mytest);
&&&&&&&&&&&&&&&&&&&&&&&&$result&=&mysql_query($query);
&&&&&&&&&&&&&&&&&&&&&&&&//如果没有找到$key,就将该查询sql的结果缓存到redis
&&&&&&&&&&&&&&&&&&&&&&&&while&($row&=&mysql_fetch_assoc($result))
&&&&&&&&&&&&&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&$redis-&set($row['id'],$row['name']);
&&&&&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&&&&&&&&&$myserver&=&'mysql';
&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&else
&&&&&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&&&&&&&&&$myserver&=&"redis";
&&&&&&&&&&&&&&&&&&&&&&&&$data[$key]&=&$redis-&get($key);
&&&&&&&&&&&&&&&&}
&&&&&&&&echo&$
&&&&&&&&echo&"&br&";
&&&&&&&&for&($key&=&1;&$key&&&9;&$key++)
&&&&&&&&&&&&&&&&echo&"number&is&&b&&font&color=#FF0000&$key&/font&&/b&";
&&&&&&&&&&&&&&&&echo&"&br&";
&&&&&&&&&&&&&&&&echo&"name&is&&b&&font&color=#FF0000&$data[$key]&/font&&/b&";
&&&&&&&&&&&&&&&&echo&"&br&";
第一次访问,redis中没有对应的KEY时
再次访问,此时redis中已有相关数据
到这里,我们已经实现了redis作为mysql的缓存服务器,但是如果更新了mysql,redis中仍然会有对应的KEY,数据就不会更新,此时就会出现mysql和redis数据不一致的情况。所以接下来就要通过mysql触发器将改变的数据同步到redis中。
六、通过gearman实现同步
Gearman是一个支持分布式的任务分发框架:
& &Gearman Job Server:Gearman核心程序,需要编译安装并以守护进程形式运行在后台。
& &Gearman Client:可以理解为任务的请求者。
& &Gearman Worker:任务的真正执行者,一般需要自己编写具体逻辑并通过守护进程方式运行,Gearman Worker接收到Gearman Client传递的任务内容后,会按顺序处理。
大致流程:
下面要编写的mysql触发器,就相当于Gearman的客户端。修改表,插入表就相当于直接下发任务。然后通过lib_mysqludf_json UDF库函数将关系数据映射为JSON格式,然后在通过gearman-mysql-udf插件将任务加入到Gearman的任务队列中,最后通过redis_worker.php,也就是Gearman的worker端来完成redis数据库的更新。
2、安装启动
[root@redis&~]#&yum&-y&install&gearmand&libgearman-devel
[root@redis&~]#&/etc/init.d/gearmand&start
正在启动&gearmand:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&[确定]
[root@redis&~]#&/etc/init.d/gearmand&status
gearmand&(pid&&7702)&正在运行...
3、安装php的gearman扩展
[root@redis&~]#&wget&-c&-t&0&https://pecl.php.net/get/gearman-1.1.1.tgz
[root@redis&~]#&tar&xvf&gearman-1.1.1.tgz&
[root@redis&~]#&cd&gearman-1.1.1
[root@redis&gearman-1.1.1]#&phpize&
[root@redis&gearman-1.1.1]#&./configure&--with-php-config=/usr/bin/php-config
[root@redis&gearman-1.1.1]#&make&
[root@redis&gearman-1.1.1]#&make&install
#如果php的配置文件中没有extension&=&gearman.so,就加上此行
[root@redis&~]#&vim&/etc/php.ini&
extension&=&gearman.so
[root@redis&~]#&/etc/init.d/php-fpm&restart
停止&php-fpm:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&[确定]
正在启动&php-fpm:&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&[确定]
这样就是安装成功了
4、安装lib_mysqludf_json
lib_mysqludf_json UDF库函数将关系数据映射为JSON格式。通常,数据库中的数据映射为JSON格式,是通过程序来转换的。
[root@redis&~]#&wget&-c&-t&0&/mysqludf/lib_mysqludf_json/archive/master.zip
[root@redis&~]#&unzip&master.zip&
[root@redis&~]#&cd&lib_mysqludf_json-master/
[root@redis&lib_mysqludf_json-master]#&gcc&$(mysql_config&--cflags)&-shared&-fPIC&-o&lib_mysqludf_json.so&lib_mysqludf_json.c
lib_mysqludf_json.c:40:23:&错误:my_global.h:没有那个文件或目录
lib_mysqludf_json.c:41:20:&错误:my_sys.h:没有那个文件或目录
lib_mysqludf_json.c:43:19:&错误:mysql.h:没有那个文件或目录
lib_mysqludf_json.c:44:21:&错误:m_ctype.h:没有那个文件或目录
lib_mysqludf_json.c:45:22:&错误:m_string.h:没有那个文件或目录
#这里编译报错是因为没有安装mysql的开发包,如果是源码安装的mysql,需要在/etc/ld.so.conf.d/
#目录下新建一个文件告诉系统mysql的头文件在哪里
[root@redis&lib_mysqludf_json-master]#&yum&-y&install&mysql-devel
[root@redis&lib_mysqludf_json-master]#&gcc&$(mysql_config&--cflags)&-shared&-fPIC&-o&lib_mysqludf_json.so&lib_mysqludf_json.c
mysql&&show&global&variables&like&'plugin_dir';
+---------------+-------------------------+
|&Variable_name&|&Value&&&&&&&&&&&&&&&&&&&|
+---------------+-------------------------+
|&plugin_dir&&&&|&/usr/lib64/mysql/plugin&|
+---------------+-------------------------+
#将模块拷贝到插件目录下
[root@redis&lib_mysqludf_json-master]#&cp&lib_mysqludf_json.so&/usr/lib64/mysql/plugin/
#注册UDF函数
mysql&&CREATE&FUNCTION&json_object&RETURNS&STRING&SONAME&'lib_mysqludf_json.so';
5、安装gearman-mysql-udf
这个插件是用来管理调用 Gearman 的分布式的队列。&
[root@redis&~]#&wget&-c&-t&0&https://launchpad.net/gearman-mysql-udf/trunk/0.6/+download/gearman-mysql-udf-0.6.tar.gz
[root@redis&~]#&tar&xvf&gearman-mysql-udf-0.6.tar.gz
[root@redis&~]#&cd&gearman-mysql-udf-0.6
[root@redis&gearman-mysql-udf-0.6]#&./configure&--with-mysql=/usr/bin/mysql_config&--libdir=/usr/lib64/mysql/plugin/
[root@redis&gearman-mysql-udf-0.6]#&make&
[root@redis&gearman-mysql-udf-0.6]#&make&install
#注册UDF函数
mysql&&CREATE&FUNCTION&gman_do_background&RETURNS&STRING&SONAME&'libgearman_mysql_udf.so';
mysql&&CREATE&FUNCTION&gman_servers_set&RETURNS&STRING&SONAME&'libgearman_mysql_udf.so';
mysql&&select&*&from&mysql.
+--------------------+-----+-------------------------+----------+
|&name&&&&&&&&&&&&&&&|&ret&|&dl&&&&&&&&&&&&&&&&&&&&&&|&type&&&&&|
+--------------------+-----+-------------------------+----------+
|&json_object&&&&&&&&|&&&0&|&lib_mysqludf_json.so&&&&|&function&|
|&gman_do_background&|&&&0&|&libgearman_mysql_udf.so&|&function&|
|&gman_servers_set&&&|&&&0&|&libgearman_mysql_udf.so&|&function&|
+--------------------+-----+-------------------------+----------+
#指定gearman的服务信息
mysql&&SELECT&gman_servers_set('127.0.0.1:4730');
6、编写mysql触发器(根据实际情况编写)
DELIMITER&$$
CREATE&TRIGGER&datatoredis&AFTER&UPDATE&ON&test&FOR&EACH&ROW&BEGIN
&&&&SET&@RECV=gman_do_background('syncToRedis',&json_object(NEW.id&as&`id`,&NEW.name&as&`name`));&
DELIMITER&;
7、编写gearman的worker端
[root@redis&~]#&vim&/www/redis_worker.php
$worker&=&new&GearmanWorker();
$worker-&addServer();
$worker-&addFunction('syncToRedis',&'syncToRedis');
$redis&=&new&Redis();
$redis-&connect('127.0.0.1',&6379);
while($worker-&work());
function&syncToRedis($job)
&&&&&&&&global&$
&&&&&&&&$workString&=&$job-&workload();
&&&&&&&&$work&=&json_decode($workString);
&&&&&&&&if(!isset($work-&id)){
&&&&&&&&&&&&&&&&return&
&&&&&&&&$redis-&set($work-&id,&$work-&name);
[root@redis&www]#&nohup&php&redis_worker.php&&
"$redis-&set($work-&id, $work-&name);"这条语句就是将id作KEY和name作VALUE分开存储,需要和前面写的php测试代码的存取一致。
8、更新mysql中的数据
mysql&&set&@RECV&=&1;
mysql&&select&@RECV;
mysql&&update&test&set&name&=&'ssss'&where&id&=&1;
mysql&&select&@RECV;
从返回值可以看到,触发器是触发成功的(这里的@RECV是上面mysql TIGGER的返回值)。我们在redis中查看数据:
[root@redis&redis]#&./redis-cli&
127.0.0.1:6379&&get&1
这里的数据居然没有变化,这是我们就要排错了。
[root@redis&~]#&vim&/var/log/audit/audit.log&
type=AVC&msg=audit(.425:107):&avc:&&denied&&{&name_connect&}&for&&pid=12453&comm="mysqld"&dest=4730&scontext=unconfined_u:system_r:mysqld_t:s0&tcontext=system_u:o
bject_r:port_t:s0&tclass=tcp_socket
#看到这样一条日志,就知道是selinux阻止了同步
#现在将selinux的模式调成Permissive&
[root@redis&~]#&getenforce&
[root@redis&~]#&setenforce&0
[root@redis&~]#&getenforce&
Permissive
设置完成以后,再次执行update,进入redis进行查看
127.0.0.1:6379&&get&1
刷新一下刚刚的php界面
到这里就基本算是大功告成了,只要application将数据写到mysql中,mysql触发器检测到更新,就会通过Gearman将数据同步到redis中。然后读取的话,就直接从redis中进行读取。当然这只是个实验环境,实际上还有很多细节要调整。
参考文章:/pages/mysql-replication-to-redis-by-gearman
【上篇】【下篇】
您可能还会对这些文章感兴趣!
同分类最新文章Redis作为Cache的使用经验 - 简书
Redis作为Cache的使用经验
redis作为缓存使用,不作数据库用途,遵循以下规则:如果缓存没有数据,即加载数据到缓存,并会设置过期时间。使用最多的三种数据类型便是:
并发环境下的缓存读取和写入方式
凡是可以用String保存的,尽量将数据用json.dumps之后再放进String,如果需要Set和SortedSet就需要警惕缓存key刚好过期时候,会有一定读取错误的问题,这个无法避免。由于我们后端使用的是MongoDB数据库,读取一般都是从库,这就必须当心读取从库后的数据还是旧的。所以在写入数据和读取数据的时候注意以下几点:
写入数据是否立刻更新缓存
如果需要立刻更新缓存的话,使用findAndModify获取最新结果并设置进缓存中,但这带来了一个弊端,有些并不是热数据,这时候放进了redis会导致占用内存,如果设置过快,redis内存的使用量增长得非常快;好处需要与另一种方式对比才明显,另一种方式是写入数据后立刻删掉该缓存,等待有需要才再加载缓存,这样的好处是redis占用缓存不会过高,总是保留着热数据,弊端就是加载缓存读取从库的数据还是未同步的,这样导致脏数据,所以前者的直接更新保证了不会是脏数据。至于采取那种方式,要看使用场景,如果不需要最新的数据,最方便是删除掉该缓存就可以了,否则就需要立刻更新缓存数据,如果键数量特别多的情况下,可以设置过期时间短一些,尽快释放redis的内存占用。
读取String数据步骤(针对Python的驱动程序)
不需要判断key是否存在,直接get回来,如果不是None就json.loads后返回,如果是None,从数据库加载数据,并setex进redis。这样做的好处:避免判断key是否存在与get的间隙,key刚好过期,这就不好处理。其实也没必要判断key是否存在,get回来的值不是None就表明有数据,None就表明key不存在,其实已经包含了exists的效果。这样基本保证并发环境下的读取不会有任何问题,可能会说操作乱序导致旧的数据覆盖新的数据,如果修改非常频繁,可以把过期时间设置短点,其实下次修改就会覆盖掉错误的,并不会有太大问题。
读取Set和SortedSet数据缓存失效问题
这种大数据集合,无法单个操作就可以判断其key存在还是该key是否有值。通常需要两个操作:加载和获取需要的数据。问题就出现在加载和获取的间隙中,这个间隙缓存刚好过期,就会出现数据错误的后果,我觉得最佳实践是这样: 保证在服务运行过程中,这些数据集合不会缓存过期。像我们使用排行榜的时候用到大量了SortedSet,当时设置了1天就过期,这样导致出现排行榜数据出现不全的问题。又如Set保存着比赛名单的数据,发现key是存在,不再加载数据,准备判断某个项是否存在该set的时候,这个set就过期了,用sismember就会返回False,就无法判断这个False的意思是该数据项不存在该set里面还是该set的key已经过期了。

我要回帖

更多关于 mongodb 导出数据库 的文章

 

随机推荐