基于python+mysql浅谈redis缓存设计与数据库关联数据处理

 

测试环境

redis-3.0.7

 

CentOS 6.5-x86_64

 

python 3.3.2

 

基于Python操作Redis

1、创建示例数据库表

CREATE TABLE tb_signin_rank(
id INT,
user_name VARCHAR(10) COMMENT '用户名',
signin_num INT COMMENT '签到次数',
signin_time DATETIME COMMENT '签到时间',
gold_coin INT COMMENT '金币' 
);
 
初始化数据
INSERT INTO tb_signin_rank 
VALUES(1, 'shouke', 0, NULL, 0),
(2, 'chuangke', 0, NULL, 0),
(3, 'ishouke', 0, NULL, 0),
(4, 'keshou', 0, NULL, 0),
(5, 'shouke', 0, NULL, 0);

2、redis缓存键值设计

key               value

表名:主键值:列名   列值

 

或者如下,通过为不同列之间建立较为紧密的关联

key                        value

表名:主键值:列值1:列名2   列值2

 

示例:把id为1的人的签到次数(假设为5)存储到redis中则可如下操作:

set('tb_signin_rank:1:signin_num', 5)

 

这样做的好处是,类似数据库一样,通过主键便可获取其它值。

 

示例:把id和用户名关联

set('tb_signin_rank:shouke:id', 1)

 

这样,通过用户名就可以查询出关联的id了:uid = r.get("tb_signin_rank:%s:id" % username)

set('tb_signin_rank:1:signin_num', 5)


set('tb_signin_rank:1:signin_num', 5)

3、redis关联数据库的数据处理

不要求强一致实时性的读请求,都由redis处理

要求强一致实时性的读请求,由数据库处理

通常包含以下两种处理模式:

模式1:

如图,先判断是否存在缓存(通常是根据key),如果存在则从缓存读取,否则从数据库读取并更新缓存。

不要求强一致实时性的读请求,都由redis处理

python缓冲区 为什么控制台可以及时查看 python 缓存数据_python

 适用场景:对数据实时性要求不高,更新比较不频繁,比如签到排行榜

模式2:

如下图,先写入redis然后,利用守护进程等方式,定时写入到数据库

模式3:

 如下图,先写入数据库,然后再更新到缓存

python缓冲区 为什么控制台可以及时查看 python 缓存数据_python_02

 适用场景:数据量较大,更新较为频繁


说明:

模式2和模式3的区别在于,前者把redis当作数据库用,通过写入redis后马上返回程序,然后定时把数据写入数据库,这也大大提高了访问速度。这种方式不足的是,这种对redis的可靠性依赖性太强


4、案例

./dbconfig.conf配置

[TESTDB]
host = 192.168.1.103
port = 3306
user = testacc
passwd = test1234
db = testdb
charset = utf8
 
#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
__author__ = 'shouke'
 
import configparser
import sys
import mysql.connector
import redis
 
if __name__ == '__main__':
    pool = redis.ConnectionPool(host='192.168.1.103', port=6379, db=0)
    r = redis.Redis(connection_pool=pool)
    # r.expire('tb_signin_rank:id:signin_num', 20)
 
    config = configparser.ConfigParser()
 
    # 从配置文件中读取数据库服务器IP、域名,端口
    config.read('./dbconfig.conf')
    host = config['TESTDB']['host']
    port = config['TESTDB']['port']
    user = config['TESTDB']['user']
    passwd = config['TESTDB']['passwd']
    db_name = config['TESTDB']['db']
    charset = config['TESTDB']['charset']
 
    try:
        dbconn = mysql.connector.connect(host=host, port=port, user=user, password=passwd, database=db_name, charset=charset)
    except Exception as e:
        print('初始化数据连接失败:%s' % e)
        sys.exit()
 
    # 执行签到
    try:
        db_cursor = dbconn.cursor()
        for id in range(1, 6):
            db_cursor.execute('UPDATE tb_signin_rank SET signin_num = signin_num + 1, signin_time = NOW(), gold_coin = gold_coin + (1 + RAND()*9) WHERE id = %s',(id,))
            db_cursor.execute('commit')
        # 更新缓存
        r.zincrby("tb_signin_rank:id:signin_num", id, 1)
    except Exception as e:
        print('执行数据库更新操作失败:%s' % e)
        db_cursor.execute('rollback')
        db_cursor.close()
        exit()
 
    # 展示用户签到次数
    for id in range(1, 6):
        result = r.zscore('tb_signin_rank:id:signin_num', id)
        if not result: # 不存在缓存,从数据库读取
            print('----从数据库读取用户签到次数----')
            try:
                db_cursor = dbconn.cursor()
                db_cursor.execute('SELECT signin_num FROM tb_signin_rank WHERE id = %s', (id,))
                result = db_cursor.fetchone()[0]
                # 更新到缓存
                r.zadd('tb_signin_rank:id:signin_num', id, result)
            except Exception as e:
                print('执行数据库查询操作失败:%s' % e)
                db_cursor.close()
        else: # 存在缓存,从缓存读取
            print('----从缓存读取用户签到次数----')
            result = int(result)
 
        print('sigin_num of user[id=%s]: %s' % (id, result))
 
 
    # 展示签到排行榜
    result = r.zrevrange('tb_signin_rank:id:signin_num', 0, 10)
    print('签到排行榜:', result)

[TESTDB]