优化过程经历了以下几个过程:
- 优化数据库的数据结构和索引(难度大)
- 文件缓存,通过IO流获取比每次都访问数据库效率略高,但是流量爆炸式增长时候,IO流也承受不了
- MemCache,当时最热门的技术,通过在数据库和数据库访问层之间加上一层缓存,第一次访问时查询数据库,将结果保存到缓存,后续的查询先检查缓存,若有直接拿去使用,效率显著提升。
商品信息
- 一般存放在关系型数据库:Mysql,阿里巴巴使用的Mysql都是经过内部改动的。
商品描述、评论(文字居多)
- 文档型数据库:MongoDB
图片
- 分布式文件系统 FastDFS
- 淘宝:TFS
- Google: GFS
- Hadoop: HDFS
- 阿里云: oss
商品关键字 用于搜索
- 搜索引擎:solr,elasticsearch
- 阿里:Isearch 多隆
商品热门的波段信息
- 内存数据库:Redis,Memcache
商品交易,外部支付接口
- 第三方应用
问题:怎么操作那么多的数据库呢?加一层,UDSL统一服务数据架构
一、NoSQL的四大分类
1. KV键值对
- 新浪:Redis
- 美团:Redis + Tair
- 阿里、百度:Redis + Memcache
2. 文档型数据库 BSON数据格式(主要是以二进制进行存储)
- MongoDB(掌握) 基于分布式文件存储的数据库。C++编写,用于处理大量文档。 MongoDB是RDBMS和NoSQL的中间产品。MongoDB是非关系型数据库中功能最丰富的,NoSQL中最像关系型数据库的数据库。
- ConthDB
3. 列存储数据库
- HBase(大数据必学) 现在一般是行进行存储的
- 分布式文件系统
4. 图关系数据库 用于广告推荐,社交网络 (存的不是图形,而是关系) 比如社交拓扑图
- Neo4j、InfoGrid
二、Redis入门
1. Redis (远程字典服务)
是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
2. 安装步骤
- 解压放到/opt的位置上
- 安装 yum install gcc-c++,然后进入解压好的redis目录,执行make,执行完之后,在执行make install方法
- 解压好的位置会在/usr/local/bin目录下找到,新建一个自定义文件夹AoConfig,从刚才解压好的redis目录复制一份redis.conf文件到AoConfig文件夹
- redis不是默认后台启动的,修改参数daemonize 默认为yes
- 通过制定的配置文件在bin文件夹下进行启动 redis-server AoConfig/redis.conf 启动redis服务
- redis-cli -p 6379连接指定的端口号测试,默认端口号6379
- shutdown关闭服务,exit退出 ps -ef|grep redis查询对应的进程服务是否开启
3. redis-benchmark 压力测试工具
# 测试:100个并发连接 100000请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
-h 指定服务器的主机名字 -p服务器的端口 -c指定并发连接的数 -n指定请求数
压力测试部分结果
====== SET ======
100000 requests completed in 2.09 seconds /对十万个请求进行测试
100 parallel clients /100个并发的客户端
3 bytes payload /每次写入三个字节
keep alive: 1 /只有一台服务器来处理这些请求,单机性能
24.27% <= 1 milliseconds
83.74% <= 2 milliseconds
96.23% <= 3 milliseconds
98.57% <= 4 milliseconds
99.50% <= 5 milliseconds
99.87% <= 6 milliseconds
99.90% <= 7 milliseconds
99.93% <= 8 milliseconds
100.00% <= 8 milliseconds //所有请求在8毫秒内完成
47846.89 requests per second //每秒处理47846次请求
4. redis的基本知识
常用的数据库的命令
//配置文件中可以看到默认的是16个数据库
`select 3 选择数据库3的数据库
dasize是数据库的大小
keys * 查看数据库当前的key`
`flushdb`:清空当前数据库中的键值对。
`flushall`:清空所有数据库的键值对。`
常用的命令
- `exists key`:判断键是否存在
- `del key`:删除键值对
- `move key db`:将键值对移动到指定数据库
- `expire(到期) key second`:设置键值对的过期时间 ttl查询过期的时间
- `type key`:查看value的数据类型
关于重命名`RENAME`和`RENAMENX`
- `rename key newkey`修改 key 的名称
- `renamenx key newkey`仅当 newkey 不存在时,将 key 改名为 newkey 。
Redis的单线程为什么那么快》因为是放在内存中的,使用单线程不会有上下文的切换
5、五大数据类型(Key就代表着数据的类型)
Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持字符串、哈希表、列表、集合、有序集合,位图,hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。
1. String类型
- 获取范围内全部的字符串: getRange key1 0 -1 获取整个字符串\
- getRange key1 0 3 截取一个字符串【0, 3】 同样的setRange也是一样的
- setex 设置过期的时间 setex key 30 "hello" //设置key的时候同时设置过期的时间 Expire key 30 设置一个存在的key的过期时间 (主要来设置一个key的过期时间)
- setnx 不存在则设置(在分布式锁的时候常常使用,只有一个客户端可以获得锁),存在则设置失败
- mset key1 value1 kye2 value2 同时进行设置多个值
- msetnx k1 v1 k2 v2 批量性添加,是一个原子性的操作,要么一起成功,要么一起失败
- getset 先get再set, 类似于比较并交换CAS
String类似的使用场景:value除了是字符串还可以是数字,用途举例:
- 计数器 (使用的是incr命令)
- 统计多单位的数量:uid(用户的id):123666: follow: 0(关注的数量)
- 粉丝数
- 对象存储缓存 (可以设置对象的过期时间 expire 或者setex)
2. List类型(所有的命令都是以l开头的)
//添加值 //对应的删除为 Lpop
Lpush list one(key)
Lpush list two
//获取值 range 代表着范围
Lrange list 0 -1
//添加值,相反的方向 对应的删除为 Rpop
Rpush list one
//获取某一个值 通过索引去获得
Lindex list 1 //获得索引为1的链表中的值
//返回链表的长度
Llen list
//移除指定的值
lrem key count value 移除指定的几个值
lrem key1 1 three 移除指定的一个three
//截取一个链表 ltrim 截取指定的元素
ltrim Mylist start stop
//给对应的值进行替换
lset key index value
//还能在某个值的前面或者是后面获取值
linsert
key就相当于一个链表,可以实现消息队列,从左边进去,右边拿出来
3. set类型(所有的命令都是以s开头的)
Redis的Set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)
基本的命令
//增
sadd myset m1 m2 m3 m4
//查
scard myset //获取数目
smembers myset //获取具体的成员
sismbers myset m1 //是否包含m1
//删
srem myset m1
//随机抽取一个
srandmember myset
spop myset //随机抽取一个并删除
//移动
smove myset myset1 m3
对集合的操作(并集操作,差集)共同关注的好友
//sdiff(差集)
sdiff myset myset1 //myset-myset1返回的是最终的结果
//sinter交集
sinter myset myset1
//sunion并集 (但并不会改变原本的值)
sunion myset myset1
共同的关注,共同的爱好,推荐好友
4. hash类型(所有的命令都是以h开头的)
//增
hset student age 18 name aoran tel 1915611
//查 hget
hget student age
hmget student age name
hgetall student //key和value一起获得
//判断存在
hexists student name
//查key
hkeys student //查找所有的key
hlen student //字段的数量
hvalues student //查找所有的值
//删
hdel student name age
//+1
hincrby student age (必须是整型的字段)
hincrbyfloat student weught 0.6 (浮点型的数字)
//可以设置对个student
hset student:1 name aoran age 25
//使用字符串进行存储
mset user:1:name aoran user1:age 18
缺点:需要冒号。字符串太复杂
Hash变更的数据user name age,尤其是用户信息之类的,经常变动的信息!Hash更适合于对象的存储,String更加适合字符串存储!
5. zset类型(所有的命令都是以z开头的)
和set不同的是每个元素都会关联一个double类型的分数(score)。redis正是通过分数来为集合中的成员进行从小到大的排序。
score相同:按字典顺序排序
有序集合的成员是唯一的,但分数(score)却可以重复。
所有的命令都有Z
增
zadd myzset 1 m1 2 m2 3 m3 //前面对应的为分数
删
zrem myzset
改
查
zcard myzset //查询的是个数
zcount myzset 0 1 //查寻的是分数在0-1的数量
zscore myzset m1 //获取指定元素的分数
`zincrby myzset 5 m2 // 将指定m2的分数加5`
zrank myzset m1 //获取指定元素的排名 zrank myzset 0 -1获取所有的元素 0 -1表示索引的范围 zrevrank从大到小
zrangebyscore myzset -inf +inf 获取所有的,从小到大排序
zrangebyscore myzset -inf +inf withscores 获取所有的,从小到大排序,并附带着成绩
zrangebyscore myzset -inf 2500 withscores //从小到大
zrevrange myzset
zset案列:排行榜,把所有的播放量统计到里面,一天进行更新
重要的消息:带权重进行判断
取topN
6、三大特殊数据类型
1.(geospatial 地理空间) GEO (key 经度 纬度 value)
//添加对应城市的经度和纬度(一般是java导入)
geoadd key 经度 纬度 value
geoadd china:city 39.90 116.40 beijing
//获得当前的定位
geopos china:city beijing
//两个人的距离
geodist china:city beijing shnaghai 两个人的距离
//附近的人(获得所有附近人的地址)通过半径来查询
georadius china:city 经度 纬度 100km
georadius china:city 经度 纬度 100km withdist(距离) withcoord(经度和纬度) count 3(限制查询的附近人的数量)
//以城市为中心进行查询 查找附近的城市
georadiusbymember china:city beijing 1000km查询北京100km以内的城市
GEO的底层的实现原理是ZSET,可以使用ZSET的命令来操作geo
2、Hyperloglog 基数统计 (使用位进行计算) 数据集中不重复的元素的个数为基数
案例:网页的UV(一个人访问一个用户多次,但还是作为一个人)
传统的方式,set保存用户的id,然后可以统计set中的元素数量作为标准判断!\但是我们的目的仅仅是为了计数,而不是保存用户id
//命令:
PFadd mykey a b c d e f
PFcount mykey //计算value的数量
pfmerge mykey mykey1 mukey2 //合并mykey1 和mykey2
缺点:有一定的误差,若可以容错市容
3、bitmaps 位存储
一般存储的是只有两个状态的,比如今天的打卡或者没打卡 布隆过滤器的实现
// 可以统计总共打卡了多少天 (底层还是一个map集合)
setbit sign 0 1 # 设置sign的第0位为 1
setbit sign 2 1 # 设置sign的第2位为 1 不设置默认 是0
setbit sign 3 1
setbit sign 5 1
0 2 3 5则代表着周一。周三,周四和周六
//查询周四是否打卡
getsit sign 3
//统计操作,看是否全勤
bitcount sign
实际的案例:查看是否活跃不活跃,之前的采用的是布尔类型类似于数组进行存储